forked from baron/baron-sso
감사로그 수행자 표시
This commit is contained in:
@@ -20,6 +20,16 @@ type AuthProfileProvider interface {
|
||||
GetEnrichedProfile(c *fiber.Ctx) (*domain.UserProfileResponse, error)
|
||||
}
|
||||
|
||||
func setAuditUserContext(c *fiber.Ctx, profile *domain.UserProfileResponse) {
|
||||
if profile == nil || profile.ID == "" {
|
||||
return
|
||||
}
|
||||
if existingUserID, _ := c.Locals("user_id").(string); existingUserID != "" {
|
||||
return
|
||||
}
|
||||
c.Locals("user_id", profile.ID)
|
||||
}
|
||||
|
||||
// RequireKetoPermission enforces permissions using Ory Keto (ReBAC)
|
||||
func RequireKetoPermission(config RBACConfig, namespace, relation string) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
@@ -30,6 +40,7 @@ func RequireKetoPermission(config RBACConfig, namespace, relation string) fiber.
|
||||
|
||||
// Store profile in locals for further use in handlers
|
||||
c.Locals("user_profile", profile)
|
||||
setAuditUserContext(c, profile)
|
||||
|
||||
role := domain.NormalizeRole(profile.Role)
|
||||
|
||||
@@ -92,6 +103,7 @@ func RequireRole(config RBACConfig) fiber.Handler {
|
||||
|
||||
// Store profile in locals for further use in handlers
|
||||
c.Locals("user_profile", profile)
|
||||
setAuditUserContext(c, profile)
|
||||
|
||||
userRole := domain.NormalizeRole(profile.Role)
|
||||
|
||||
@@ -139,6 +151,7 @@ func RequireTenantMatch(config RBACConfig) fiber.Handler {
|
||||
|
||||
// Store profile in locals for further use in handlers
|
||||
c.Locals("user_profile", profile)
|
||||
setAuditUserContext(c, profile)
|
||||
|
||||
userRole := domain.NormalizeRole(profile.Role)
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"baron-sso-backend/internal/service"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
@@ -89,6 +91,68 @@ func TestRequireRole_Success(t *testing.T) {
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestRequireRole_SetsUserIDForAuditContext(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockAuth := new(MockAuthProvider)
|
||||
config := RBACConfig{
|
||||
AllowedRoles: []string{"admin"},
|
||||
AuthHandler: mockAuth,
|
||||
}
|
||||
|
||||
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(&domain.UserProfileResponse{
|
||||
ID: "user1",
|
||||
Role: "admin",
|
||||
}, nil)
|
||||
|
||||
app.Get("/test", RequireRole(config), func(c *fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{
|
||||
"user_id": c.Locals("user_id"),
|
||||
})
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/test", nil)
|
||||
resp, _ := app.Test(req)
|
||||
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
var body map[string]string
|
||||
assert.NoError(t, readJSON(resp, &body))
|
||||
assert.Equal(t, "user1", body["user_id"])
|
||||
}
|
||||
|
||||
func TestRequireRole_PreservesExistingUserID(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockAuth := new(MockAuthProvider)
|
||||
config := RBACConfig{
|
||||
AllowedRoles: []string{"admin"},
|
||||
AuthHandler: mockAuth,
|
||||
}
|
||||
|
||||
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(&domain.UserProfileResponse{
|
||||
ID: "profile-user",
|
||||
Role: "admin",
|
||||
}, nil)
|
||||
|
||||
app.Use(func(c *fiber.Ctx) error {
|
||||
c.Locals("user_id", "existing-user")
|
||||
return c.Next()
|
||||
})
|
||||
app.Get("/test", RequireRole(config), func(c *fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{
|
||||
"user_id": c.Locals("user_id"),
|
||||
})
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/test", nil)
|
||||
resp, _ := app.Test(req)
|
||||
|
||||
assert.Equal(t, 200, resp.StatusCode)
|
||||
|
||||
var body map[string]string
|
||||
assert.NoError(t, readJSON(resp, &body))
|
||||
assert.Equal(t, "existing-user", body["user_id"])
|
||||
}
|
||||
|
||||
func TestRequireRole_Forbidden(t *testing.T) {
|
||||
app := fiber.New()
|
||||
mockAuth := new(MockAuthProvider)
|
||||
@@ -199,3 +263,8 @@ func TestRequireRole_Unauthorized(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 401, resp.StatusCode)
|
||||
}
|
||||
|
||||
func readJSON(resp *http.Response, target any) error {
|
||||
defer resp.Body.Close()
|
||||
return json.NewDecoder(resp.Body).Decode(target)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user