Files
mesh-catalog/modules/keycloak/cmd/keycloak-provider/live_test.go
T
jochen 77fb1ecfb2 keycloak: port to Go and repair an admin that refuses the mesh's secret
Twice the identity provider's admin kept an older password than the one the
mesh minted (an adopted, then a moved database), and the provisioner failed
every consumer until it was repaired by hand (hq issue 179). The module now
checks the admin's login and repairs a refusal itself through the server's
bootstrap command, verifies, brakes a failed repair and announces it, and
stops asking the server while refused. Ported to Go to change it.
2026-10-06 00:13:42 +02:00

61 lines
2.3 KiB
Go

package main
// The repair against a real Keycloak (issue 179's procedure, run by the guard). Skipped unless
// MESH_KEYCLOAK_LIVE_CONTAINER names a throwaway Keycloak 26 container whose realm was created with
// another admin password than "new", and MESH_KEYCLOAK_LIVE_URL reaches it, e.g.:
//
// docker network create kc-live
// docker run -d --name kc-live-db --network kc-live -e POSTGRES_PASSWORD=pg -e POSTGRES_DB=keycloak postgres:17-alpine
// docker run -d --name kc-live --network kc-live -p 127.0.0.1:18080:8080 \
// -e KC_DB=postgres -e KC_DB_URL=jdbc:postgresql://kc-live-db/keycloak -e KC_DB_USERNAME=postgres \
// -e KC_DB_PASSWORD=pg -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=old \
// quay.io/keycloak/keycloak:26.0.8 start-dev
// MESH_KEYCLOAK_LIVE_CONTAINER=kc-live MESH_KEYCLOAK_LIVE_URL=http://127.0.0.1:18080 go test -run Live ./...
//
// A database whose admin kept an older password than the mesh's is exactly that container.
import (
"os"
"strings"
"testing"
"time"
)
func TestLiveRepair(t *testing.T) {
container, base := os.Getenv("MESH_KEYCLOAK_LIVE_CONTAINER"), os.Getenv("MESH_KEYCLOAK_LIVE_URL")
if container == "" || base == "" {
t.Skip("no MESH_KEYCLOAK_LIVE_CONTAINER / MESH_KEYCLOAK_LIVE_URL")
}
kc := NewClient(base, "admin", func() (string, error) { return "new", nil }, "master")
var said, events []string
g := &Guard{KC: kc, Container: container,
Log: func(f string, a ...any) { said = append(said, f) },
Announce: func(e string, _ map[string]any) { events = append(events, e) }}
if s, err := g.Check(ctx); s != AdminRejected {
t.Fatalf("the container's admin should refuse the mesh's password first: %s %v", s, err)
}
start := time.Now()
r := g.Ensure(ctx, true, false)
if !r.Repaired {
t.Fatalf("not repaired after %s: %+v %+v", time.Since(start), r, r.LastRepair)
}
t.Logf("repaired in %s", time.Since(start).Round(time.Second))
users, err := kc.ListUsers(ctx, "master", "", 100)
if err != nil {
t.Fatal(err)
}
for _, u := range users {
if name, _ := u["username"].(string); strings.HasPrefix(name, "mesh-repair-") {
t.Fatalf("the temporary admin %s is still there", name)
}
}
if strings.Join(events, ",") != EventRepaired {
t.Fatal(events)
}
// And a second pass changes nothing.
if r := g.Ensure(ctx, true, false); r.Repaired || r.State != AdminOK {
t.Fatalf("%+v", r)
}
}