mirror of https://github.com/veypi/OneAuth.git
test: Add integration tests for org, role and oauth client
- Add OAuth client CRUD and access control tests
- Add organization CRUD, tree and access control tests
- Add role CRUD, access control and system role protection tests
- Remove user:read permission from default user role
master
parent
63792b449f
commit
7f7591cf6d
@ -0,0 +1,120 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// OAuthClientResp OAuth 客户端响应
|
||||
type OAuthClientResp struct {
|
||||
ID string `json:"id"`
|
||||
ClientID string `json:"client_id"`
|
||||
ClientSecret string `json:"client_secret,omitempty"`
|
||||
Name string `json:"name"`
|
||||
RedirectURIs string `json:"redirect_uris"`
|
||||
AllowedScopes string `json:"allowed_scopes"`
|
||||
}
|
||||
|
||||
// Test OAuth Client CRUD
|
||||
func TestOAuthClientCRUD(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
var clientID string // This is ClientID (string), not ID (UUID)
|
||||
|
||||
// Test 1: List OAuth Clients
|
||||
t.Run("List OAuth Clients", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/oauth/clients", nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
Items []OAuthClientResp `json:"items"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
t.Logf("Total OAuth clients: %d", len(data.Items))
|
||||
})
|
||||
|
||||
// Test 2: Create OAuth Client
|
||||
t.Run("Create OAuth Client", func(t *testing.T) {
|
||||
resp := doRequest(t, "POST", "/api/oauth/clients", map[string]interface{}{
|
||||
"name": "Test OAuth Client",
|
||||
"redirect_uris": []string{"https://example.com/callback"},
|
||||
"allowed_scopes": "openid profile email",
|
||||
}, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data OAuthClientResp
|
||||
decodeResponse(t, resp, &data)
|
||||
clientID = data.ClientID // Use ClientID, not ID
|
||||
t.Logf("Created OAuth client: %s (ID: %s)", clientID, data.ID)
|
||||
})
|
||||
|
||||
if clientID == "" {
|
||||
t.Fatal("Failed to create OAuth client, skipping remaining tests")
|
||||
}
|
||||
|
||||
// Test 3: Get OAuth Client Details
|
||||
t.Run("Get OAuth Client Details", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data OAuthClientResp
|
||||
decodeResponse(t, resp, &data)
|
||||
|
||||
if data.Name != "Test OAuth Client" {
|
||||
t.Errorf("Expected name 'Test OAuth Client', got '%s'", data.Name)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 4: Update OAuth Client
|
||||
t.Run("Update OAuth Client", func(t *testing.T) {
|
||||
resp := doRequest(t, "PATCH", "/api/oauth/clients/"+clientID, map[string]string{
|
||||
"name": "Updated OAuth Client",
|
||||
}, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Verify update
|
||||
resp = doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data OAuthClientResp
|
||||
decodeResponse(t, resp, &data)
|
||||
|
||||
if data.Name != "Updated OAuth Client" {
|
||||
t.Errorf("Expected name 'Updated OAuth Client', got '%s'", data.Name)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 5: Delete OAuth Client
|
||||
t.Run("Delete OAuth Client", func(t *testing.T) {
|
||||
resp := doRequest(t, "DELETE", "/api/oauth/clients/"+clientID, nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Verify deletion
|
||||
resp = doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, AdminToken)
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected client to be deleted, but got 200")
|
||||
} else {
|
||||
t.Logf("Client deleted successfully, got code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Test regular user OAuth client access
|
||||
func TestOAuthClientAccessControl(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
// Regular user should be able to list OAuth clients (oauth-client:read)
|
||||
t.Run("Regular User List Clients", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/oauth/clients", nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
})
|
||||
|
||||
// Regular user should be able to create OAuth clients (oauth-client:create)
|
||||
t.Run("Regular User Create Client", func(t *testing.T) {
|
||||
resp := doRequest(t, "POST", "/api/oauth/clients", map[string]interface{}{
|
||||
"name": "User OAuth Client",
|
||||
"redirect_uris": []string{"https://example.com/callback"},
|
||||
"allowed_scopes": "openid profile email",
|
||||
}, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,223 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOrgCRUD(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
var orgID string
|
||||
|
||||
// Test 1: Create Organization
|
||||
t.Run("Create Organization", func(t *testing.T) {
|
||||
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
|
||||
"code": "test_org_crud",
|
||||
"name": "Test Org CRUD",
|
||||
"description": "Organization for CRUD tests",
|
||||
}, User1Token)
|
||||
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
orgID = data.ID
|
||||
t.Logf("Created org: %s", orgID)
|
||||
})
|
||||
|
||||
if orgID == "" {
|
||||
t.Fatal("Failed to create org, skipping remaining tests")
|
||||
}
|
||||
|
||||
// Test 2: List Organizations
|
||||
t.Run("List Organizations", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/orgs", nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
Items []OrgResp `json:"items"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
t.Logf("Total orgs: %d", data.Total)
|
||||
|
||||
if data.Total <= 0 {
|
||||
t.Errorf("Expected at least 1 org, got %d", data.Total)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 3: Get Org Details
|
||||
t.Run("Get Org Details", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/orgs/"+orgID, nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data OrgResp
|
||||
decodeResponse(t, resp, &data)
|
||||
|
||||
if data.Name != "Test Org CRUD" {
|
||||
t.Errorf("Expected name 'Test Org CRUD', got '%s'", data.Name)
|
||||
}
|
||||
if data.Code != "test_org_crud" {
|
||||
t.Errorf("Expected code 'test_org_crud', got '%s'", data.Code)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 4: Update Organization
|
||||
t.Run("Update Organization", func(t *testing.T) {
|
||||
resp := doRequest(t, "PATCH", "/api/orgs/"+orgID, map[string]string{
|
||||
"name": "Updated Org Name",
|
||||
"description": "Updated description",
|
||||
}, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Verify update
|
||||
resp = doRequest(t, "GET", "/api/orgs/"+orgID, nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data OrgResp
|
||||
decodeResponse(t, resp, &data)
|
||||
|
||||
if data.Name != "Updated Org Name" {
|
||||
t.Errorf("Expected name 'Updated Org Name', got '%s'", data.Name)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 5: Get Org Members
|
||||
t.Run("Get Org Members", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/orgs/"+orgID+"/members", nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
Items []struct {
|
||||
UserID string `json:"user_id"`
|
||||
Role string `json:"role"`
|
||||
} `json:"items"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
t.Logf("Org members count: %d", len(data.Items))
|
||||
})
|
||||
|
||||
// Test 6: Delete Organization (as owner)
|
||||
t.Run("Delete Organization", func(t *testing.T) {
|
||||
resp := doRequest(t, "DELETE", "/api/orgs/"+orgID, nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Verify deletion
|
||||
resp = doRequest(t, "GET", "/api/orgs/"+orgID, nil, User1Token)
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected org to be deleted, but got 200")
|
||||
} else {
|
||||
t.Logf("Org deleted successfully, got code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 7: List after deletion
|
||||
t.Run("List After Delete", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/orgs", nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
t.Logf("Total orgs after delete: %d", data.Total)
|
||||
})
|
||||
}
|
||||
|
||||
// Test Org Tree
|
||||
func TestOrgTree(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
// Create parent org
|
||||
var parentOrgID string
|
||||
t.Run("Create Parent Org", func(t *testing.T) {
|
||||
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
|
||||
"code": "parent_org",
|
||||
"name": "Parent Organization",
|
||||
"description": "Parent org for tree test",
|
||||
}, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
parentOrgID = data.ID
|
||||
})
|
||||
|
||||
if parentOrgID == "" {
|
||||
t.Fatal("Failed to create parent org")
|
||||
}
|
||||
|
||||
// Get org tree
|
||||
t.Run("Get Org Tree", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/orgs/tree", nil, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Tree endpoint returns array directly
|
||||
var data []OrgResp
|
||||
decodeResponse(t, resp, &data)
|
||||
t.Logf("Org tree items: %d", len(data))
|
||||
})
|
||||
}
|
||||
|
||||
// Test non-member access
|
||||
func TestOrgAccessControl(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
// User1 creates org
|
||||
var orgID string
|
||||
t.Run("User1 Creates Org", func(t *testing.T) {
|
||||
resp := doRequest(t, "POST", "/api/orgs", map[string]string{
|
||||
"code": "private_org",
|
||||
"name": "Private Organization",
|
||||
"description": "Private org for access test",
|
||||
}, User1Token)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
orgID = data.ID
|
||||
})
|
||||
|
||||
if orgID == "" {
|
||||
t.Fatal("Failed to create org")
|
||||
}
|
||||
|
||||
// User2 (non-member) tries to access
|
||||
t.Run("Non-member Get Org", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/orgs/"+orgID, nil, User2Token)
|
||||
// Should fail with 403
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected non-member to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("Non-member correctly denied access, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Non-member Update Org", func(t *testing.T) {
|
||||
resp := doRequest(t, "PATCH", "/api/orgs/"+orgID, map[string]string{
|
||||
"name": "Hacked",
|
||||
}, User2Token)
|
||||
// Should fail with 403
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected non-member to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("Non-member correctly denied update, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Non-member Get Members", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/orgs/"+orgID+"/members", nil, User2Token)
|
||||
// Should fail with 403
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected non-member to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("Non-member correctly denied members access, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRoleCRUD(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
var roleID string
|
||||
|
||||
// Test 1: List Roles (as admin)
|
||||
t.Run("List Roles", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/roles", nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
Items []struct {
|
||||
ID string `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
} `json:"items"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
t.Logf("Total roles: %d", len(data.Items))
|
||||
|
||||
if len(data.Items) == 0 {
|
||||
t.Errorf("Expected some roles, got 0")
|
||||
}
|
||||
})
|
||||
|
||||
// Test 2: Create Role
|
||||
t.Run("Create Role", func(t *testing.T) {
|
||||
resp := doRequest(t, "POST", "/api/roles", map[string]string{
|
||||
"code": "test_role",
|
||||
"name": "Test Role",
|
||||
"description": "Role created for testing",
|
||||
}, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
roleID = data.ID
|
||||
t.Logf("Created role: %s", roleID)
|
||||
})
|
||||
|
||||
if roleID == "" {
|
||||
t.Fatal("Failed to create role, skipping remaining tests")
|
||||
}
|
||||
|
||||
// Test 3: Get Role Details
|
||||
t.Run("Get Role Details", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/roles/"+roleID, nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
|
||||
if data.Code != "test_role" {
|
||||
t.Errorf("Expected code 'test_role', got '%s'", data.Code)
|
||||
}
|
||||
if data.Name != "Test Role" {
|
||||
t.Errorf("Expected name 'Test Role', got '%s'", data.Name)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 4: Update Role
|
||||
t.Run("Update Role", func(t *testing.T) {
|
||||
resp := doRequest(t, "PATCH", "/api/roles/"+roleID, map[string]string{
|
||||
"name": "Updated Test Role",
|
||||
"description": "Updated description",
|
||||
}, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Verify update
|
||||
resp = doRequest(t, "GET", "/api/roles/"+roleID, nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
var data struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
|
||||
if data.Name != "Updated Test Role" {
|
||||
t.Errorf("Expected name 'Updated Test Role', got '%s'", data.Name)
|
||||
}
|
||||
})
|
||||
|
||||
// Test 5: Get Role Permissions
|
||||
t.Run("Get Role Permissions", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/roles/"+roleID+"/permissions", nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Returns array directly
|
||||
var data []struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
decodeResponse(t, resp, &data)
|
||||
t.Logf("Role permissions count: %d", len(data))
|
||||
})
|
||||
|
||||
// Test 6: Update Role Permissions
|
||||
t.Run("Update Role Permissions", func(t *testing.T) {
|
||||
// First, get available permissions
|
||||
resp := doRequest(t, "GET", "/api/roles", nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Try to update with an empty permission list first
|
||||
resp = doRequest(t, "PUT", "/api/roles/"+roleID+"/permissions", map[string]interface{}{
|
||||
"permission_ids": []string{},
|
||||
}, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
t.Logf("Updated role permissions to empty")
|
||||
})
|
||||
|
||||
// Test 7: Delete Role
|
||||
t.Run("Delete Role", func(t *testing.T) {
|
||||
resp := doRequest(t, "DELETE", "/api/roles/"+roleID, nil, AdminToken)
|
||||
assertStatus(t, resp, 200)
|
||||
|
||||
// Verify deletion
|
||||
resp = doRequest(t, "GET", "/api/roles/"+roleID, nil, AdminToken)
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected role to be deleted, but got 200")
|
||||
} else {
|
||||
t.Logf("Role deleted successfully, got code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Test role access control
|
||||
func TestRoleAccessControl(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
// Regular user tries to access role endpoints - should fail (admin only)
|
||||
t.Run("Regular User List Roles", func(t *testing.T) {
|
||||
resp := doRequest(t, "GET", "/api/roles", nil, User1Token)
|
||||
// Should fail - role:read requires admin permission
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected regular user to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("Regular user correctly denied list roles, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Regular User Create Role", func(t *testing.T) {
|
||||
resp := doRequest(t, "POST", "/api/roles", map[string]string{
|
||||
"code": "illegal_role",
|
||||
"name": "Should Fail",
|
||||
"description": "Should not be created",
|
||||
}, User1Token)
|
||||
// Should fail - needs role:create permission
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected regular user to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("Regular user correctly denied create role, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Test system role protection
|
||||
func TestSystemRoleProtection(t *testing.T) {
|
||||
ensureUsers(t)
|
||||
|
||||
// Try to modify system role (admin)
|
||||
t.Run("Update System Role", func(t *testing.T) {
|
||||
resp := doRequest(t, "PATCH", "/api/roles/admin", map[string]string{
|
||||
"name": "Hacked Admin",
|
||||
}, AdminToken)
|
||||
// Should fail - system roles are protected
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected system role update to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("System role correctly protected, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Update System Role Permissions", func(t *testing.T) {
|
||||
resp := doRequest(t, "PUT", "/api/roles/admin/permissions", map[string]interface{}{
|
||||
"permission_ids": []string{},
|
||||
}, AdminToken)
|
||||
// Should fail - system roles are protected
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected system role permissions update to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("System role permissions correctly protected, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Delete System Role", func(t *testing.T) {
|
||||
resp := doRequest(t, "DELETE", "/api/roles/admin", nil, AdminToken)
|
||||
// Should fail - system roles cannot be deleted
|
||||
if resp.Code == 200 {
|
||||
t.Errorf("Expected system role deletion to be denied, got 200")
|
||||
} else {
|
||||
t.Logf("System role deletion correctly protected, code: %d", resp.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Reference in New Issue