1
0
forked from baron/baron-sso
Files
baron-sso/backend/internal/middleware/rbac_test.go
2026-02-05 17:01:47 +09:00

194 lines
5.2 KiB
Go

package middleware
import (
"baron-sso-backend/internal/domain"
"baron-sso-backend/internal/service"
"context"
"errors"
"net/http/httptest"
"testing"
"github.com/gofiber/fiber/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// MockAuthProvider is a mock for AuthProvider interface
type MockAuthProvider struct {
mock.Mock
}
func (m *MockAuthProvider) GetEnrichedProfile(c *fiber.Ctx) (*domain.UserProfileResponse, error) {
args := m.Called(c)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*domain.UserProfileResponse), args.Error(1)
}
// MockKetoService is a mock for KetoService interface
type MockKetoService struct {
mock.Mock
}
func (m *MockKetoService) CheckPermission(ctx context.Context, subject, namespace, object, relation string) (bool, error) {
args := m.Called(ctx, subject, namespace, object, relation)
return args.Bool(0), args.Error(1)
}
func (m *MockKetoService) CreateRelation(ctx context.Context, namespace, object, relation, subject string) error {
args := m.Called(ctx, namespace, object, relation, subject)
return args.Error(0)
}
func (m *MockKetoService) DeleteRelation(ctx context.Context, namespace, object, relation, subject string) error {
args := m.Called(ctx, namespace, object, relation, subject)
return args.Error(0)
}
func (m *MockKetoService) ListRelations(ctx context.Context, namespace, object, relation, subject string) ([]service.RelationTuple, error) {
args := m.Called(ctx, namespace, object, relation, subject)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]service.RelationTuple), args.Error(1)
}
// Fixed MockKetoService to match service.KetoService exactly if possible.
// Wait, middleware/rbac.go imports baron-sso-backend/internal/service.
// So I should use service.RelationTuple.
func TestRequireRole_Success(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.SendString("ok")
})
req := httptest.NewRequest("GET", "/test", nil)
resp, _ := app.Test(req)
assert.Equal(t, 200, resp.StatusCode)
}
func TestRequireRole_Forbidden(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: "user",
}, nil)
app.Get("/test", RequireRole(config), func(c *fiber.Ctx) error {
return c.SendString("ok")
})
req := httptest.NewRequest("GET", "/test", nil)
resp, _ := app.Test(req)
assert.Equal(t, 403, resp.StatusCode)
}
func TestRequireKetoPermission_Success(t *testing.T) {
app := fiber.New()
mockAuth := new(MockAuthProvider)
mockKeto := new(MockKetoService)
config := RBACConfig{
AuthHandler: mockAuth,
KetoService: mockKeto,
}
profile := &domain.UserProfileResponse{ID: "user1", Role: "user"}
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(profile, nil)
mockKeto.On("CheckPermission", mock.Anything, "user1", "tenants", "tenant1", "read").Return(true, nil)
app.Get("/tenants/:id", RequireKetoPermission(config, "tenants", "read"), func(c *fiber.Ctx) error {
return c.SendString("ok")
})
req := httptest.NewRequest("GET", "/tenants/tenant1", nil)
resp, _ := app.Test(req)
assert.Equal(t, 200, resp.StatusCode)
}
func TestRequireTenantMatch_SuperAdmin(t *testing.T) {
app := fiber.New()
mockAuth := new(MockAuthProvider)
config := RBACConfig{
AuthHandler: mockAuth,
}
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(&domain.UserProfileResponse{
ID: "admin1",
Role: domain.RoleSuperAdmin,
}, nil)
app.Get("/tenants/:tenantId/data", RequireTenantMatch(config), func(c *fiber.Ctx) error {
return c.SendString("ok")
})
req := httptest.NewRequest("GET", "/tenants/any-tenant/data", nil)
resp, _ := app.Test(req)
assert.Equal(t, 200, resp.StatusCode)
}
func TestRequireTenantMatch_Forbidden(t *testing.T) {
app := fiber.New()
mockAuth := new(MockAuthProvider)
config := RBACConfig{
AuthHandler: mockAuth,
}
tenant1 := "tenant1"
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(&domain.UserProfileResponse{
ID: "user1",
Role: domain.RoleTenantAdmin,
TenantID: &tenant1,
}, nil)
app.Get("/tenants/:tenantId/data", RequireTenantMatch(config), func(c *fiber.Ctx) error {
return c.SendString("ok")
})
req := httptest.NewRequest("GET", "/tenants/tenant2/data", nil)
resp, _ := app.Test(req)
assert.Equal(t, 403, resp.StatusCode)
}
func TestRequireRole_Unauthorized(t *testing.T) {
app := fiber.New()
mockAuth := new(MockAuthProvider)
config := RBACConfig{
AuthHandler: mockAuth,
}
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(nil, errors.New("unauthorized"))
app.Get("/test", RequireRole(config), func(c *fiber.Ctx) error {
return c.SendString("ok")
})
req := httptest.NewRequest("GET", "/test", nil)
resp, _ := app.Test(req)
assert.Equal(t, 401, resp.StatusCode)
}