1
0
forked from baron/baron-sso

custom claim 권한체크 확인

This commit is contained in:
2026-06-11 08:29:25 +09:00
parent 839ca9d407
commit 4d77060b5d
79 changed files with 4268 additions and 670 deletions

View File

@@ -34,6 +34,7 @@ type DevHandler struct {
SecretRepo domain.ClientSecretRepository
AuditRepo domain.AuditRepository
KratosAdmin service.KratosAdminService
IdentityWriter service.IdentityWriteService
ConsentRepo repository.ClientConsentRepository
Keto service.KetoService
KetoOutbox repository.KetoOutboxRepository
@@ -78,13 +79,18 @@ func NewDevHandler(
authProvider = auth[0]
}
kratosAdmin := service.NewKratosAdminService()
return &DevHandler{
Hydra: service.NewHydraAdminService(),
Redis: redis,
HeadlessJWKS: service.NewHeadlessJWKSCacheService(redis, nil),
SecretRepo: secretRepo,
AuditRepo: nil,
KratosAdmin: service.NewKratosAdminService(),
KratosAdmin: kratosAdmin,
IdentityWriter: service.NewIdentityWriteService(
kratosAdmin,
redis,
),
ConsentRepo: consentRepo,
Keto: keto,
KetoOutbox: ketoOutbox,
@@ -233,6 +239,7 @@ type normalizedIDTokenClaim struct {
Key string `json:"key"`
Value string `json:"value"`
ValueType string `json:"valueType"`
Nullable bool `json:"nullable"`
ReadPermission string `json:"readPermission"`
WritePermission string `json:"writePermission"`
}
@@ -1659,7 +1666,17 @@ func (h *DevHandler) syncRPUserMetadataToKratos(ctx context.Context, userID stri
}
rawRPClaims[clientID] = metadata
traits["rp_custom_claims"] = rawRPClaims
_, err = h.KratosAdmin.UpdateIdentity(ctx, identity.ID, traits, identity.State)
identityWriter := h.IdentityWriter
if identityWriter == nil {
identityWriter = service.NewIdentityWriteService(h.KratosAdmin, h.Redis)
}
_, err = identityWriter.UpdateIdentity(ctx, service.IdentityUpdateRequest{
IdentityID: identity.ID,
Traits: traits,
State: identity.State,
Reason: "rp_custom_claims_sync",
Source: "dev_handler",
})
if err != nil {
return fmt.Errorf("failed to update kratos rp user metadata: %w", err)
}
@@ -3459,8 +3476,11 @@ func normalizeIDTokenClaimsWithOptions(rawClaims any, allowTopLevel bool) ([]nor
}
value := strings.TrimSpace(readInterfaceString(record["value"], ""))
if _, err := parseConfiguredClaimValue(value, valueType); err != nil {
return nil, fmt.Errorf("metadata.id_token_claims %s.%s is invalid: %w", namespace, key, err)
nullable, _ := record["nullable"].(bool)
if !(nullable && value == "") {
if _, err := parseConfiguredClaimValue(value, valueType); err != nil {
return nil, fmt.Errorf("metadata.id_token_claims %s.%s is invalid: %w", namespace, key, err)
}
}
signature := namespace + ":" + key
@@ -3474,6 +3494,7 @@ func normalizeIDTokenClaimsWithOptions(rawClaims any, allowTopLevel bool) ([]nor
Key: key,
Value: value,
ValueType: valueType,
Nullable: nullable,
ReadPermission: normalizeCustomClaimPermission(record["readPermission"]),
WritePermission: normalizeCustomClaimPermission(record["writePermission"]),
})
@@ -3854,6 +3875,85 @@ func (h *DevHandler) countScopedDashboardAuditMetrics(
return failureCount, int64(len(activeSessions)), nil
}
func appendDevTenantUnique(tenants []domain.Tenant, tenant domain.Tenant) []domain.Tenant {
tenantID := strings.TrimSpace(tenant.ID)
tenantSlug := strings.TrimSpace(tenant.Slug)
if tenantID == "" && tenantSlug == "" {
return tenants
}
for _, existing := range tenants {
if tenantID != "" && strings.EqualFold(existing.ID, tenantID) {
return tenants
}
if tenantSlug != "" && strings.EqualFold(existing.Slug, tenantSlug) {
return tenants
}
}
return append(tenants, tenant)
}
func shouldListDevManageableTenants(role string) bool {
switch strings.ToLower(strings.TrimSpace(role)) {
case "tenant_admin", "tenantadmin", "tenant-admin", "rp_admin", "admin":
return true
default:
return false
}
}
func resolveDevProfileAppointmentTenants(ctx context.Context, tenantSvc service.TenantService, metadata map[string]any) []domain.Tenant {
if tenantSvc == nil || metadata == nil {
return nil
}
appointments := userAppointmentSliceFromRaw(metadata["additionalAppointments"])
if len(appointments) == 0 {
return nil
}
tenants := make([]domain.Tenant, 0, len(appointments))
for _, raw := range appointments {
appointment, ok := raw.(map[string]any)
if !ok {
continue
}
if tenantID := normalizeMetadataString(appointment["tenantId"]); tenantID != "" {
if tenant, err := tenantSvc.GetTenant(ctx, tenantID); err == nil && tenant != nil {
tenants = appendDevTenantUnique(tenants, *tenant)
continue
}
}
if tenantID := normalizeMetadataString(appointment["tenant_id"]); tenantID != "" {
if tenant, err := tenantSvc.GetTenant(ctx, tenantID); err == nil && tenant != nil {
tenants = appendDevTenantUnique(tenants, *tenant)
continue
}
}
if tenantSlug := normalizeMetadataString(appointment["tenantSlug"]); tenantSlug != "" {
if tenant, err := tenantSvc.GetTenantBySlug(ctx, tenantSlug); err == nil && tenant != nil {
tenants = appendDevTenantUnique(tenants, *tenant)
continue
}
}
if tenantSlug := normalizeMetadataString(appointment["tenant_slug"]); tenantSlug != "" {
if tenant, err := tenantSvc.GetTenantBySlug(ctx, tenantSlug); err == nil && tenant != nil {
tenants = appendDevTenantUnique(tenants, *tenant)
continue
}
}
if tenantSlug := normalizeMetadataString(appointment["slug"]); tenantSlug != "" {
if tenant, err := tenantSvc.GetTenantBySlug(ctx, tenantSlug); err == nil && tenant != nil {
tenants = appendDevTenantUnique(tenants, *tenant)
}
}
}
return tenants
}
// ListMyTenants returns the list of tenants the current user manages or belongs to.
func (h *DevHandler) ListMyTenants(c *fiber.Ctx) error {
profile, err := h.Auth.GetEnrichedProfile(c)
@@ -3862,20 +3962,6 @@ func (h *DevHandler) ListMyTenants(c *fiber.Ctx) error {
}
role := normalizeUserRole(profile.Role)
if role == domain.RoleUser {
if profile.TenantID == nil || strings.TrimSpace(*profile.TenantID) == "" {
return c.JSON([]domain.Tenant{})
}
tenant, err := h.TenantSvc.GetTenant(c.Context(), *profile.TenantID)
if err != nil {
return errorJSON(c, fiber.StatusInternalServerError, "failed to get tenant")
}
if tenant == nil {
return c.JSON([]domain.Tenant{})
}
return c.JSON([]domain.Tenant{*tenant})
}
if role == domain.RoleSuperAdmin {
tenants, _, err := h.TenantSvc.ListTenants(c.Context(), 100, 0, "", "")
if err != nil {
@@ -3884,26 +3970,32 @@ func (h *DevHandler) ListMyTenants(c *fiber.Ctx) error {
return c.JSON(tenants)
}
tenants, err := h.TenantSvc.ListManageableTenants(c.Context(), profile.ID)
if err != nil {
return errorJSON(c, fiber.StatusInternalServerError, "failed to list manageable tenants: "+err.Error())
tenants := make([]domain.Tenant, 0, 1+len(profile.JoinedTenants))
if shouldListDevManageableTenants(profile.Role) {
manageable, err := h.TenantSvc.ListManageableTenants(c.Context(), profile.ID)
if err != nil {
return errorJSON(c, fiber.StatusInternalServerError, "failed to list manageable tenants: "+err.Error())
}
for _, tenant := range manageable {
tenants = appendDevTenantUnique(tenants, tenant)
}
}
if profile.TenantID != nil && *profile.TenantID != "" {
found := false
for _, t := range tenants {
if t.ID == *profile.TenantID {
found = true
break
}
}
if !found {
if primary, err := h.TenantSvc.GetTenant(c.Context(), *profile.TenantID); err == nil && primary != nil {
tenants = append(tenants, *primary)
}
if primary, err := h.TenantSvc.GetTenant(c.Context(), *profile.TenantID); err != nil {
return errorJSON(c, fiber.StatusInternalServerError, "failed to get tenant")
} else if primary != nil {
tenants = appendDevTenantUnique(tenants, *primary)
}
}
for _, tenant := range profile.JoinedTenants {
tenants = appendDevTenantUnique(tenants, tenant)
}
for _, tenant := range resolveDevProfileAppointmentTenants(c.Context(), h.TenantSvc, profile.Metadata) {
tenants = appendDevTenantUnique(tenants, tenant)
}
return c.JSON(tenants)
}