- module path renamed (no external importers); golangci local-prefixes - ACCESS_ENGINE_RUN_TOPAZ_INTEGRATION replaces FLEX_AUTH_RUN_TOPAZ_INTEGRATION - README title and identity paragraph reflect the landed renames - docs/runtime-rename-inventory.md: per-consumer references by kind and order - chart name, image package and /opt paths deliberately left for T05 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 735967@bnt-lap001 Assistant-Session: 5971967f-77f8-4ced-9e02-c6ef67040ffc
96 lines
2.6 KiB
Go
96 lines
2.6 KiB
Go
package directory
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/netkingdom/access-engine/pkg/api"
|
|
)
|
|
|
|
// MergeResults combines resolver results while keeping source metadata.
|
|
func MergeResults(subject api.SubjectRef, results ...ResolveResult) SubjectEnrichment {
|
|
groups := map[string]struct{}{}
|
|
roles := map[api.CanonicalRole]struct{}{}
|
|
metadata := map[string]any{}
|
|
enrichment := SubjectEnrichment{
|
|
Subject: subject,
|
|
Metadata: metadata,
|
|
}
|
|
|
|
for _, result := range results {
|
|
for _, group := range result.Groups {
|
|
groups[group.ID] = struct{}{}
|
|
if group.Descriptor != nil {
|
|
enrichment.Descriptors = append(enrichment.Descriptors, *group.Descriptor)
|
|
}
|
|
}
|
|
for _, role := range result.Roles {
|
|
roles[role.Role] = struct{}{}
|
|
}
|
|
if !result.Freshness.RetrievedAt.IsZero() || result.Freshness.Source != "" {
|
|
enrichment.Freshness = append(enrichment.Freshness, result.Freshness)
|
|
}
|
|
if result.Overage.Detected {
|
|
enrichment.Overage = append(enrichment.Overage, result.Overage)
|
|
}
|
|
if result.Source != "" {
|
|
metadata["source:"+string(result.Source)] = true
|
|
}
|
|
}
|
|
|
|
enrichment.Groups = sortedStringKeys(groups)
|
|
enrichment.Roles = sortedRoleKeys(roles)
|
|
return enrichment
|
|
}
|
|
|
|
// ApplyToSubject returns a subject with resolved groups, roles, and metadata.
|
|
func ApplyToSubject(subject api.Subject, enrichment SubjectEnrichment) api.Subject {
|
|
out := subject
|
|
out.Groups = mergeStrings(out.Groups, enrichment.Groups)
|
|
out.Roles = mergeRoles(out.Roles, enrichment.Roles)
|
|
if out.Metadata == nil {
|
|
out.Metadata = map[string]any{}
|
|
}
|
|
out.Metadata["directory_freshness"] = enrichment.Freshness
|
|
out.Metadata["directory_overage"] = enrichment.Overage
|
|
return out
|
|
}
|
|
|
|
func mergeStrings(a, b []string) []string {
|
|
items := map[string]struct{}{}
|
|
for _, value := range a {
|
|
items[value] = struct{}{}
|
|
}
|
|
for _, value := range b {
|
|
items[value] = struct{}{}
|
|
}
|
|
return sortedStringKeys(items)
|
|
}
|
|
|
|
func mergeRoles(a, b []api.CanonicalRole) []api.CanonicalRole {
|
|
items := map[api.CanonicalRole]struct{}{}
|
|
for _, value := range a {
|
|
items[value] = struct{}{}
|
|
}
|
|
for _, value := range b {
|
|
items[value] = struct{}{}
|
|
}
|
|
return sortedRoleKeys(items)
|
|
}
|
|
|
|
func sortedStringKeys(items map[string]struct{}) []string {
|
|
keys := make([]string, 0, len(items))
|
|
for key := range items {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
return keys
|
|
}
|
|
|
|
func sortedRoleKeys(items map[api.CanonicalRole]struct{}) []api.CanonicalRole {
|
|
keys := make([]api.CanonicalRole, 0, len(items))
|
|
for key := range items {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })
|
|
return keys
|
|
}
|