package tests import ( "testing" ) // TestOAuthClientSecretLeak 测试 OAuth 客户端密钥泄漏 func TestOAuthClientSecretLeak(t *testing.T) { ensureUsers(t) var clientID string var clientSecret string // Admin 创建 OAuth 客户端 t.Run("Admin creates OAuth client", func(t *testing.T) { resp := doRequest(t, "POST", "/api/oauth/clients", map[string]any{ "name": "Test Client", "redirect_uris": []string{"https://example.com/callback"}, "allowed_scopes": "openid profile email", }, AdminToken) assertStatus(t, resp, 200) var data struct { ClientID string `json:"client_id"` ClientSecret string `json:"client_secret"` } decodeResponse(t, resp, &data) clientID = data.ClientID clientSecret = data.ClientSecret }) if clientID == "" || clientSecret == "" { t.Skip("Failed to create OAuth client") } // 普通用户不应该看到 client_secret t.Run("Regular user cannot see client secret", func(t *testing.T) { resp := doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, User1Token) assertStatus(t, resp, 200) var data struct { ClientSecret string `json:"client_secret"` } decodeResponse(t, resp, &data) if data.ClientSecret != "" { t.Errorf("Regular user should not see client_secret, got: %s", data.ClientSecret) } }) // Admin 也不应该在 GET 请求中看到 client_secret t.Run("Admin GET should not reveal client secret", func(t *testing.T) { resp := doRequest(t, "GET", "/api/oauth/clients/"+clientID, nil, AdminToken) assertStatus(t, resp, 200) var data struct { ClientSecret string `json:"client_secret"` } decodeResponse(t, resp, &data) // client_secret 应该只在创建时返回 if data.ClientSecret != "" { t.Logf("Warning: client_secret visible in GET response: %s", data.ClientSecret) } }) // 清理 t.Run("Cleanup", func(t *testing.T) { resp := doRequest(t, "DELETE", "/api/oauth/clients/"+clientID, nil, AdminToken) assertStatus(t, resp, 200) }) } // TestOAuthClientAccessControlSecurity 测试 OAuth 客户端访问控制安全 func TestOAuthClientAccessControlSecurity(t *testing.T) { ensureUsers(t) var clientID string // User1 创建 OAuth 客户端 t.Run("User1 creates OAuth client", func(t *testing.T) { resp := doRequest(t, "POST", "/api/oauth/clients", map[string]any{ "name": "User1 Client", "redirect_uris": []string{"https://user1.com/callback"}, "allowed_scopes": "openid profile", }, User1Token) assertStatus(t, resp, 200) var data struct { ClientID string `json:"client_id"` } decodeResponse(t, resp, &data) clientID = data.ClientID }) if clientID == "" { t.Skip("Failed to create OAuth client") } // User2 不应该能修改 User1 的客户端 t.Run("User2 cannot modify User1's client", func(t *testing.T) { resp := doRequest(t, "PATCH", "/api/oauth/clients/"+clientID, map[string]string{ "name": "Hacked by User2", }, User2Token) if resp.Code == 200 { t.Errorf("User2 should not be able to modify User1's client, got 200") } }) // User2 不应该能删除 User1 的客户端 t.Run("User2 cannot delete User1's client", func(t *testing.T) { resp := doRequest(t, "DELETE", "/api/oauth/clients/"+clientID, nil, User2Token) if resp.Code == 200 { t.Errorf("User2 should not be able to delete User1's client, got 200") } }) // Admin 可以修改任何客户端 t.Run("Admin can modify any client", func(t *testing.T) { resp := doRequest(t, "PATCH", "/api/oauth/clients/"+clientID, map[string]string{ "name": "Modified by Admin", }, AdminToken) assertStatus(t, resp, 200) }) // 清理 t.Run("Cleanup", func(t *testing.T) { resp := doRequest(t, "DELETE", "/api/oauth/clients/"+clientID, nil, AdminToken) assertStatus(t, resp, 200) }) } // TestOAuthAuthorizeEndpoint 测试 OAuth 授权端点安全 func TestOAuthAuthorizeEndpoint(t *testing.T) { ensureUsers(t) // 测试缺少参数的授权请求 t.Run("Authorize without client_id", func(t *testing.T) { resp := doRequest(t, "GET", "/api/oauth/authorize?response_type=code&redirect_uri=https://example.com/callback", nil, "") // 应该返回错误,因为没有 client_id if resp.Code == 200 { t.Errorf("Expected error for missing client_id, got 200") } }) // 测试无效的 response_type t.Run("Authorize with invalid response_type", func(t *testing.T) { resp := doRequest(t, "GET", "/api/oauth/authorize?client_id=test&response_type=invalid&redirect_uri=https://example.com/callback", nil, "") if resp.Code == 200 { t.Errorf("Expected error for invalid response_type, got 200") } }) } // TestOAuthTokenEndpoint 测试 OAuth Token 端点安全 func TestOAuthTokenEndpoint(t *testing.T) { ensureUsers(t) // 测试缺少参数的 token 请求 t.Run("Token without grant_type", func(t *testing.T) { resp := doRequest(t, "POST", "/api/oauth/token", map[string]string{ "client_id": "test", }, "") // 应该返回错误 if resp.Code == 200 { t.Errorf("Expected error for missing grant_type, got 200") } }) // 测试无效的 grant_type t.Run("Token with invalid grant_type", func(t *testing.T) { resp := doRequest(t, "POST", "/api/oauth/token", map[string]string{ "grant_type": "invalid_grant", "client_id": "test", }, "") if resp.Code == 200 { t.Errorf("Expected error for invalid grant_type, got 200") } }) } // TestOAuthScopeValidation 测试 OAuth Scope 验证 func TestOAuthScopeValidation(t *testing.T) { ensureUsers(t) var clientID string // 创建带有特定 scope 限制的客户端 t.Run("Create client with limited scopes", func(t *testing.T) { resp := doRequest(t, "POST", "/api/oauth/clients", map[string]any{ "name": "Limited Scope Client", "redirect_uris": []string{"https://example.com/callback"}, "allowed_scopes": "openid profile", // 只允许 openid 和 profile }, AdminToken) assertStatus(t, resp, 200) var data struct { ClientID string `json:"client_id"` } decodeResponse(t, resp, &data) clientID = data.ClientID }) if clientID == "" { t.Skip("Failed to create OAuth client") } // 测试:请求超出允许范围的 scope t.Run("Request scope beyond allowed", func(t *testing.T) { // 注意:这需要实际的授权流程,这里只是测试端点行为 resp := doRequest(t, "GET", "/api/oauth/authorize?client_id="+clientID+"&response_type=code&redirect_uri=https://example.com/callback&scope=openid profile email admin", nil, "") // 应该返回错误或限制 scope if resp.Code == 200 { t.Logf("Warning: Request for excessive scope returned 200, scope validation may be missing") } }) // 清理 t.Run("Cleanup", func(t *testing.T) { resp := doRequest(t, "DELETE", "/api/oauth/clients/"+clientID, nil, AdminToken) assertStatus(t, resp, 200) }) } // TestOAuthRedirectURISecurity 测试 OAuth Redirect URI 安全 func TestOAuthRedirectURISecurity(t *testing.T) { ensureUsers(t) var clientID string // 创建带有特定 redirect_uri 的客户端 t.Run("Create client with specific redirect_uri", func(t *testing.T) { resp := doRequest(t, "POST", "/api/oauth/clients", map[string]any{ "name": "URI Test Client", "redirect_uris": []string{"https://trusted.example.com/callback"}, "allowed_scopes": "openid", }, AdminToken) assertStatus(t, resp, 200) var data struct { ClientID string `json:"client_id"` } decodeResponse(t, resp, &data) clientID = data.ClientID }) if clientID == "" { t.Skip("Failed to create OAuth client") } // 测试:使用不匹配的 redirect_uri t.Run("Authorize with mismatched redirect_uri", func(t *testing.T) { resp := doRequest(t, "GET", "/api/oauth/authorize?client_id="+clientID+"&response_type=code&redirect_uri=https://evil.com/callback", nil, "") // 应该返回错误,因为 redirect_uri 不匹配 if resp.Code == 200 { t.Errorf("Expected error for mismatched redirect_uri, got 200") } }) // 清理 t.Run("Cleanup", func(t *testing.T) { resp := doRequest(t, "DELETE", "/api/oauth/clients/"+clientID, nil, AdminToken) assertStatus(t, resp, 200) }) }