Files
flex-auth/internal/adapters/directory/enrichment.go
tegwick 32933c71f9
Some checks failed
CI / Build and Test (push) Has been cancelled
CI / Lint (push) Has been cancelled
Add directory group resolver adapters
2026-05-17 07:24:50 +02:00

97 lines
2.6 KiB
Go

package directory
import (
"sort"
"github.com/netkingdom/flex-auth/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
}