1
0
forked from baron/baron-sso

4단계 역할 정규화 및 dev 권한 스코프 검증 강화

This commit is contained in:
2026-03-04 13:16:34 +09:00
parent 0f8b19a9b1
commit eac16cfcd9
11 changed files with 521 additions and 177 deletions

View File

@@ -266,8 +266,6 @@ func TestGetStats_Success(t *testing.T) {
}
mockKeto := new(devMockKetoService)
// mockKeto setup to allow stats view
mockKeto.On("CheckPermission", mock.Anything, "u1", "System", "AppManager", "member").Return(true, nil)
h := &DevHandler{
Hydra: &service.HydraAdminService{
@@ -281,7 +279,7 @@ func TestGetStats_Success(t *testing.T) {
tenantID := "t1"
app.Use(func(c *fiber.Ctx) error {
c.Locals("user_profile", &domain.UserProfileResponse{
ID: "u1", Role: domain.RoleUser, TenantID: &tenantID,
ID: "u1", Role: domain.RoleTenantAdmin, TenantID: &tenantID,
})
return c.Next()
})
@@ -297,7 +295,7 @@ func TestGetStats_Success(t *testing.T) {
assert.Equal(t, int64(2), res.TotalClients)
assert.Equal(t, int64(7), res.AuthFailures)
assert.Equal(t, int64(3), res.ActiveSessions)
mockKeto.AssertExpectations(t)
mockKeto.AssertNotCalled(t, "CheckPermission", mock.Anything, mock.Anything, "System", "AppManager", "member")
}
func TestDevHandler_NoAuditNoAction(t *testing.T) {
@@ -323,3 +321,78 @@ func TestDevHandler_NoAuditNoAction(t *testing.T) {
assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
})
}
func TestListAuditLogs_TenantMemberForbidden(t *testing.T) {
h := &DevHandler{
Hydra: &service.HydraAdminService{AdminURL: "http://hydra.test"},
AuditRepo: &mockAuditRepo{},
Keto: new(devMockKetoService),
}
app := fiber.New()
tenantID := "tenant-a"
app.Use(func(c *fiber.Ctx) error {
c.Locals("user_profile", &domain.UserProfileResponse{
ID: "u-member",
Role: domain.RoleUser,
TenantID: &tenantID,
})
return c.Next()
})
app.Get("/api/v1/dev/audit-logs", h.ListAuditLogs)
req := httptest.NewRequest(http.MethodGet, "/api/v1/dev/audit-logs", nil)
resp, _ := app.Test(req, -1)
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
}
func TestListAuditLogs_RPAdminScope(t *testing.T) {
auditRepo := &mockAuditRepo{
logs: []domain.AuditLog{
{
EventID: "evt-1",
EventType: "POST /api/v1/dev/clients",
Status: "success",
Timestamp: time.Now().UTC(),
Details: `{"target_id":"client-allowed","tenant_id":"tenant-a","action":"CREATE_CLIENT"}`,
},
{
EventID: "evt-2",
EventType: "POST /api/v1/dev/clients",
Status: "success",
Timestamp: time.Now().UTC().Add(-time.Minute),
Details: `{"target_id":"client-other","tenant_id":"tenant-a","action":"CREATE_CLIENT"}`,
},
},
}
h := &DevHandler{
Hydra: &service.HydraAdminService{AdminURL: "http://hydra.test"},
AuditRepo: auditRepo,
Keto: new(devMockKetoService),
}
app := fiber.New()
tenantID := "tenant-a"
app.Use(func(c *fiber.Ctx) error {
c.Locals("user_profile", &domain.UserProfileResponse{
ID: "u-rp-admin",
Role: domain.RoleRPAdmin,
TenantID: &tenantID,
Metadata: map[string]any{
"managed_client_ids": []any{"client-allowed"},
},
})
return c.Next()
})
app.Get("/api/v1/dev/audit-logs", h.ListAuditLogs)
req := httptest.NewRequest(http.MethodGet, "/api/v1/dev/audit-logs?limit=50", nil)
resp, _ := app.Test(req, -1)
assert.Equal(t, http.StatusOK, resp.StatusCode)
var result devAuditListResponse
_ = json.NewDecoder(resp.Body).Decode(&result)
assert.Len(t, result.Items, 1)
assert.Equal(t, "evt-1", result.Items[0].EventID)
}