fix(PROJ-23): Privilege Escalation in Tenant-LDAP + Login-Reihenfolge
- BUG-1 (P0): domain_admin kann keine Rollen > auditor in default_role/ group_mappings setzen — serverseitige Allowlist-Prüfung in handleSaveTenantLDAP (user/auditor) und handleAdminSaveTenantLDAP (user/auditor/domain_admin) - WARN-1: Login-Fallback-Reihenfolge korrigiert — tenant_ldap wird jetzt VOR globalem ldap_config geprüft (Spec: tenant > global > local) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+42
-46
@@ -68,50 +68,9 @@ func (m *Manager) Login(username, password string) (string, *userstore.User, err
|
||||
return m.issueToken(user)
|
||||
}
|
||||
|
||||
// 2. Global LDAP fallback when the store is wired and the config is enabled.
|
||||
if m.ldapStore != nil {
|
||||
cfg, ldapErr := m.ldapStore.GetWithPassword(context.Background())
|
||||
if ldapErr == nil && cfg != nil && cfg.Enabled {
|
||||
attrs, authErr := ldapauth.Authenticate(ldapauth.Config{
|
||||
URL: cfg.URL,
|
||||
BindDN: cfg.BindDN,
|
||||
BindPassword: cfg.BindPassword,
|
||||
BaseDN: cfg.BaseDN,
|
||||
UserFilter: cfg.UserFilter,
|
||||
TLS: cfg.TLS,
|
||||
TLSSkipVerify: cfg.TLSSkipVerify,
|
||||
}, username, password)
|
||||
if authErr == nil {
|
||||
// Determine role: check group_mappings first, fall back to default_role.
|
||||
role := cfg.DefaultRole
|
||||
if role == "" {
|
||||
role = userstore.RoleUser
|
||||
}
|
||||
memberOf := attrs["memberOf"]
|
||||
if memberOf != "" {
|
||||
for _, gm := range cfg.GroupMappings {
|
||||
if gm.GroupDN != "" && containsGroup(memberOf, gm.GroupDN) {
|
||||
role = gm.Role
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
email := attrs["mail"]
|
||||
if email == "" {
|
||||
email = username + "@ldap.local"
|
||||
}
|
||||
|
||||
ldapUser, upsertErr := m.store.UpsertLDAPUser(username, email, role, nil)
|
||||
if upsertErr == nil {
|
||||
return m.issueToken(ldapUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. PROJ-23: Per-tenant LDAP fallback — extract domain from username,
|
||||
// look up the tenant, and try LDAP auth with that tenant's config.
|
||||
// 2. PROJ-23: Per-tenant LDAP — checked first so tenant config takes priority
|
||||
// over global LDAP (spec: tenant_ldap > ldap_config > config.yml).
|
||||
// Extract domain from username (user@domain.tld), resolve tenant, try auth.
|
||||
if m.tenantLdapStore != nil && m.tenantLookup != nil {
|
||||
if domain := extractDomain(username); domain != "" {
|
||||
ctx := context.Background()
|
||||
@@ -142,12 +101,10 @@ func (m *Manager) Login(username, password string) (string, *userstore.User, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
email := attrs["mail"]
|
||||
if email == "" {
|
||||
email = username
|
||||
}
|
||||
|
||||
ldapUser, upsertErr := m.store.UpsertLDAPUser(username, email, role, tenantID)
|
||||
if upsertErr == nil {
|
||||
return m.issueToken(ldapUser)
|
||||
@@ -158,6 +115,45 @@ func (m *Manager) Login(username, password string) (string, *userstore.User, err
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Global LDAP fallback — only reached if no matching tenant LDAP config found.
|
||||
if m.ldapStore != nil {
|
||||
cfg, ldapErr := m.ldapStore.GetWithPassword(context.Background())
|
||||
if ldapErr == nil && cfg != nil && cfg.Enabled {
|
||||
attrs, authErr := ldapauth.Authenticate(ldapauth.Config{
|
||||
URL: cfg.URL,
|
||||
BindDN: cfg.BindDN,
|
||||
BindPassword: cfg.BindPassword,
|
||||
BaseDN: cfg.BaseDN,
|
||||
UserFilter: cfg.UserFilter,
|
||||
TLS: cfg.TLS,
|
||||
TLSSkipVerify: cfg.TLSSkipVerify,
|
||||
}, username, password)
|
||||
if authErr == nil {
|
||||
role := cfg.DefaultRole
|
||||
if role == "" {
|
||||
role = userstore.RoleUser
|
||||
}
|
||||
memberOf := attrs["memberOf"]
|
||||
if memberOf != "" {
|
||||
for _, gm := range cfg.GroupMappings {
|
||||
if gm.GroupDN != "" && containsGroup(memberOf, gm.GroupDN) {
|
||||
role = gm.Role
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
email := attrs["mail"]
|
||||
if email == "" {
|
||||
email = username + "@ldap.local"
|
||||
}
|
||||
ldapUser, upsertErr := m.store.UpsertLDAPUser(username, email, role, nil)
|
||||
if upsertErr == nil {
|
||||
return m.issueToken(ldapUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil, fmt.Errorf("auth: login: invalid credentials")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user