Map explicit tenant groups into OIDC claims
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 26s

This commit is contained in:
tegwick 2026-07-28 00:34:19 +02:00
parent 993a4dd589
commit e8b4eded88
3 changed files with 96 additions and 3 deletions

View file

@ -290,6 +290,7 @@ func mapEntryToUser(entry *ldap.Entry) domain.User {
for _, dn := range memberOfs {
groups = append(groups, groupNameFromDN(dn))
}
tenant, roles := identityEnvelopeFromGroups(groups)
return domain.User{
ID: entry.DN,
@ -297,10 +298,48 @@ func mapEntryToUser(entry *ldap.Entry) domain.User {
DisplayName: displayName,
Email: entry.GetAttributeValue("mail"),
Groups: groups,
Roles: roles,
Tenant: tenant,
Enabled: true, // LLDAP does not expose a disabled flag in base schema
}
}
// identityEnvelopeFromGroups maps explicit tenant membership groups to the
// coarse IAM Profile envelope. Fine-grained authorization remains flex-auth's
// responsibility. Supported names are:
//
// tenant:<kind>:<slug>:users
// tenant:<kind>:<slug>:admins
//
// Multiple tenant envelopes are deliberately ignored because an interactive
// token must carry one unambiguous active tenant.
func identityEnvelopeFromGroups(groups []string) (string, []string) {
tenants := make(map[string]bool)
admins := make(map[string]bool)
for _, group := range groups {
switch {
case strings.HasPrefix(group, "tenant:") && strings.HasSuffix(group, ":users"):
tenants[strings.TrimSuffix(group, ":users")] = true
case strings.HasPrefix(group, "tenant:") && strings.HasSuffix(group, ":admins"):
tenant := strings.TrimSuffix(group, ":admins")
tenants[tenant] = true
admins[tenant] = true
}
}
if len(tenants) != 1 {
return "", []string{}
}
var tenant string
for candidate := range tenants {
tenant = candidate
}
roles := []string{"user"}
if admins[tenant] {
roles = append(roles, "tenant-admin")
}
return tenant, roles
}
// groupNameFromDN extracts the cn value from an LDAP DN such as
// "cn=admins,ou=groups,dc=netkingdom,dc=local" → "admins".
// If parsing fails the full DN is returned unchanged.