You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/tests/multi_tenant_test.go

255 lines
6.7 KiB
Go

package tests
import (
"testing"
)
// TestMultiTenantIsolation 测试多租户数据隔离
func TestMultiTenantIsolation(t *testing.T) {
ensureUsers(t)
var org1ID, org2ID string
// User1 创建组织1
t.Run("User1 creates Organization 1", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "tenant_test_org1",
"name": "Tenant Test Org 1",
}, User1Token)
if resp.Code == 200 {
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
org1ID = data.ID
} else {
// 可能已经存在,尝试获取
resp = doRequest(t, "GET", "/api/orgs", nil, User1Token)
assertStatus(t, resp, 200)
}
})
// User2 创建组织2
t.Run("User2 creates Organization 2", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "tenant_test_org2",
"name": "Tenant Test Org 2",
}, User2Token)
if resp.Code == 200 {
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
org2ID = data.ID
}
})
if org1ID == "" || org2ID == "" {
t.Skip("Failed to create test orgs")
}
// 测试User1 不能访问 User2 的组织
t.Run("User1 cannot access User2's organization", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+org2ID, nil, User1Token)
if resp.Code == 200 {
t.Errorf("User1 should not be able to access User2's org, got 200")
}
})
// 测试User2 不能访问 User1 的组织
t.Run("User2 cannot access User1's organization", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+org1ID, nil, User2Token)
if resp.Code == 200 {
t.Errorf("User2 should not be able to access User1's org, got 200")
}
})
// 测试User1 添加 User2 到组织1后User2 可以访问
t.Run("User1 adds User2 to Org1", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs/"+org1ID+"/members", map[string]string{
"user_id": User2ID,
"role": "member",
}, User1Token)
assertStatus(t, resp, 200)
})
// User2 现在应该能访问组织1
t.Run("User2 can now access Org1 as member", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+org1ID, nil, User2Token)
assertStatus(t, resp, 200)
})
// 但 User2 不应该能修改组织1只有 owner 可以)
t.Run("User2 cannot update Org1 as member", func(t *testing.T) {
resp := doRequest(t, "PATCH", "/api/orgs/"+org1ID, map[string]string{
"name": "Hacked by User2",
}, User2Token)
// 应该失败,因为需要 org:update 权限
if resp.Code == 200 {
t.Errorf("User2 should not be able to update Org1, got 200")
}
})
}
// TestOrgMemberPermission 测试组织成员权限
func TestOrgMemberPermission(t *testing.T) {
ensureUsers(t)
var orgID string
// User1 创建组织
t.Run("User1 creates org", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "member_perm_test",
"name": "Member Permission Test",
}, User1Token)
if resp.Code == 200 {
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
orgID = data.ID
}
})
if orgID == "" {
t.Skip("Failed to create org")
}
// User1 添加 User2 为成员
t.Run("Add User2 as member", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs/"+orgID+"/members", map[string]string{
"user_id": User2ID,
"role": "member",
}, User1Token)
assertStatus(t, resp, 200)
})
// User2 可以查看成员列表
t.Run("Member can view member list", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+orgID+"/members", nil, User2Token)
assertStatus(t, resp, 200)
})
// User2 不能添加新成员(需要 org:update 权限)
t.Run("Member cannot add new members", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs/"+orgID+"/members", map[string]string{
"user_id": AdminID,
"role": "member",
}, User2Token)
if resp.Code == 200 {
t.Errorf("Member should not be able to add new members, got 200")
}
})
// 清理
t.Run("Cleanup", func(t *testing.T) {
resp := doRequest(t, "DELETE", "/api/orgs/"+orgID, nil, User1Token)
assertStatus(t, resp, 200)
})
}
// TestCrossOrgDataLeak 测试跨组织数据泄漏
func TestCrossOrgDataLeak(t *testing.T) {
ensureUsers(t)
var orgID string
// User1 创建组织
t.Run("User1 creates org", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "data_leak_test",
"name": "Data Leak Test",
}, User1Token)
if resp.Code == 200 {
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
orgID = data.ID
}
})
if orgID == "" {
t.Skip("Failed to create org")
}
// User1 添加 User2 为成员
t.Run("Add User2 as member", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs/"+orgID+"/members", map[string]string{
"user_id": User2ID,
"role": "member",
}, User1Token)
assertStatus(t, resp, 200)
})
// User2 获取组织详情
t.Run("User2 gets org details", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+orgID, nil, User2Token)
assertStatus(t, resp, 200)
})
// User2 不应该能看到其他组织的成员
// 注意:这里假设 orgID+1 是另一个不存在的组织ID
t.Run("User2 cannot access non-existent org", func(t *testing.T) {
fakeOrgID := "non-existent-org-id-12345"
resp := doRequest(t, "GET", "/api/orgs/"+fakeOrgID, nil, User2Token)
if resp.Code == 200 {
t.Errorf("Should not be able to access non-existent org, got 200")
}
})
// 清理
t.Run("Cleanup", func(t *testing.T) {
resp := doRequest(t, "DELETE", "/api/orgs/"+orgID, nil, User1Token)
assertStatus(t, resp, 200)
})
}
// TestOrgIDHeaderManipulation 测试 X-Org-ID 头部操作
func TestOrgIDHeaderManipulation(t *testing.T) {
ensureUsers(t)
var orgID string
// User1 创建组织
t.Run("User1 creates org", func(t *testing.T) {
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
"code": "header_test_org",
"name": "Header Test Org",
}, User1Token)
if resp.Code == 200 {
var data struct {
ID string `json:"id"`
}
decodeResponse(t, resp, &data)
orgID = data.ID
}
})
if orgID == "" {
t.Skip("Failed to create org")
}
// 测试User2 尝试通过伪造 X-Org-ID 访问组织
// 注意:这需要修改 doRequest 函数来支持自定义 header
// 目前测试表明 User2 无法访问
t.Run("User2 cannot access org without membership", func(t *testing.T) {
resp := doRequest(t, "GET", "/api/orgs/"+orgID, nil, User2Token)
if resp.Code == 200 {
t.Errorf("User2 should not be able to access org without membership, got 200")
}
})
// 清理
t.Run("Cleanup", func(t *testing.T) {
resp := doRequest(t, "DELETE", "/api/orgs/"+orgID, nil, User1Token)
assertStatus(t, resp, 200)
})
}