1
0
forked from baron/baron-sso

offline 스코프 제거, rp_claims 값 표준화

This commit is contained in:
2026-06-11 14:50:26 +09:00
parent f60b15a17b
commit c495e9119b
26 changed files with 1034 additions and 300 deletions

View File

@@ -9,6 +9,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gofiber/fiber/v2"
@@ -511,7 +512,7 @@ func TestWithHanmacFamilyTenantClaims_DefaultClaimsOnlyWithoutTenantScope(t *tes
assert.NotContains(t, claims, "lead_tenants")
}
func TestAcceptConsentRequest_IncludesRPProfileClaims(t *testing.T) {
func TestAcceptConsentRequest_DoesNotEmitLegacyProfileArray(t *testing.T) {
var capturedClaims map[string]any
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
@@ -579,7 +580,7 @@ func TestAcceptConsentRequest_IncludesRPProfileClaims(t *testing.T) {
"approvalLevel": "A",
"internalMemo": "관리자 전용",
},
}, nil).Once()
}, nil).Maybe()
h.RPUserMetadataRepo = repo
app := fiber.New()
@@ -597,14 +598,7 @@ func TestAcceptConsentRequest_IncludesRPProfileClaims(t *testing.T) {
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.NotNil(t, capturedClaims)
rpProfiles, ok := capturedClaims["rp_profiles"].([]any)
assert.True(t, ok)
assert.Len(t, rpProfiles, 1)
profile := rpProfiles[0].(map[string]any)
assert.Equal(t, "client-app", profile["client_id"])
fields := profile["fields"].(map[string]any)
assert.Equal(t, "A", fields["approvalLevel"])
assert.NotContains(t, fields, "internalMemo")
assert.NotContains(t, capturedClaims, legacyProfileArrayClaimKeyForTest())
repo.AssertExpectations(t)
}
@@ -728,7 +722,7 @@ func TestAcceptConsentRequest_AppliesConfiguredIDTokenClaims(t *testing.T) {
if r.URL.Path == "/oauth2/auth/requests/consent" && r.URL.Query().Get("consent_challenge") == "challenge-configured-claims" {
return httpJSONAny(r, http.StatusOK, map[string]any{
"challenge": "challenge-configured-claims",
"requested_scope": []string{"openid", "profile"},
"requested_scope": []string{"openid", "profile", "tenant"},
"subject": "user-789",
"client": map[string]any{
"client_id": "client-configured-claims",
@@ -823,8 +817,13 @@ func TestAcceptConsentRequest_AppliesConfiguredIDTokenClaims(t *testing.T) {
rpClaims, ok := capturedClaims["rp_claims"].(map[string]any)
if assert.True(t, ok) {
assert.Equal(t, float64(2), rpClaims["tier"])
assert.Equal(t, []any{"sso", "claims"}, rpClaims["features"])
tier := rpClaims["tier"].(map[string]any)
assert.Equal(t, float64(2), tier["value"])
assert.Equal(t, "admin_only", tier["readPermission"])
assert.Equal(t, "admin_only", tier["writePermission"])
features := rpClaims["features"].(map[string]any)
assert.Equal(t, []any{"sso", "claims"}, features["value"])
}
}
@@ -835,7 +834,7 @@ func TestAcceptConsentRequest_UsesUpdatedRPUserMetadataForRPClaims(t *testing.T)
if r.URL.Path == "/oauth2/auth/requests/consent" && r.URL.Query().Get("consent_challenge") == "challenge-rp-user-claims" {
return httpJSONAny(r, http.StatusOK, map[string]any{
"challenge": "challenge-rp-user-claims",
"requested_scope": []string{"openid", "profile"},
"requested_scope": []string{"openid", "profile", "tenant"},
"subject": "user-rp-claims",
"client": map[string]any{
"client_id": "client-rp-claims",
@@ -883,6 +882,14 @@ func TestAcceptConsentRequest_UsesUpdatedRPUserMetadataForRPClaims(t *testing.T)
"value": "2026-06-09T09:30",
"valueType": "datetime",
},
{
"namespace": "rp_claims",
"key": "tenants",
"value": "must-not-shadow-tenants",
"valueType": "text",
"readPermission": "user_and_admin",
"writePermission": "user_and_admin",
},
},
},
},
@@ -914,10 +921,36 @@ func TestAcceptConsentRequest_UsesUpdatedRPUserMetadataForRPClaims(t *testing.T)
h.KratosAdmin.(*MockKratosAdminService).On("GetIdentity", mock.Anything, "user-rp-claims").Return(&service.KratosIdentity{
ID: "user-rp-claims",
Traits: map[string]any{
"email": "rp-user@example.com",
"name": "RP User",
"email": "rp-user@example.com",
"name": "RP User",
"tenant_id": "tenant-leaf",
"tenant-leaf": map[string]any{
"department": "Platform",
"rp_claims": map[string]any{"mustNotLeak": true},
"rp_custom_claims": map[string]any{"client-rp-claims": map[string]any{"mustNotLeak": true}},
},
"rp_custom_claims": map[string]any{
"client-rp-claims": map[string]any{"approvalLevel": "B"},
},
},
}, nil)
rootTenantID := "tenant-root"
mockTenantSvc := new(MockTenantService)
mockTenantSvc.On("GetTenant", mock.Anything, "tenant-leaf").Return(&domain.Tenant{
ID: "tenant-leaf",
Slug: "platform",
Name: "플랫폼팀",
Type: domain.TenantTypeUserGroup,
ParentID: &rootTenantID,
}, nil)
mockTenantSvc.On("GetTenant", mock.Anything, rootTenantID).Return(&domain.Tenant{
ID: rootTenantID,
Slug: "root",
Name: "루트",
Type: domain.TenantTypeCompany,
}, nil)
mockTenantSvc.On("ListJoinedTenants", mock.Anything, "user-rp-claims").Return([]domain.Tenant{}, nil)
h.TenantService = mockTenantSvc
repo := new(devMockRPUserMetadataRepo)
repo.On("Get", mock.Anything, "client-rp-claims", "user-rp-claims").Return(&domain.RPUserMetadata{
ClientID: "client-rp-claims",
@@ -931,8 +964,8 @@ func TestAcceptConsentRequest_UsesUpdatedRPUserMetadataForRPClaims(t *testing.T)
"theme": "dark",
"density": "compact",
},
"contractDate": "2026-06-10",
"approvedAt": "2026-06-09T10:30",
"contractDate": float64(1781017200),
"approvedAt": float64(1780968600),
"internalMemo": "must-not-leak",
"approvalLevel_permissions": map[string]any{
"readPermission": "admin_only",
@@ -947,7 +980,7 @@ func TestAcceptConsentRequest_UsesUpdatedRPUserMetadataForRPClaims(t *testing.T)
reqBody, _ := json.Marshal(map[string]any{
"consent_challenge": "challenge-rp-user-claims",
"grant_scope": []string{"openid", "profile"},
"grant_scope": []string{"openid", "profile", "tenant"},
})
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/consent/accept", bytes.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
@@ -959,16 +992,58 @@ func TestAcceptConsentRequest_UsesUpdatedRPUserMetadataForRPClaims(t *testing.T)
assert.NotNil(t, capturedClaims)
rpClaims, ok := capturedClaims["rp_claims"].(map[string]any)
if assert.True(t, ok) {
assert.Equal(t, "B", rpClaims["approvalLevel"])
assert.Equal(t, false, rpClaims["activeMember"])
assert.Equal(t, float64(42), rpClaims["score"])
assert.Equal(t, []any{"sso", "claims"}, rpClaims["featureList"])
assert.Equal(t, map[string]any{"theme": "dark", "density": "compact"}, rpClaims["preferences"])
assert.Equal(t, "2026-06-10", rpClaims["contractDate"])
assert.Equal(t, "2026-06-09T10:30", rpClaims["approvedAt"])
approvalLevel := rpClaims["approvalLevel"].(map[string]any)
assert.Equal(t, "B", approvalLevel["value"])
assert.Equal(t, "user_and_admin", approvalLevel["readPermission"])
assert.Equal(t, "user_and_admin", approvalLevel["writePermission"])
activeMember := rpClaims["activeMember"].(map[string]any)
assert.Equal(t, false, activeMember["value"])
score := rpClaims["score"].(map[string]any)
assert.Equal(t, float64(42), score["value"])
featureList := rpClaims["featureList"].(map[string]any)
assert.Equal(t, []any{"sso", "claims"}, featureList["value"])
preferences := rpClaims["preferences"].(map[string]any)
assert.Equal(t, map[string]any{"theme": "dark", "density": "compact"}, preferences["value"])
contractDate := rpClaims["contractDate"].(map[string]any)
assert.Equal(t, float64(1781017200), contractDate["value"])
approvedAt := rpClaims["approvedAt"].(map[string]any)
assert.Equal(t, float64(1780968600), approvedAt["value"])
assert.NotContains(t, rpClaims, "tenants")
assert.NotContains(t, rpClaims, "internalMemo")
assert.NotContains(t, rpClaims, "approvalLevel_permissions")
}
assert.NotContains(t, capturedClaims, "rp_profiles")
assert.NotContains(t, capturedClaims["joined_tenants"], "rp_custom_claims")
tenants := capturedClaims["tenants"].(map[string]any)
assert.Contains(t, tenants, "tenant-leaf")
assert.NotEqual(t, "must-not-shadow-tenants", capturedClaims["tenants"])
assertNoRPClaimDataInTenantClaims(t, tenants)
assert.NotContains(t, capturedClaims, legacyProfileArrayClaimKeyForTest())
repo.AssertExpectations(t)
}
func legacyProfileArrayClaimKeyForTest() string {
return strings.Join([]string{"rp", "profiles"}, "_")
}
func assertNoRPClaimDataInTenantClaims(t *testing.T, value any) {
t.Helper()
switch typed := value.(type) {
case map[string]any:
for key, child := range typed {
assert.False(t, strings.HasPrefix(key, "rp_"))
assertNoRPClaimDataInTenantClaims(t, child)
}
case []any:
for _, child := range typed {
assertNoRPClaimDataInTenantClaims(t, child)
}
}
}