generated from coulomb/repo-seed
193 lines
6.4 KiB
Go
193 lines
6.4 KiB
Go
package keycloak_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/netkingdom/flex-auth/internal/adapters/keycloak"
|
|
"github.com/netkingdom/flex-auth/pkg/api"
|
|
)
|
|
|
|
func TestBuildAuthorizationRequestUsesUMAPermissionShape(t *testing.T) {
|
|
adapter := newAdapter(t, &fakeClient{})
|
|
|
|
got, err := adapter.BuildAuthorizationRequest(api.CheckRequest{
|
|
ID: "check:keycloak",
|
|
Subject: api.SubjectRef{ID: "user:alice"},
|
|
Action: "read",
|
|
Resource: api.ResourceRef{ID: "document:internal-note", Type: "document", System: "markitect-tool"},
|
|
Context: map[string]any{"purpose": "support"},
|
|
CaringContext: caringDescriptor(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("BuildAuthorizationRequest: %v", err)
|
|
}
|
|
if got.Realm != "platform" || got.Audience != "markitect-tool" {
|
|
t.Fatalf("realm/audience = %s/%s", got.Realm, got.Audience)
|
|
}
|
|
if got.Permission.ResourceID != "document:internal-note" || got.Permission.Scope != "read" {
|
|
t.Fatalf("permission = %+v", got.Permission)
|
|
}
|
|
if got.ClaimToken["caring_context"] == nil {
|
|
t.Fatal("claim token missing CARING context")
|
|
}
|
|
}
|
|
|
|
func TestResourceRegistrationsFromManifest(t *testing.T) {
|
|
got := keycloak.ResourceRegistrationsFromManifest(api.ResourceManifest{
|
|
ID: "manifest:markitect",
|
|
System: "markitect-tool",
|
|
Actions: []string{"read", "export"},
|
|
Resources: []api.Resource{
|
|
{
|
|
ID: "document:internal-note",
|
|
Type: "document",
|
|
Path: "/docs/internal-note",
|
|
Labels: []string{"internal"},
|
|
TrustZone: "internal",
|
|
Owner: "team:platform",
|
|
},
|
|
},
|
|
})
|
|
if len(got) != 1 {
|
|
t.Fatalf("len = %d", len(got))
|
|
}
|
|
if got[0].ID != "document:internal-note" || got[0].Type != "document" {
|
|
t.Fatalf("registration = %+v", got[0])
|
|
}
|
|
if len(got[0].Scopes) != 2 || got[0].Attributes["trust_zone"][0] != "internal" {
|
|
t.Fatalf("registration = %+v", got[0])
|
|
}
|
|
}
|
|
|
|
func TestAdapterCheckWrapsKeycloakAllow(t *testing.T) {
|
|
client := &fakeClient{
|
|
result: keycloak.AuthorizationResult{
|
|
Allowed: true,
|
|
Reason: "uma_permission_granted",
|
|
RPTTokenID: "rpt:123",
|
|
PolicyVersion: "kc-v2",
|
|
Diagnostics: map[string]any{"policy": "document-reader"},
|
|
},
|
|
}
|
|
adapter := newAdapter(t, client)
|
|
|
|
got, err := adapter.Check(context.Background(), api.CheckRequest{
|
|
ID: "check:allow",
|
|
Subject: api.SubjectRef{ID: "user:alice"},
|
|
Action: "read",
|
|
Resource: api.ResourceRef{ID: "document:internal-note", Type: "document", System: "markitect-tool"},
|
|
CaringContext: caringDescriptor(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Check: %v", err)
|
|
}
|
|
if got.Effect != api.DecisionEffectAllow || got.Reason != "uma_permission_granted" {
|
|
t.Fatalf("decision = %s/%s", got.Effect, got.Reason)
|
|
}
|
|
if got.Provenance.Evaluator != keycloak.EvaluatorName || got.MatchedPolicyVersion != "kc-v2" {
|
|
t.Fatalf("provenance = %+v matched=%s", got.Provenance, got.MatchedPolicyVersion)
|
|
}
|
|
if got.Diagnostics["permission"] != "document:internal-note#read" || got.Diagnostics["rpt_token_id"] != "rpt:123" {
|
|
t.Fatalf("diagnostics = %+v", got.Diagnostics)
|
|
}
|
|
if got.Caring == nil || got.Caring.Descriptor == nil {
|
|
t.Fatal("missing CARING descriptor")
|
|
}
|
|
}
|
|
|
|
func TestAdapterFailsClosedOnUnavailableKeycloak(t *testing.T) {
|
|
client := &fakeClient{
|
|
err: keycloak.NewBackendError(keycloak.FailureUnavailable, "authorize", errors.New("connect refused")),
|
|
}
|
|
adapter := newAdapter(t, client)
|
|
|
|
got, err := adapter.Check(context.Background(), api.CheckRequest{
|
|
ID: "check:down",
|
|
Subject: api.SubjectRef{ID: "user:alice"},
|
|
Action: "read",
|
|
Resource: api.ResourceRef{ID: "document:internal-note", Type: "document", System: "markitect-tool"},
|
|
CaringContext: caringDescriptor(),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Check: %v", err)
|
|
}
|
|
if got.Effect != api.DecisionEffectDeny || got.Reason != "keycloak_unavailable" {
|
|
t.Fatalf("decision = %s/%s; want fail closed", got.Effect, got.Reason)
|
|
}
|
|
if got.Diagnostics["keycloak_failure"] != "unavailable" {
|
|
t.Fatalf("diagnostics = %+v", got.Diagnostics)
|
|
}
|
|
if got.Caring.ConformanceFindings[0].Code != "KEYCLOAK-UNAVAILABLE" {
|
|
t.Fatalf("finding = %+v", got.Caring.ConformanceFindings[0])
|
|
}
|
|
}
|
|
|
|
func TestRegisterResourceManifestDelegatesToClient(t *testing.T) {
|
|
client := &fakeClient{}
|
|
adapter := newAdapter(t, client)
|
|
|
|
report, err := adapter.RegisterResourceManifest(context.Background(), api.ResourceManifest{
|
|
System: "markitect-tool",
|
|
Actions: []string{"read"},
|
|
Resources: []api.Resource{
|
|
{ID: "document:internal-note", Type: "document"},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("RegisterResourceManifest: %v", err)
|
|
}
|
|
if report.ResourcesWritten != 1 || len(client.registered) != 1 {
|
|
t.Fatalf("report = %+v registered = %+v", report, client.registered)
|
|
}
|
|
}
|
|
|
|
func newAdapter(t *testing.T, client *fakeClient) *keycloak.Adapter {
|
|
t.Helper()
|
|
adapter, err := keycloak.New(client, keycloak.Options{
|
|
Realm: "platform",
|
|
Audience: "markitect-tool",
|
|
PolicyPackage: "keycloak.authz",
|
|
PolicyVersion: "v1",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("New: %v", err)
|
|
}
|
|
return adapter
|
|
}
|
|
|
|
func caringDescriptor() *api.CaringAccessDescriptor {
|
|
return &api.CaringAccessDescriptor{
|
|
ID: "descriptor:keycloak-reader",
|
|
Profile: api.CaringProfileCaring040RC2,
|
|
SubjectType: api.SubjectTypeHuman,
|
|
OrganizationRelation: api.OrganizationRelationCustomer,
|
|
CanonicalRole: api.CanonicalRoleDoer,
|
|
Scope: api.CaringScope{Level: api.ScopeLevelResource, ID: "document:internal-note"},
|
|
Planes: []api.Plane{api.PlaneData},
|
|
Capabilities: []api.Capability{api.CapabilityView},
|
|
ExposureModes: []api.ExposureMode{api.ExposureModeMasked},
|
|
Restrictions: []api.Restriction{api.RestrictionExportBlocked},
|
|
}
|
|
}
|
|
|
|
type fakeClient struct {
|
|
result keycloak.AuthorizationResult
|
|
err error
|
|
registered []keycloak.ResourceRegistration
|
|
}
|
|
|
|
func (c *fakeClient) Authorize(context.Context, keycloak.AuthorizationRequest) (keycloak.AuthorizationResult, error) {
|
|
return c.result, c.err
|
|
}
|
|
|
|
func (c *fakeClient) RegisterResources(_ context.Context, resources []keycloak.ResourceRegistration) (keycloak.ResourceImportReport, error) {
|
|
c.registered = append([]keycloak.ResourceRegistration(nil), resources...)
|
|
return keycloak.ResourceImportReport{ResourcesWritten: len(resources), ResourceServerID: "rs:markitect"}, nil
|
|
}
|
|
|
|
func (c *fakeClient) Health(context.Context) error {
|
|
return nil
|
|
}
|