Skip to content

Commit

Permalink
[db-queries] Avoid interactive transaction for all authz checks (#4506)
Browse files Browse the repository at this point in the history
While working on
oxidecomputer/customer-support#46 , I noticed
that we perform an interactive transaction for every authz check
perfomed by the database.

This PR re-writes this code to avoid using an interactive transaction
altogether.
  • Loading branch information
smklein authored Nov 17, 2023
1 parent ca9d90a commit 2585b88
Showing 1 changed file with 34 additions and 35 deletions.
69 changes: 34 additions & 35 deletions nexus/db-queries/src/db/datastore/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ impl DataStore {
resource_type: ResourceType,
resource_id: Uuid,
) -> Result<Vec<RoleAssignment>, Error> {
use db::schema::role_assignment::dsl;
use db::schema::role_assignment::dsl as role_dsl;
use db::schema::silo_group_membership::dsl as group_dsl;

// There is no resource-specific authorization check because all
// authenticated users need to be able to list their own roles --
Expand All @@ -140,41 +141,39 @@ impl DataStore {
// into some hurt by assigning loads of roles to someone and having that
// person attempt to access anything.

self.pool_connection_authorized(opctx).await?
.transaction_async(|conn| async move {
let mut role_assignments = dsl::role_assignment
.filter(dsl::identity_type.eq(identity_type.clone()))
.filter(dsl::identity_id.eq(identity_id))
.filter(dsl::resource_type.eq(resource_type.to_string()))
.filter(dsl::resource_id.eq(resource_id))
.select(RoleAssignment::as_select())
.load_async::<RoleAssignment>(&conn)
.await?;

// Return the roles that a silo user has from their group memberships
if identity_type == IdentityType::SiloUser {
use db::schema::silo_group_membership;

let mut group_role_assignments = dsl::role_assignment
.filter(dsl::identity_type.eq(IdentityType::SiloGroup))
.filter(dsl::identity_id.eq_any(
silo_group_membership::dsl::silo_group_membership
.filter(silo_group_membership::dsl::silo_user_id.eq(identity_id))
.select(silo_group_membership::dsl::silo_group_id)
))
.filter(dsl::resource_type.eq(resource_type.to_string()))
.filter(dsl::resource_id.eq(resource_id))
.select(RoleAssignment::as_select())
.load_async::<RoleAssignment>(&conn)
.await?;

role_assignments.append(&mut group_role_assignments);
}
let direct_roles_query = role_dsl::role_assignment
.filter(role_dsl::identity_type.eq(identity_type.clone()))
.filter(role_dsl::identity_id.eq(identity_id))
.filter(role_dsl::resource_type.eq(resource_type.to_string()))
.filter(role_dsl::resource_id.eq(resource_id))
.select(RoleAssignment::as_select());

let roles_from_groups_query = role_dsl::role_assignment
.filter(role_dsl::identity_type.eq(IdentityType::SiloGroup))
.filter(
role_dsl::identity_id.eq_any(
group_dsl::silo_group_membership
.filter(group_dsl::silo_user_id.eq(identity_id))
.select(group_dsl::silo_group_id),
),
)
.filter(role_dsl::resource_type.eq(resource_type.to_string()))
.filter(role_dsl::resource_id.eq(resource_id))
.select(RoleAssignment::as_select());

Ok(role_assignments)
})
.await
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
let conn = self.pool_connection_authorized(opctx).await?;
if identity_type == IdentityType::SiloUser {
direct_roles_query
.union(roles_from_groups_query)
.load_async::<RoleAssignment>(&*conn)
.await
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
} else {
direct_roles_query
.load_async::<RoleAssignment>(&*conn)
.await
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))
}
}

/// Fetches all of the externally-visible role assignments for the specified
Expand Down

0 comments on commit 2585b88

Please sign in to comment.