forked from baron/baron-sso
270 lines
8.0 KiB
Go
270 lines
8.0 KiB
Go
package handler
|
|
|
|
import (
|
|
"baron-sso-backend/internal/domain"
|
|
"baron-sso-backend/internal/repository"
|
|
"baron-sso-backend/internal/service"
|
|
"baron-sso-backend/internal/testsupport"
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
)
|
|
|
|
func TestTenantHandler_Relations(t *testing.T) {
|
|
if !testsupport.DockerAvailable() {
|
|
t.Skip("Docker provider is unavailable in this environment")
|
|
}
|
|
|
|
db := newTenantHandlerSeedDeleteDB(t)
|
|
if err := db.AutoMigrate(&domain.TenantDomain{}, &domain.KetoOutbox{}); err != nil {
|
|
t.Fatalf("failed to migrate tenant domains or outbox: %v", err)
|
|
}
|
|
|
|
// Create a test tenant in DB with a valid UUID
|
|
tenantID := "00000000-0000-0000-0000-000000000030"
|
|
tenant := domain.Tenant{
|
|
ID: tenantID,
|
|
Name: "Relation Test Tenant",
|
|
Slug: "relation-test-tenant",
|
|
Type: domain.TenantTypeCompany,
|
|
Status: domain.TenantStatusActive,
|
|
}
|
|
if err := db.Create(&tenant).Error; err != nil {
|
|
t.Fatalf("failed to create tenant: %v", err)
|
|
}
|
|
|
|
mockSvc := new(MockTenantService)
|
|
mockKeto := new(devMockKetoService)
|
|
realOutbox := repository.NewKetoOutboxRepository(db)
|
|
|
|
h := &TenantHandler{
|
|
DB: db,
|
|
Service: mockSvc,
|
|
Keto: mockKeto,
|
|
KetoOutbox: realOutbox,
|
|
}
|
|
|
|
userID := "user-relation-1"
|
|
|
|
t.Run("ListRelations - Returns correct relations aggregated by user", func(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Get("/tenants/:id/relations", h.ListRelations)
|
|
|
|
mockKeto.On("ListRelations", mock.Anything, "Tenant", tenantID, "", "").Return([]service.RelationTuple{
|
|
{
|
|
Namespace: "Tenant",
|
|
Object: tenantID,
|
|
Relation: "schema_managers",
|
|
SubjectID: "User:" + userID,
|
|
},
|
|
{
|
|
Namespace: "Tenant",
|
|
Object: tenantID,
|
|
Relation: "profile_viewers",
|
|
SubjectID: "User:" + userID,
|
|
},
|
|
{
|
|
Namespace: "Tenant",
|
|
Object: tenantID,
|
|
Relation: "unrelated_relation", // Should be filtered out
|
|
SubjectID: "User:" + userID,
|
|
},
|
|
}, nil).Once()
|
|
|
|
req := httptest.NewRequest("GET", "/tenants/"+tenantID+"/relations", nil)
|
|
resp, err := app.Test(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
var got struct {
|
|
Items []struct {
|
|
UserID string `json:"userId"`
|
|
Name string `json:"name"`
|
|
Email string `json:"email"`
|
|
Relations []string `json:"relations"`
|
|
} `json:"items"`
|
|
}
|
|
err = json.NewDecoder(resp.Body).Decode(&got)
|
|
if err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
assert.Len(t, got.Items, 1)
|
|
assert.Equal(t, userID, got.Items[0].UserID)
|
|
assert.Contains(t, got.Items[0].Relations, "schema_managers")
|
|
assert.Contains(t, got.Items[0].Relations, "profile_viewers")
|
|
assert.NotContains(t, got.Items[0].Relations, "unrelated_relation")
|
|
mockKeto.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("AddRelation - Inserts into KetoOutbox DB table", func(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Post("/tenants/:id/relations", h.AddRelation)
|
|
|
|
mockKeto.On("ListRelations", mock.Anything, "Tenant", tenantID, "schema_managers", "User:"+userID).Return([]service.RelationTuple{}, nil).Once()
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"userId": userID,
|
|
"relation": "schema_managers",
|
|
})
|
|
req := httptest.NewRequest("POST", "/tenants/"+tenantID+"/relations", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := app.Test(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
// Verify row was written to the keto_outboxes DB table
|
|
var outboxEntries []domain.KetoOutbox
|
|
if err := db.Where("object = ? AND relation = ? AND action = ?", tenantID, "schema_managers", domain.KetoOutboxActionCreate).Find(&outboxEntries).Error; err != nil {
|
|
t.Fatalf("failed to query outbox: %v", err)
|
|
}
|
|
|
|
assert.Len(t, outboxEntries, 1)
|
|
assert.Equal(t, "Tenant", outboxEntries[0].Namespace)
|
|
assert.Equal(t, "User:"+userID, outboxEntries[0].Subject)
|
|
mockKeto.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("RemoveRelation - Inserts delete action into KetoOutbox DB table", func(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Delete("/tenants/:id/relations", h.RemoveRelation)
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"userId": userID,
|
|
"relation": "schema_managers",
|
|
})
|
|
req := httptest.NewRequest("DELETE", "/tenants/"+tenantID+"/relations", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := app.Test(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
// Verify delete action row was written to the keto_outboxes DB table
|
|
var outboxEntries []domain.KetoOutbox
|
|
if err := db.Where("object = ? AND relation = ? AND action = ?", tenantID, "schema_managers", domain.KetoOutboxActionDelete).Find(&outboxEntries).Error; err != nil {
|
|
t.Fatalf("failed to query outbox: %v", err)
|
|
}
|
|
|
|
assert.Len(t, outboxEntries, 1)
|
|
assert.Equal(t, "Tenant", outboxEntries[0].Namespace)
|
|
assert.Equal(t, "User:"+userID, outboxEntries[0].Subject)
|
|
})
|
|
}
|
|
|
|
func TestTenantHandler_SystemRelations(t *testing.T) {
|
|
if !testsupport.DockerAvailable() {
|
|
t.Skip("Docker provider is unavailable in this environment")
|
|
}
|
|
|
|
db := newTenantHandlerSeedDeleteDB(t)
|
|
if err := db.AutoMigrate(&domain.KetoOutbox{}); err != nil {
|
|
t.Fatalf("failed to migrate outbox: %v", err)
|
|
}
|
|
|
|
mockSvc := new(MockTenantService)
|
|
mockKeto := new(devMockKetoService)
|
|
realOutbox := repository.NewKetoOutboxRepository(db)
|
|
|
|
h := &TenantHandler{
|
|
DB: db,
|
|
Service: mockSvc,
|
|
Keto: mockKeto,
|
|
KetoOutbox: realOutbox,
|
|
}
|
|
|
|
userID := "user-system-1"
|
|
|
|
t.Run("ListSystemRelations - Returns correct system relations", func(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Get("/system/relations", h.ListSystemRelations)
|
|
|
|
mockKeto.On("ListRelations", mock.Anything, "System", "system", "", "").Return([]service.RelationTuple{
|
|
{
|
|
Namespace: "System",
|
|
Object: "system",
|
|
Relation: "ory_ssot_viewers",
|
|
SubjectID: "User:" + userID,
|
|
},
|
|
{
|
|
Namespace: "System",
|
|
Object: "system",
|
|
Relation: "audit_logs_viewers",
|
|
SubjectID: "User:" + userID,
|
|
},
|
|
}, nil).Once()
|
|
|
|
req := httptest.NewRequest("GET", "/system/relations", nil)
|
|
resp, err := app.Test(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
var got struct {
|
|
Items []struct {
|
|
UserID string `json:"userId"`
|
|
Relations []string `json:"relations"`
|
|
} `json:"items"`
|
|
}
|
|
err = json.NewDecoder(resp.Body).Decode(&got)
|
|
if err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
assert.Len(t, got.Items, 1)
|
|
assert.Equal(t, userID, got.Items[0].UserID)
|
|
assert.Contains(t, got.Items[0].Relations, "ory_ssot_viewers")
|
|
assert.Contains(t, got.Items[0].Relations, "audit_logs_viewers")
|
|
mockKeto.AssertExpectations(t)
|
|
})
|
|
|
|
t.Run("AddSystemRelation - Inserts into KetoOutbox DB table with System namespace", func(t *testing.T) {
|
|
app := fiber.New()
|
|
app.Post("/system/relations", h.AddSystemRelation)
|
|
|
|
mockKeto.On("ListRelations", mock.Anything, "System", "system", "ory_ssot_viewers", "User:"+userID).Return([]service.RelationTuple{}, nil).Once()
|
|
|
|
body, _ := json.Marshal(map[string]string{
|
|
"userId": userID,
|
|
"relation": "ory_ssot_viewers",
|
|
})
|
|
req := httptest.NewRequest("POST", "/system/relations", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := app.Test(req)
|
|
if err != nil {
|
|
t.Fatalf("request failed: %v", err)
|
|
}
|
|
|
|
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
|
var outboxEntries []domain.KetoOutbox
|
|
if err := db.Where("object = ? AND relation = ? AND action = ?", "system", "ory_ssot_viewers", domain.KetoOutboxActionCreate).Find(&outboxEntries).Error; err != nil {
|
|
t.Fatalf("failed to query outbox: %v", err)
|
|
}
|
|
|
|
assert.Len(t, outboxEntries, 1)
|
|
assert.Equal(t, "System", outboxEntries[0].Namespace)
|
|
assert.Equal(t, "User:"+userID, outboxEntries[0].Subject)
|
|
mockKeto.AssertExpectations(t)
|
|
})
|
|
}
|