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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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)
})
}