forked from baron/baron-sso
RP 테넌트 제한 backend 구현
This commit is contained in:
144
backend/internal/handler/client_tenant_access.go
Normal file
144
backend/internal/handler/client_tenant_access.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"errors"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
clientTenantAccessRestrictedKey = "tenant_access_restricted"
|
||||
clientAllowedTenantsKey = "allowed_tenants"
|
||||
)
|
||||
|
||||
func normalizeClientTenantAccessMetadata(metadata map[string]interface{}) (map[string]interface{}, error) {
|
||||
if metadata == nil {
|
||||
metadata = map[string]interface{}{}
|
||||
}
|
||||
|
||||
restricted := readMetadataBoolValue(metadata, clientTenantAccessRestrictedKey)
|
||||
allowedTenants := normalizeMetadataStringSlice(metadata[clientAllowedTenantsKey])
|
||||
ownerTenantID := normalizeMetadataString(metadata["tenant_id"])
|
||||
|
||||
if len(allowedTenants) > 0 {
|
||||
restricted = true
|
||||
}
|
||||
|
||||
if !restricted {
|
||||
delete(metadata, clientAllowedTenantsKey)
|
||||
metadata[clientTenantAccessRestrictedKey] = false
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
if ownerTenantID != "" {
|
||||
allowedTenants = append(allowedTenants, ownerTenantID)
|
||||
}
|
||||
allowedTenants = uniqueSortedStrings(allowedTenants)
|
||||
if len(allowedTenants) == 0 {
|
||||
return nil, errors.New("allowed_tenants is required when tenant_access_restricted is enabled")
|
||||
}
|
||||
|
||||
metadata[clientTenantAccessRestrictedKey] = true
|
||||
metadata[clientAllowedTenantsKey] = allowedTenants
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func clientTenantAccessRestricted(metadata map[string]interface{}) bool {
|
||||
if metadata == nil {
|
||||
return false
|
||||
}
|
||||
if readMetadataBoolValue(metadata, clientTenantAccessRestrictedKey) {
|
||||
return true
|
||||
}
|
||||
return len(normalizeMetadataStringSlice(metadata[clientAllowedTenantsKey])) > 0
|
||||
}
|
||||
|
||||
func clientAllowedTenants(metadata map[string]interface{}) []string {
|
||||
if metadata == nil {
|
||||
return nil
|
||||
}
|
||||
if !clientTenantAccessRestricted(metadata) {
|
||||
return nil
|
||||
}
|
||||
return uniqueSortedStrings(normalizeMetadataStringSlice(metadata[clientAllowedTenantsKey]))
|
||||
}
|
||||
|
||||
func normalizeMetadataStringSlice(raw any) []string {
|
||||
switch value := raw.(type) {
|
||||
case []string:
|
||||
return uniqueSortedStrings(value)
|
||||
case []any:
|
||||
items := make([]string, 0, len(value))
|
||||
for _, item := range value {
|
||||
if s, ok := item.(string); ok {
|
||||
items = append(items, s)
|
||||
}
|
||||
}
|
||||
return uniqueSortedStrings(items)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeMetadataString(raw any) string {
|
||||
s, ok := raw.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
func uniqueSortedStrings(values []string) []string {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
seen := make(map[string]struct{}, len(values))
|
||||
out := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
if trimmed == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[trimmed]; ok {
|
||||
continue
|
||||
}
|
||||
seen[trimmed] = struct{}{}
|
||||
out = append(out, trimmed)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func clientTenantAccessAllowed(profile *domain.UserProfileResponse, client domain.HydraClient) bool {
|
||||
if !clientTenantAccessRestricted(client.Metadata) {
|
||||
return true
|
||||
}
|
||||
allowed := clientAllowedTenants(client.Metadata)
|
||||
if len(allowed) == 0 {
|
||||
return false
|
||||
}
|
||||
keys := manageableTenantKeysFromProfile(profile)
|
||||
if len(keys) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, tenantID := range allowed {
|
||||
if _, ok := keys[strings.ToLower(strings.TrimSpace(tenantID))]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func tenantNotAllowedError(c *fiber.Ctx) error {
|
||||
return errorJSONCode(c, fiber.StatusForbidden, "tenant_not_allowed", "허용되지 않은 테넌트입니다.")
|
||||
}
|
||||
|
||||
func isClientTenantAccessAllowed(profile *domain.UserProfileResponse, client domain.HydraClient) bool {
|
||||
if profile == nil {
|
||||
return false
|
||||
}
|
||||
return clientTenantAccessAllowed(profile, client)
|
||||
}
|
||||
Reference in New Issue
Block a user