Restore the test that both keycloak paths leave the same client, over the SDK's moved path (issue 849)
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/provider"
|
||||
"git.novox.be/novox/mesh-sdk/go/provider/harness"
|
||||
)
|
||||
|
||||
const testIssuer = "https://id.example.org/realms/Novox"
|
||||
@@ -69,3 +73,121 @@ func TestTheGrantPathLeavesWhenTheConsumersFileIsFilled(t *testing.T) {
|
||||
t.Fatal("did not leave when the file was filled")
|
||||
}
|
||||
}
|
||||
|
||||
// bucketBus is the helper's bus over in-memory buckets.
|
||||
type bucketBus struct {
|
||||
mu sync.Mutex
|
||||
buckets map[string]map[string][]byte
|
||||
}
|
||||
|
||||
func (b *bucketBus) Watch(_ context.Context, bucket string, handle func(string, []byte)) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
for k, v := range b.buckets[bucket] {
|
||||
handle(k, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bucketBus) Put(_ context.Context, bucket, key string, value []byte) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.buckets[bucket] == nil {
|
||||
b.buckets[bucket] = map[string][]byte{}
|
||||
}
|
||||
b.buckets[bucket][key] = append([]byte(nil), value...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bucketBus) Delete(_ context.Context, bucket, key string) error {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
delete(b.buckets[bucket], key)
|
||||
return nil
|
||||
}
|
||||
|
||||
// grafanaContributes and grafanaNames are what grafana's plan entry carries for keycloak: its contribution, and the
|
||||
// names the controller composed for it (issues 776, 792).
|
||||
var grafanaContributes = map[string]any{"label": "grafana", "endpoint": "web", "callback": "/login/generic_oauth"}
|
||||
var grafanaNames = map[string]string{"name": "grafana.example.org", "internal-name": "grafana.home.internal"}
|
||||
|
||||
// Against a Keycloak, both paths leave the same client: the grant file through the harness, and the consumers' file
|
||||
// through the SDK's moved path with keycloak's spec (issue 849).
|
||||
func TestBothPathsLeaveTheSameClientInKeycloak(t *testing.T) {
|
||||
grafana := provider.ConsumerIdentity("home", "grafana")
|
||||
values := map[string]any{"name": grafanaNames["name"], "internal-name": grafanaNames["internal-name"]}
|
||||
for k, v := range grafanaContributes {
|
||||
values[k] = v
|
||||
}
|
||||
now := time.Date(2026, 10, 15, 12, 0, 0, 0, time.UTC)
|
||||
made := func(through func(p provisioner)) Rep {
|
||||
f, o := oidcWorld(t)
|
||||
through(provisioner{clients: o, announce: func(string, map[string]any) {}, log: func(string, ...any) {}})
|
||||
return only(t, f, grafana)
|
||||
}
|
||||
viaGrant := made(func(p provisioner) {
|
||||
dir := t.TempDir()
|
||||
secret := filepath.Join(dir, grafana+".secret")
|
||||
if err := os.WriteFile(secret, []byte("pw-"+grafana+"\n"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
raw, _ := json.Marshal(map[string]any{"requirement": ProvisionName, "given": []map[string]any{
|
||||
{"as": grafana, "node": "home", "values": values, "secret": secret}}})
|
||||
receives := filepath.Join(dir, "mesh.json")
|
||||
if err := os.WriteFile(receives, raw, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := &harness.Harness{Resource: ProvisionName, Receives: receives, Adapter: p,
|
||||
Now: func() time.Time { return now }, Log: func(string, ...any) {}}
|
||||
h.Reconcile(ctx)
|
||||
})
|
||||
viaFile := made(func(p provisioner) {
|
||||
list, _ := json.Marshal([]provider.ConsumerCredential{{Consumer: "home", Module: "grafana",
|
||||
Credential: provider.PairName("novox", ProvisionName, "home", "grafana", ""), Value: "pw-" + grafana}})
|
||||
file := filepath.Join(t.TempDir(), "consumers.json")
|
||||
if err := os.WriteFile(file, list, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := &harness.Harness{Resource: ProvisionName, Adapter: p, Node: "novox",
|
||||
Now: func() time.Time { return now }, Log: func(string, ...any) {}}
|
||||
run, _, _, err := moved(file, h, testIssuer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bus := &bucketBus{buckets: map[string]map[string][]byte{}}
|
||||
plan, _ := json.Marshal(map[string]any{"node": "home", "module": "grafana", "provisioning": map[string]any{
|
||||
"provisions": []any{map[string]any{"provision": ProvisionName, "provider": "novox",
|
||||
"provider-module": "keycloak", "contributes": grafanaContributes, "names": grafanaNames}}}})
|
||||
_ = bus.Put(ctx, provider.AssignmentsBucket, "home.grafana", plan)
|
||||
pr := provider.New(provider.Options{Node: "novox", Module: "keycloak", Bus: bus,
|
||||
Vault: provider.ConsumersFile{Paths: []string{file}},
|
||||
Offers: map[string]provider.Offer{ProvisionName: {Credential: provider.PerConsumer,
|
||||
Serves: servedValues(testIssuer)}},
|
||||
Make: run.Make, Gone: run.Gone, Logf: func(string, ...any) {}})
|
||||
out, err := pr.Pass(ctx)
|
||||
if err != nil || len(out) != 1 || out[0].Status != provider.Answered {
|
||||
t.Fatalf("%+v %v", out, err)
|
||||
}
|
||||
})
|
||||
// Keycloak's own ids differ between two runs; nothing the mesh sets does.
|
||||
for _, c := range []Rep{viaGrant, viaFile} {
|
||||
delete(c, "id")
|
||||
raw, _ := json.Marshal(c)
|
||||
var again Rep
|
||||
_ = json.Unmarshal(raw, &again)
|
||||
for k := range c {
|
||||
delete(c, k)
|
||||
}
|
||||
for k, v := range again {
|
||||
c[k] = v
|
||||
}
|
||||
if mappers, ok := c["protocolMappers"].([]any); ok {
|
||||
for _, m := range mappers {
|
||||
delete(m.(map[string]any), "id")
|
||||
}
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(viaGrant, viaFile) {
|
||||
t.Fatalf("through the file %v, through the grant %v", viaFile, viaGrant)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user