1
0
forked from baron/baron-sso

테넌트 접근 제한 로직 보강

This commit is contained in:
2026-04-28 10:57:16 +09:00
parent 367368805a
commit 955128a25a
3 changed files with 375 additions and 48 deletions

View File

@@ -3944,6 +3944,70 @@ func (h *AuthHandler) GetMe(c *fiber.Ctx) error {
return c.JSON(profile)
}
func (h *AuthHandler) resolveProfileForSubject(ctx context.Context, subject string) (*domain.UserProfileResponse, error) {
subject = strings.TrimSpace(subject)
if subject == "" || h.KratosAdmin == nil {
return nil, fmt.Errorf("subject profile unavailable")
}
identity, err := h.KratosAdmin.GetIdentity(ctx, subject)
if err != nil {
return nil, err
}
if identity == nil {
return nil, fmt.Errorf("identity not found")
}
profile := h.mapKratosIdentityToProfile(identity.ID, identity.Traits)
if profile == nil {
return nil, fmt.Errorf("failed to map identity profile")
}
return h.hydrateResolvedProfile(ctx, profile), nil
}
func (h *AuthHandler) hydrateResolvedProfile(ctx context.Context, profile *domain.UserProfileResponse) *domain.UserProfileResponse {
if profile == nil {
return nil
}
profile.Role = domain.NormalizeRole(profile.Role)
if profile.Role == "" {
profile.Role = domain.RoleUser
}
if h.TenantService != nil {
if profile.Tenant == nil && profile.TenantID != nil && *profile.TenantID != "" {
if tenant, err := h.TenantService.GetTenant(ctx, *profile.TenantID); err == nil {
profile.Tenant = tenant
}
}
if profile.Tenant == nil && profile.CompanyCode != "" {
if tenant, err := h.TenantService.GetTenantBySlug(ctx, profile.CompanyCode); err == nil && tenant != nil {
profile.Tenant = tenant
if profile.TenantID == nil || *profile.TenantID == "" {
profile.TenantID = &tenant.ID
}
}
}
}
if h.TenantService != nil {
if profile.Role == domain.RoleTenantAdmin {
manageable, err := h.TenantService.ListManageableTenants(ctx, profile.ID)
if err == nil {
profile.ManageableTenants = manageable
}
}
joined, err := h.TenantService.ListJoinedTenants(ctx, profile.ID)
if err == nil {
profile.JoinedTenants = joined
}
}
return profile
}
// GetEnrichedProfile - Exported wrapper for resolveCurrentProfile used by middlewares
func (h *AuthHandler) GetEnrichedProfile(c *fiber.Ctx) (*domain.UserProfileResponse, error) {
return h.resolveCurrentProfile(c)
@@ -5132,8 +5196,14 @@ func (h *AuthHandler) GetConsentRequest(c *fiber.Ctx) error {
)
profile, err := h.resolveCurrentProfile(c)
if tenantErr := enforceClientTenantAccess(c, consentRequest.Client, profile, err); tenantErr != nil {
return tenantErr
if (err != nil || profile == nil) && consentRequest.Subject != "" {
if fallbackProfile, fallbackErr := h.resolveProfileForSubject(c.Context(), consentRequest.Subject); fallbackErr == nil {
profile = fallbackProfile
err = nil
}
}
if enforceClientTenantAccess(c, h.TenantService, consentRequest.Client, profile, err) {
return nil
}
// [New] 로컬 DB에서 기존 동의 내역 확인 (강제 자동 승인 전략)
@@ -5342,8 +5412,14 @@ func (h *AuthHandler) AcceptConsentRequest(c *fiber.Ctx) error {
consentRequest.RequestedScope = mergeRequestedScopesWithClientRequirements(consentRequest.Client, consentRequest.RequestedScope)
profile, err := h.resolveCurrentProfile(c)
if tenantErr := enforceClientTenantAccess(c, consentRequest.Client, profile, err); tenantErr != nil {
return tenantErr
if (err != nil || profile == nil) && consentRequest.Subject != "" {
if fallbackProfile, fallbackErr := h.resolveProfileForSubject(c.Context(), consentRequest.Subject); fallbackErr == nil {
profile = fallbackProfile
err = nil
}
}
if enforceClientTenantAccess(c, h.TenantService, consentRequest.Client, profile, err) {
return nil
}
// 3. Hydra에 승인 요청
@@ -5484,9 +5560,15 @@ func (h *AuthHandler) AcceptOidcLoginRequest(c *fiber.Ctx) error {
}
profile, err := h.resolveCurrentProfile(c)
if (err != nil || profile == nil) && loginReq != nil && strings.TrimSpace(loginReq.Subject) != "" {
if fallbackProfile, fallbackErr := h.resolveProfileForSubject(c.Context(), loginReq.Subject); fallbackErr == nil {
profile = fallbackProfile
err = nil
}
}
if loginReq != nil {
if tenantErr := enforceClientTenantAccess(c, loginReq.Client, profile, err); tenantErr != nil {
return tenantErr
if enforceClientTenantAccess(c, h.TenantService, loginReq.Client, profile, err) {
return nil
}
}
@@ -5631,37 +5713,7 @@ func (h *AuthHandler) resolveCurrentProfile(c *fiber.Ctx) (*domain.UserProfileRe
delete(profile.Metadata, "_used_identifier") // Cleanup
}
// Fetch Tenant Metadata if missing
if h.TenantService != nil {
if profile.Tenant == nil && profile.TenantID != nil && *profile.TenantID != "" {
if tenant, err := h.TenantService.GetTenant(c.Context(), *profile.TenantID); err == nil {
profile.Tenant = tenant
}
}
if profile.Tenant == nil && profile.CompanyCode != "" {
if tenant, err := h.TenantService.GetTenantBySlug(c.Context(), profile.CompanyCode); err == nil && tenant != nil {
profile.Tenant = tenant
if profile.TenantID == nil || *profile.TenantID == "" {
profile.TenantID = &tenant.ID
}
}
}
}
// [New] Fetch manageable and joined tenants
if h.TenantService != nil {
if profile.Role == domain.RoleTenantAdmin {
manageable, err := h.TenantService.ListManageableTenants(c.Context(), profile.ID)
if err == nil {
profile.ManageableTenants = manageable
}
}
joined, err := h.TenantService.ListJoinedTenants(c.Context(), profile.ID)
if err == nil {
profile.JoinedTenants = joined
}
}
profile = h.hydrateResolvedProfile(c.Context(), profile)
// 4. Save to Redis Cache (Short TTL)
// IMPORTANT: In dev mode, if role was overridden, we should NOT cache it under the token key