mesh/delivery-group group feat/774-l3-reland rejected: composed together: the checker's verdict at the members' heads (b48c90fea31f): integration: mesh-self-ch…
mesh/delivery closed: a newer head of the same pull request
mesh/check failed: integration: mesh-self-check/cmd/mesh-self-check.TestTheSelfCheckWholeRunsOnTheBus: the run answered {Run:doctor-1793351737-1 At:20…
One commit with exactly the tree of the reviewed head35b4509c0a, on main12c2574c9d, so two compiled programs added and removed on the branch leave no trace in history (issue 1577).
117 lines
4.0 KiB
Go
117 lines
4.0 KiB
Go
package main
|
|
|
|
// Retirement against a real Keycloak (novox/hq ADR 0230). Skipped unless MESH_KEYCLOAK_LIVE_URL reaches
|
|
// a throwaway Keycloak whose master admin's password is MESH_KEYCLOAK_LIVE_PASSWORD, e.g.:
|
|
//
|
|
// docker run -d --rm --name kc-retire -p 127.0.0.1:18081:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
|
|
// -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:26.0.8 start-dev
|
|
// MESH_KEYCLOAK_LIVE_URL=http://127.0.0.1:18081 MESH_KEYCLOAK_LIVE_PASSWORD=admin go test -run LiveRetire ./...
|
|
//
|
|
// It proves what the fake cannot: a retired client is refused by Keycloak itself at the authorization
|
|
// endpoint, keeps its secret and redirects, is served again once enabled, and is deleted only once
|
|
// retired.
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLiveRetirement(t *testing.T) {
|
|
base, pw := os.Getenv("MESH_KEYCLOAK_LIVE_URL"), os.Getenv("MESH_KEYCLOAK_LIVE_PASSWORD")
|
|
if base == "" || pw == "" {
|
|
t.Skip("no MESH_KEYCLOAK_LIVE_URL / MESH_KEYCLOAK_LIVE_PASSWORD")
|
|
}
|
|
kc := NewClient(base, "admin", func() (string, error) { return pw, nil }, "master")
|
|
o := OidcClients{KC: kc, Realm: "master"}
|
|
p := grafana("live-secret", nil)
|
|
p.As = "mesh_live_retire"
|
|
t.Cleanup(func() {
|
|
if found, _ := kc.FindClient(ctx, "master", p.As); found != nil {
|
|
id, _ := found["id"].(string)
|
|
kc.DeleteClientByID(ctx, "master", id)
|
|
}
|
|
})
|
|
if _, err := o.Ensure(ctx, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// The authorization endpoint is where a consumer's users arrive: 200 with a login page while the
|
|
// client is enabled, refused while it is not.
|
|
authorize := func() int {
|
|
q := url.Values{"client_id": {p.As}, "response_type": {"code"}, "scope": {"openid"},
|
|
"redirect_uri": {"https://grafana.example.org/login/generic_oauth"}}
|
|
resp, err := (&http.Client{CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }}).
|
|
Get(base + "/realms/master/protocol/openid-connect/auth?" + q.Encode())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
resp.Body.Close()
|
|
return resp.StatusCode
|
|
}
|
|
if code := authorize(); code != 200 {
|
|
t.Fatalf("an enabled client is refused: %d", code)
|
|
}
|
|
mustHold(t, o, p, true)
|
|
|
|
at := time.Now().UTC().Truncate(time.Second)
|
|
if done, err := o.Retire(ctx, p.As, "the mesh stopped asking for it", at); done != "retired" || err != nil {
|
|
t.Fatal(done, err)
|
|
}
|
|
if code := authorize(); code == 200 {
|
|
t.Fatal("a retired client is still served")
|
|
}
|
|
// Its secret is removed (ADR 0570 clause 5): the mesh's no longer authenticates.
|
|
if found, err := o.KC.FindClient(ctx, o.Realm, p.As); err != nil || found == nil {
|
|
t.Fatal(found, err)
|
|
} else if id, _ := found["id"].(string); true {
|
|
if secret, err := o.KC.ClientSecretByID(ctx, o.Realm, id); err != nil || secret == p.Password {
|
|
t.Fatal("a retired client keeps the mesh's secret", err)
|
|
}
|
|
}
|
|
// Retired, so a person may delete it.
|
|
if err := o.Delete(ctx, p.As); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Made again, retired, listed, and enabled again as it was.
|
|
if _, err := o.Ensure(ctx, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
o.Retire(ctx, p.As, "again", at)
|
|
inv, err := o.Inventory(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
found := false
|
|
for _, r := range inv.Retired {
|
|
found = found || (r.Consumer == p.As && r.RetiredAt.Equal(at) && r.Why == "again")
|
|
}
|
|
if !found {
|
|
t.Fatalf("not listed retired: %+v", inv)
|
|
}
|
|
secret, err := kc.ClientSecretByID(ctx, "master", clientID(t, kc, p.As))
|
|
if err != nil || secret != "live-secret" {
|
|
t.Fatal("the secret was not kept:", secret, err)
|
|
}
|
|
if _, err := o.Ensure(ctx, p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if code := authorize(); code != 200 {
|
|
t.Fatalf("re-enabled and still refused: %d", code)
|
|
}
|
|
mustHold(t, o, p, true)
|
|
if err := o.Delete(ctx, p.As); err == nil {
|
|
t.Fatal("deleted an enabled client")
|
|
}
|
|
}
|
|
|
|
func clientID(t *testing.T, kc *Client, clientId string) string {
|
|
found, err := kc.FindClient(ctx, "master", clientId)
|
|
if err != nil || found == nil {
|
|
t.Fatal(found, err)
|
|
}
|
|
id, _ := found["id"].(string)
|
|
return id
|
|
}
|