ultisuite-backend/internal/orgpolicy/auth.go
R3D347HR4Y d3c930cac6
Some checks are pending
CI / Go tests (push) Waiting to run
CI / Integration tests (push) Waiting to run
CI / DB migrations (push) Waiting to run
feat(identity-providers): add management for identity providers in admin API
- Introduced new endpoints for managing identity providers, including retrieval of redirect URIs and testing/syncing providers.
- Enhanced organization settings to include identity provider configurations, allowing for self-enrollment and domain restrictions.
- Implemented caching for access policies and added validation for identity provider secrets.
- Added integration tests to ensure proper functionality of identity provider management and policy enforcement.
2026-06-09 09:36:38 +02:00

145 lines
3.2 KiB
Go

package orgpolicy
import (
"strings"
"github.com/ultisuite/ulti-backend/internal/auth"
)
type IdentityProviderPolicy struct {
ID string
Slug string
Type string
Enabled bool
AllowedEmailDomains []string
AllowedIdentities []string
AllowedOrganizations []string
}
type AuthAccessPolicy struct {
AllowSelfEnrollment bool
Providers []IdentityProviderPolicy
}
func (p AuthAccessPolicy) AllowsIdentity(email string, claims *auth.Claims) bool {
enabled := make([]IdentityProviderPolicy, 0)
for _, provider := range p.Providers {
if provider.Enabled {
enabled = append(enabled, provider)
}
}
if len(enabled) == 0 {
return true
}
if claims != nil && strings.TrimSpace(claims.Source) != "" {
for _, provider := range enabled {
if provider.Slug == claims.Source && providerAllows(provider, email, claims) {
return true
}
}
}
hasRestrictions := false
for _, provider := range enabled {
if providerHasRestrictions(provider) {
hasRestrictions = true
if providerAllows(provider, email, claims) {
return true
}
}
}
if !hasRestrictions {
return true
}
return false
}
func providerHasRestrictions(provider IdentityProviderPolicy) bool {
return len(provider.AllowedEmailDomains) > 0 ||
len(provider.AllowedIdentities) > 0 ||
len(provider.AllowedOrganizations) > 0
}
func providerAllows(provider IdentityProviderPolicy, email string, claims *auth.Claims) bool {
if len(provider.AllowedIdentities) > 0 {
if !containsFold(provider.AllowedIdentities, email) {
return false
}
}
if len(provider.AllowedEmailDomains) > 0 {
if !emailDomainAllowed(email, provider.AllowedEmailDomains) {
return false
}
}
if len(provider.AllowedOrganizations) > 0 {
if claims == nil || !organizationAllowed(claims, provider.AllowedOrganizations) {
return false
}
}
return true
}
func emailDomainAllowed(email string, domains []string) bool {
email = strings.ToLower(strings.TrimSpace(email))
at := strings.LastIndex(email, "@")
if at < 0 {
return false
}
domain := email[at+1:]
for _, allowed := range domains {
allowed = strings.ToLower(strings.TrimSpace(strings.TrimPrefix(allowed, "@")))
if allowed == "" {
continue
}
if domain == allowed || strings.HasSuffix(domain, "."+allowed) {
return true
}
}
return false
}
func organizationAllowed(claims *auth.Claims, orgs []string) bool {
candidates := []string{
claims.HD,
claims.TID,
claims.Org,
}
for _, org := range orgs {
org = strings.TrimSpace(org)
if org == "" {
continue
}
for _, candidate := range candidates {
if strings.EqualFold(strings.TrimSpace(candidate), org) {
return true
}
}
}
return false
}
func containsFold(list []string, value string) bool {
value = strings.ToLower(strings.TrimSpace(value))
for _, item := range list {
if strings.ToLower(strings.TrimSpace(item)) == value {
return true
}
}
return false
}
func stringSlice(v any) []string {
raw, ok := v.([]any)
if !ok {
return nil
}
out := make([]string, 0, len(raw))
for _, item := range raw {
if s, ok := item.(string); ok && strings.TrimSpace(s) != "" {
out = append(out, strings.TrimSpace(s))
}
}
return out
}