[PR 4] Add Manage Users permission for Custom Roles (#7960)

This commit is contained in:
Tom authored and GitHub committed 2026-11-14 13:57:32 +01:00
1 parent 644ddfa000
commit e03151d307
4 files changed
+301 -55

No files matched your search

+56 -2
View File
@@ -882,6 +882,51 @@ impl<'r> FromRequest<'r> for AdminHeaders {
}
}
/// Generates the request guard of the routes a Custom member may use with a permission. Like AdminHeaders it lets a
/// confirmed Owner or Admin through, and a confirmed Custom member holding the permission.
macro_rules! permission_headers {
($(#[$doc:meta])* $name:ident, $permission:expr) => {
$(#[$doc])*
pub struct $name {
pub device: Device,
pub user: User,
pub membership_type: MembershipType,
pub membership: Membership,
pub ip: ClientIp,
pub org_id: OrganizationId,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for $name {
type Error = &'static str;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let headers = try_outcome!(OrgHeaders::from_request(request).await);
if headers.is_confirmed_and_admin() || headers.membership.has_custom_permission($permission) {
Outcome::Success(Self {
device: headers.device,
user: headers.user,
membership_type: headers.membership_type,
org_id: headers.membership.org_uuid.clone(),
membership: headers.membership,
ip: headers.ip,
})
} else {
// Forbidden as in Bitwarden's permission requirements
error!(target: "auth", "The current user is missing the permission for this endpoint");
Outcome::Error((rocket::http::Status::Forbidden, "You don't have permission to call this endpoint"))
}
}
}
};
}
permission_headers!(
/// For the routes that manage the members of an organization.
ManageUsersHeaders,
CustomPermission::ManageUsers
);
// col_id is usually the fourth path param ("/organizations/<org_id>/collections/<col_id>"),
// but there could be cases where it is a query value.
// First check the path, if this is not a valid uuid, try the query values.
@@ -906,6 +951,7 @@ fn get_col_id(request: &Request<'_>) -> Option<CollectionId> {
pub enum CollectionAction {
Edit,
Read,
ReadAccess,
Delete,
}
@@ -930,8 +976,11 @@ pub async fn custom_member_may(
match action {
CollectionAction::Delete => member.has_custom_permission(CustomPermission::DeleteAnyCollection),
CollectionAction::Edit if member.can_edit_any_collection() => true,
CollectionAction::Read if member.can_view_all_collections() => true,
CollectionAction::Edit | CollectionAction::Read => {
// Individual access details may be read with Manage users. The separate /users route follows
// Bitwarden's ReadAccess operation, which does not grant that permission on its own.
CollectionAction::Read if member.can_view_all_collection_access_details() => true,
CollectionAction::ReadAccess if member.can_view_all_collections() => true,
CollectionAction::Edit | CollectionAction::Read | CollectionAction::ReadAccess => {
Collection::find_explicitly_manageable_by_user(&member.org_uuid, &member.user_uuid, conn)
.await
.contains(col_id)
@@ -1020,6 +1069,11 @@ collection_headers!(
CollectionReadHeaders,
CollectionAction::Read
);
collection_headers!(
/// The collection's /users route has narrower access than its /details route.
CollectionUsersReadHeaders,
CollectionAction::ReadAccess
);
collection_headers!(
/// The same as ManagerHeaders, for the routes which delete the collection.
CollectionDeleteHeaders,