forked from baron/baron-sso
124 lines
3.7 KiB
Go
124 lines
3.7 KiB
Go
package handler
|
|
|
|
import (
|
|
"baron-sso-backend/internal/domain"
|
|
"baron-sso-backend/internal/middleware"
|
|
"baron-sso-backend/internal/service"
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
// Reusing MockKetoService from previous step or defining here if needed
|
|
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 {
|
|
return m.Called(ctx, namespace, object, relation, subject).Error(0)
|
|
}
|
|
|
|
func (m *MockKetoService) DeleteRelation(ctx context.Context, namespace, object, relation, subject string) error {
|
|
return m.Called(ctx, namespace, object, relation, subject).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)
|
|
return args.Get(0).([]service.RelationTuple), args.Error(1)
|
|
}
|
|
|
|
func (m *MockKetoService) ListObjects(ctx context.Context, namespace, relation, subject string) ([]string, error) {
|
|
args := m.Called(ctx, namespace, relation, subject)
|
|
return args.Get(0).([]string), args.Error(1)
|
|
}
|
|
|
|
// MockAuthHandler implements middleware.AuthProfileProvider
|
|
type MockAuthHandler struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *MockAuthHandler) GetEnrichedProfile(c *fiber.Ctx) (*domain.UserProfileResponse, error) {
|
|
args := m.Called(c)
|
|
return args.Get(0).(*domain.UserProfileResponse), args.Error(1)
|
|
}
|
|
|
|
func TestRequireKetoPermission_Tenant_AuditContext(t *testing.T) {
|
|
app := fiber.New()
|
|
mockKeto := new(MockKetoService)
|
|
mockAuth := new(MockAuthHandler)
|
|
|
|
config := middleware.RBACConfig{
|
|
AuthHandler: mockAuth,
|
|
KetoService: mockKeto,
|
|
}
|
|
|
|
userID := "user-1"
|
|
tenantID := "tenant-abc"
|
|
|
|
// Mock user profile
|
|
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(&domain.UserProfileResponse{
|
|
ID: userID,
|
|
Role: domain.RoleTenantAdmin,
|
|
}, nil)
|
|
|
|
// Mock Keto: Allow access
|
|
mockKeto.On("CheckPermission", mock.Anything, userID, "Tenant", tenantID, "manage").Return(true, nil)
|
|
|
|
// Route with middleware
|
|
app.Get("/test/tenants/:id", middleware.RequireKetoPermission(config, "Tenant", "manage"), func(c *fiber.Ctx) error {
|
|
// Verify that tenant_id was injected into Locals for audit log
|
|
assert.Equal(t, tenantID, c.Locals("tenant_id"))
|
|
return c.SendStatus(fiber.StatusOK)
|
|
})
|
|
|
|
// Execute
|
|
req := httptest.NewRequest("GET", "/test/tenants/"+tenantID, nil)
|
|
resp, _ := app.Test(req)
|
|
|
|
// Verify
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
mockKeto.AssertExpectations(t)
|
|
mockAuth.AssertExpectations(t)
|
|
}
|
|
|
|
func TestRequireKetoPermission_Deny(t *testing.T) {
|
|
app := fiber.New()
|
|
mockKeto := new(MockKetoService)
|
|
mockAuth := new(MockAuthHandler)
|
|
|
|
config := middleware.RBACConfig{
|
|
AuthHandler: mockAuth,
|
|
KetoService: mockKeto,
|
|
}
|
|
|
|
userID := "user-bad"
|
|
tenantID := "tenant-secret"
|
|
|
|
mockAuth.On("GetEnrichedProfile", mock.Anything).Return(&domain.UserProfileResponse{
|
|
ID: userID,
|
|
Role: domain.RoleUser,
|
|
}, nil)
|
|
|
|
// Mock Keto: Deny access
|
|
mockKeto.On("CheckPermission", mock.Anything, userID, "Tenant", tenantID, "view").Return(false, nil)
|
|
|
|
app.Get("/test/tenants/:id", middleware.RequireKetoPermission(config, "Tenant", "view"), func(c *fiber.Ctx) error {
|
|
return c.SendStatus(fiber.StatusOK)
|
|
})
|
|
|
|
req := httptest.NewRequest("GET", "/test/tenants/"+tenantID, nil)
|
|
resp, _ := app.Test(req)
|
|
|
|
assert.Equal(t, http.StatusForbidden, resp.StatusCode)
|
|
}
|