From 5c162411355c52d6e65e678c0dd1efee0e4e2bc7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Jun 2026 08:18:16 +0530 Subject: [PATCH 1/2] Add Rust integration tests for issue #513 - role assignments with same role on multiple scopes Tests verify that listing role assignments correctly handles cases where the same role is assigned to a user on multiple different targets (projects + system). Fixes: #513 --- .../integration/src/assignment/grant/list.rs | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/tests/integration/src/assignment/grant/list.rs b/tests/integration/src/assignment/grant/list.rs index 35fa6bccb..f94c0e015 100644 --- a/tests/integration/src/assignment/grant/list.rs +++ b/tests/integration/src/assignment/grant/list.rs @@ -200,3 +200,140 @@ async fn test_list_user_roles() -> Result<()> { ); Ok(()) } + +// Add these two tests at the BOTTOM of: +// tests/integration/src/assignment/grant/list.rs +// (paste before the last closing brace if any, or just at the end of the file) + +#[traced_test] +#[tokio::test] +async fn test_list_role_assignments_by_user_same_role_multiple_scopes() -> Result<()> { + // Regression test for issue #513: + // Assigning the same role to a user on multiple projects + system was + // mixing up assignments due to HashMap by role_id. This test verifies + // all assignments are returned correctly when listing by user_id only. + let (state, _) = get_state().await?; + + let domain = create_domain!(state)?; + let project_1 = create_project!(state, domain.id.clone())?; + let project_2 = create_project!(state, domain.id.clone())?; + let user = create_user!(state, domain.id.clone())?; + let role = create_role!(state)?; + + // Assign the SAME role to the user on two different projects AND system + for assignment in [ + AssignmentCreate::user_project(&user.id, &project_1.id, &role.id, false), + AssignmentCreate::user_project(&user.id, &project_2.id, &role.id, false), + AssignmentCreate::user_system(&user.id, "all", &role.id, false), + ] { + state + .provider + .get_assignment_provider() + .create_grant(&state, assignment) + .await?; + } + + // List assignments filtering by user_id ONLY (no target filter) + let assignments = state + .provider + .get_assignment_provider() + .list_role_assignments( + &state, + &RoleAssignmentListParametersBuilder::default() + .user_id(user.id.clone()) + .build()?, + ) + .await?; + + // Should return exactly 3 assignments - one per scope + // (Before the fix, HashMap deduplicated by role_id and returned only 1) + assert_eq!( + assignments.len(), + 3, + "Expected 3 role assignments (same role on project_1 + project_2 + system), got {}. \ + This may indicate the HashMap bug from issue #513 is present.", + assignments.len() + ); + + // Verify both project targets are present + let target_ids: BTreeSet = assignments.iter().map(|a| a.target_id.clone()).collect(); + assert!( + target_ids.contains(&project_1.id), + "Assignment on project_1 should be present" + ); + assert!( + target_ids.contains(&project_2.id), + "Assignment on project_2 should be present" + ); + + Ok(()) +} + +#[traced_test] +#[tokio::test] +async fn test_list_role_assignments_by_role_id_same_role_multiple_scopes() -> Result<()> { + // Regression test for issue #513: + // Listing by role_id should return all assignments for that role + // across different targets (project_1, project_2, system). + let (state, _) = get_state().await?; + + let domain = create_domain!(state)?; + let project_1 = create_project!(state, domain.id.clone())?; + let project_2 = create_project!(state, domain.id.clone())?; + let user = create_user!(state, domain.id.clone())?; + let role = create_role!(state)?; + + // Assign the SAME role on two projects and system + for assignment in [ + AssignmentCreate::user_project(&user.id, &project_1.id, &role.id, false), + AssignmentCreate::user_project(&user.id, &project_2.id, &role.id, false), + AssignmentCreate::user_system(&user.id, "all", &role.id, false), + ] { + state + .provider + .get_assignment_provider() + .create_grant(&state, assignment) + .await?; + } + + // List assignments filtering by role_id ONLY + let assignments = state + .provider + .get_assignment_provider() + .list_role_assignments( + &state, + &RoleAssignmentListParametersBuilder::default() + .role_id(role.id.clone()) + .build()?, + ) + .await?; + + // Filter to only our test user's assignments + let user_assignments: Vec<_> = assignments + .iter() + .filter(|a| a.actor_id == user.id) + .collect(); + + // Should have all 3 assignments for this user+role combination + assert_eq!( + user_assignments.len(), + 3, + "Expected 3 assignments when filtering by role_id, got {}. \ + This may indicate the HashMap bug from issue #513 is present.", + user_assignments.len() + ); + + // Verify the correct targets are present + let target_ids: BTreeSet = + user_assignments.iter().map(|a| a.target_id.clone()).collect(); + assert!( + target_ids.contains(&project_1.id), + "Assignment on project_1 should be present when filtering by role_id" + ); + assert!( + target_ids.contains(&project_2.id), + "Assignment on project_2 should be present when filtering by role_id" + ); + + Ok(()) +} \ No newline at end of file From 62c6cb4a4f4d501d01b0e90a5819de37b70d6443 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 4 Jun 2026 09:08:54 +0530 Subject: [PATCH 2/2] Add API test for issue #515 - /auth/projects returns empty for user without roles Tests that when a user has no role assignments, the /auth/projects endpoint returns an empty list, not all projects in the system. Fixes: #515 --- tests/api/src/auth/project.rs | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tests/api/src/auth/project.rs b/tests/api/src/auth/project.rs index 006ba7459..d24f7422c 100644 --- a/tests/api/src/auth/project.rs +++ b/tests/api/src/auth/project.rs @@ -57,3 +57,85 @@ async fn test_list_user_projects() -> Result<()> { assert!(!projects.is_empty()); Ok(()) } + + +#[tokio::test] +async fn test_auth_projects_returns_empty_for_user_without_roles() -> Result<()> { + // Regression test for issue #515: + // When a user has no role assignments, /auth/projects should return + // an empty list, not all projects in the system. + + let mut admin_client = TestClient::default()?; + admin_client.auth_admin().await?; + + // Create a domain for the test user + let domain_response = admin_client + .client + .post(admin_client.base_url.join("v3/domains")?) + .json(&serde_json::json!({ + "domain": { + "name": format!("test-domain-{}", uuid::Uuid::new_v4()), + "enabled": true + } + })) + .send() + .await?; + + let domain: serde_json::Value = domain_response.json().await?; + let domain_id = domain["domain"]["id"].as_str().unwrap(); + + // Create a user with no role assignments + let user_password = "TestPassword123!"; + let user_response = admin_client + .client + .post(admin_client.base_url.join("v3/users")?) + .json(&serde_json::json!({ + "user": { + "name": format!("test-user-{}", uuid::Uuid::new_v4()), + "domain_id": domain_id, + "password": user_password, + "enabled": true + } + })) + .send() + .await?; + + let user: serde_json::Value = user_response.json().await?; + let user_id = user["user"]["id"].as_str().unwrap(); + + // Authenticate as the user with no roles + let mut user_client = TestClient::default()?; + user_client + .auth_password( + PasswordAuthBuilder::default() + .user( + UserBuilder::default() + .id(user_id) + .domain( + DomainBuilder::default() + .id(domain_id) + .build()? + ) + .build()? + ) + .password(user_password) + .build()?, + None, // No scope - unscoped auth + ) + .await?; + + // Call /auth/projects - should return empty list + let projects = list_auth_projects(&Arc::new( + AsyncOpenStack::new(&CloudConfig::from_env()?).await? + )) + .await?; + + // The user has no role assignments, so projects list should be empty + assert!( + projects.is_empty(), + "User with no role assignments should see no projects. \ + This may indicate the bug from issue #515 is present (returning all projects instead of empty list)" + ); + + Ok(()) +} \ No newline at end of file