1
0
forked from baron/baron-sso

코드 테스트 실패 수정

This commit is contained in:
2026-06-11 08:55:41 +09:00
parent 4d77060b5d
commit 0bb3ccb850
13 changed files with 517 additions and 382 deletions

View File

@@ -125,6 +125,100 @@ func TestDevHandler_RPUserMetadataRoundTrip(t *testing.T) {
repo.AssertExpectations(t)
}
func TestDevHandler_RPUserMetadataAdminUpsertRequiresRPManage(t *testing.T) {
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.Path == "/clients/client-1" {
return httpJSONAny(r, http.StatusOK, map[string]any{
"client_id": "client-1",
"client_name": "Client One",
"metadata": map[string]any{
"tenant_id": "tenant-1",
"id_token_claims": []map[string]any{
{
"namespace": "rp_claims",
"key": "approvalLevel",
"valueType": "text",
"value": "A",
"readPermission": "user_and_admin",
"writePermission": "user_and_admin",
},
},
},
}), nil
}
return httpJSONAny(r, http.StatusNotFound, nil), nil
})
t.Run("tenant grant does not allow rp user metadata admin upsert", func(t *testing.T) {
repo := new(devMockRPUserMetadataRepo)
repo.On("Upsert", mock.Anything, mock.AnythingOfType("*domain.RPUserMetadata")).Return(nil).Maybe()
keto := new(devMockKetoService)
keto.On("CheckPermission", mock.Anything, "User:operator-1", "RelyingParty", "client-1", "manage").Return(false, nil)
keto.On("CheckPermission", mock.Anything, "User:operator-1", "Tenant", "tenant-1", "grant_dev_permissions").Return(true, nil).Maybe()
h := &DevHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: &http.Client{Transport: transport},
},
Keto: keto,
RPUserMetadataRepo: repo,
}
app := fiber.New()
app.Use(func(c *fiber.Ctx) error {
c.Locals("user_profile", &domain.UserProfileResponse{ID: "operator-1", Role: domain.RoleUser})
return c.Next()
})
app.Put("/api/v1/dev/clients/:id/users/:userId/metadata", h.UpsertRPUserMetadata)
body, _ := json.Marshal(map[string]any{
"metadata": map[string]any{"approvalLevel": "B"},
})
req := httptest.NewRequest(http.MethodPut, "/api/v1/dev/clients/client-1/users/user-1/metadata", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, _ := app.Test(req, -1)
require.Equal(t, http.StatusForbidden, resp.StatusCode)
repo.AssertNotCalled(t, "Upsert", mock.Anything, mock.Anything)
keto.AssertExpectations(t)
})
t.Run("rp manage allows rp user metadata admin upsert", func(t *testing.T) {
repo := new(devMockRPUserMetadataRepo)
repo.On("Upsert", mock.Anything, mock.MatchedBy(func(row *domain.RPUserMetadata) bool {
return row.ClientID == "client-1" &&
row.UserID == "user-1" &&
row.Metadata["approvalLevel"] == "B"
})).Return(nil).Once()
keto := new(devMockKetoService)
keto.On("CheckPermission", mock.Anything, "User:operator-1", "RelyingParty", "client-1", "manage").Return(true, nil)
h := &DevHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: &http.Client{Transport: transport},
},
Keto: keto,
RPUserMetadataRepo: repo,
}
app := fiber.New()
app.Use(func(c *fiber.Ctx) error {
c.Locals("user_profile", &domain.UserProfileResponse{ID: "operator-1", Role: domain.RoleUser})
return c.Next()
})
app.Put("/api/v1/dev/clients/:id/users/:userId/metadata", h.UpsertRPUserMetadata)
body, _ := json.Marshal(map[string]any{
"metadata": map[string]any{"approvalLevel": "B"},
})
req := httptest.NewRequest(http.MethodPut, "/api/v1/dev/clients/client-1/users/user-1/metadata", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, _ := app.Test(req, -1)
require.Equal(t, http.StatusOK, resp.StatusCode)
repo.AssertExpectations(t)
keto.AssertExpectations(t)
})
}
func TestDevHandler_RPUserMetadataMirrorsToKratosTraits(t *testing.T) {
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.Path == "/clients/client-1" {
@@ -201,6 +295,130 @@ func TestDevHandler_RPUserMetadataMirrorsToKratosTraits(t *testing.T) {
kratos.AssertExpectations(t)
}
func TestDevHandler_SelfUpdateRPUserMetadataHonorsWritePermission(t *testing.T) {
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.Path == "/clients/client-1" {
return httpJSONAny(r, http.StatusOK, map[string]any{
"client_id": "client-1",
"client_name": "Client One",
"metadata": map[string]any{
"tenant_id": "tenant-1",
"id_token_claims": []map[string]any{
{
"namespace": "rp_claims",
"key": "approvalLevel",
"valueType": "text",
"value": "A",
"readPermission": "user_and_admin",
"writePermission": "user_and_admin",
},
{
"namespace": "rp_claims",
"key": "internalRank",
"valueType": "text",
"value": "S",
"readPermission": "admin_only",
"writePermission": "admin_only",
},
},
},
}), nil
}
return httpJSONAny(r, http.StatusNotFound, nil), nil
})
t.Run("rejects admin_only claim", func(t *testing.T) {
repo := new(devMockRPUserMetadataRepo)
repo.On("Upsert", mock.Anything, mock.AnythingOfType("*domain.RPUserMetadata")).Return(nil).Maybe()
h := &DevHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: &http.Client{Transport: transport},
},
RPUserMetadataRepo: repo,
}
app := fiber.New()
app.Use(func(c *fiber.Ctx) error {
c.Locals("user_profile", &domain.UserProfileResponse{ID: "user-1", Role: domain.RoleUser})
return c.Next()
})
app.Put("/api/v1/dev/clients/:id/users/me/metadata", h.SelfUpdateRPUserMetadata)
body, _ := json.Marshal(map[string]any{
"metadata": map[string]any{"internalRank": "A"},
})
req := httptest.NewRequest(http.MethodPut, "/api/v1/dev/clients/client-1/users/me/metadata", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, _ := app.Test(req, -1)
require.Equal(t, http.StatusForbidden, resp.StatusCode)
repo.AssertNotCalled(t, "Upsert", mock.Anything, mock.Anything)
})
t.Run("allows user_and_admin claim for self", func(t *testing.T) {
repo := new(devMockRPUserMetadataRepo)
repo.On("Get", mock.Anything, "client-1", "user-1").Return(&domain.RPUserMetadata{
ClientID: "client-1",
UserID: "user-1",
Metadata: domain.JSONMap{
"internalRank": "S",
"internalRank_permissions": map[string]any{
"readPermission": "admin_only",
"writePermission": "admin_only",
},
},
}, nil).Once()
repo.On("Upsert", mock.Anything, mock.MatchedBy(func(row *domain.RPUserMetadata) bool {
return row.ClientID == "client-1" &&
row.UserID == "user-1" &&
row.Metadata["approvalLevel"] == "B" &&
row.Metadata["internalRank"] == "S"
})).Return(nil).Once()
kratos := new(MockKratosAdmin)
kratos.On("GetIdentity", mock.Anything, "user-1").Return(&service.KratosIdentity{
ID: "user-1",
State: "active",
Traits: map[string]any{
"email": "user@example.com",
},
}, nil).Once()
var capturedTraits map[string]any
kratos.On("UpdateIdentity", mock.Anything, "user-1", mock.Anything, "active").Run(func(args mock.Arguments) {
capturedTraits = args.Get(2).(map[string]any)
}).Return(&service.KratosIdentity{ID: "user-1", State: "active", Traits: map[string]any{}}, nil).Once()
h := &DevHandler{
Hydra: &service.HydraAdminService{
AdminURL: "http://hydra.test",
HTTPClient: &http.Client{Transport: transport},
},
KratosAdmin: kratos,
IdentityWriter: service.NewIdentityWriteService(kratos, nil),
RPUserMetadataRepo: repo,
}
app := fiber.New()
app.Use(func(c *fiber.Ctx) error {
c.Locals("user_profile", &domain.UserProfileResponse{ID: "user-1", Role: domain.RoleUser})
return c.Next()
})
app.Put("/api/v1/dev/clients/:id/users/me/metadata", h.SelfUpdateRPUserMetadata)
body, _ := json.Marshal(map[string]any{
"metadata": map[string]any{"approvalLevel": "B"},
})
req := httptest.NewRequest(http.MethodPut, "/api/v1/dev/clients/client-1/users/me/metadata", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, _ := app.Test(req, -1)
require.Equal(t, http.StatusOK, resp.StatusCode)
rpClaims := capturedTraits["rp_custom_claims"].(map[string]any)
clientClaims := rpClaims["client-1"].(domain.JSONMap)
require.Equal(t, "B", clientClaims["approvalLevel"])
require.Equal(t, "S", clientClaims["internalRank"])
repo.AssertExpectations(t)
kratos.AssertExpectations(t)
})
}
func TestDevHandler_RPUserMetadataRejectsUndefinedClaimKey(t *testing.T) {
transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
if r.URL.Path == "/clients/client-1" {