[PR #7397] Add custom role permissions: Manage Users, Groups, Policies #37074

Open
opened 2026-07-13 21:35:01 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/dani-garcia/vaultwarden/pull/7397
Author: @tom27052006
Created: 7/1/2026
Status: 🔄 Open

Base: mainHead: feature/custom-role-permissions


📝 Commits (10+)

  • 1f6d457 Add custom role permissions: Manage Users, Groups, Policies
  • df18711 Silence struct_excessive_bools lint for Membership
  • 4d6b667 Server-side collection/group permission hardening; works without web-vault changes
  • e72c97c Harden custom-role permissions: block indirect collection access via groups; block manage_users revoke/restore of admins; cargo fmt
  • 9f45aa7 Fix privilege escalation: gate access_all in edit_member
  • 43fb19f Harden custom-role permission checks
  • 02b6c2c Restrict group reads to manage_groups and hide policy contents without manage_policies
  • 3b07e1e Merge remote-tracking branch 'upstream/main' into feature/custom-role-permissions
  • 1dcd3ea Block collection access via group assignment in edit_member and send_invite
  • 81b76d9 Persist manage_* permission flags on invite

📊 Changes

15 files changed (+732 additions, -164 deletions)

View changed files

migrations/mysql/2026-06-30-120000_add_custom_role_permissions/down.sql (+6 -0)
migrations/mysql/2026-06-30-120000_add_custom_role_permissions/up.sql (+9 -0)
migrations/postgresql/2026-06-30-120000_add_custom_role_permissions/down.sql (+6 -0)
migrations/postgresql/2026-06-30-120000_add_custom_role_permissions/up.sql (+9 -0)
migrations/sqlite/2026-06-30-120000_add_custom_role_permissions/down.sql (+6 -0)
migrations/sqlite/2026-06-30-120000_add_custom_role_permissions/up.sql (+9 -0)
📝 src/api/admin.rs (+7 -0)
📝 src/api/core/organizations.rs (+513 -111)
📝 src/auth.rs (+83 -0)
📝 src/db/models/collection.rs (+6 -5)
📝 src/db/models/organization.rs (+62 -46)
📝 src/db/schema.rs (+3 -0)
📝 src/static/scripts/admin_users.js (+5 -1)
📝 src/static/templates/admin/users.hbs (+3 -0)
📝 src/static/templates/scss/vaultwarden.scss.hbs (+5 -1)

📄 Description

Summary

This PR implements three granular custom-role permissions for organization members with the Custom role:

  • Manage Users
  • Manage Groups
  • Manage Policies

A member with the Custom role can be granted any combination of these three permissions. Each permission unlocks only its corresponding management actions (inviting/editing members, creating/editing groups, or editing policies) without granting access to collection contents.

Previously the Custom role was silently mapped to Manager and these permission checkboxes were hidden in the web vault because the server didn't support them.

This is a server-only change — it works with the standard, unmodified web-vault. (An earlier version of this PR required a web-vault patch; that is no longer necessary.)

Motivation

Currently, letting someone manage members or groups requires giving them an Admin role, which also grants visibility of every collection. This PR makes it possible to delegate specific administrative tasks without exposing collection contents — e.g. "this person may manage members, but must not see or manage collections".

Changes

  • Migration adding manage_users, manage_groups, manage_policies boolean columns to users_organizations (sqlite / mysql / postgresql).
  • MembershipType::Custom is now a real, persisted variant instead of being downgraded to Manager. Its ordering stays at Manager level; elevated capabilities come solely from the explicit flags.
  • Three new request guards (ManageUsersHeaders, ManageGroupsHeaders, ManagePoliciesHeaders) that allow Admin/Owner or a Custom user with the matching flag.
  • Write endpoints (member/group/policy management) are gated by the new guards. The list/bootstrap read endpoints the Admin Console needs to load (organization collection list, group list) stay on the looser Manager guard. Per-group read endpoints (a single group's details and its member list) require the manage_groups guard, and the policy-list endpoint returns policy contents only to Admin/Owner or manage_policies members (an empty list otherwise) — so the console still loads without disclosing group internals or policy configuration to members who only manage users.
  • The collection-details endpoint returns collection metadata (names/ids only) to custom users with a manage permission, so the standard web-vault can render member/group collection assignment without crashing. Cipher contents remain gated by the existing cipher endpoints.
  • Collection write paths are restricted so that a custom user without real collection rights cannot add/remove collection access — directly, in bulk, on invite, or indirectly via group assignment.
  • Narrowed the injected CSS rule that hid the custom-role checkboxes so the three supported ones are shown.

Security testing

I tested the introduced attack surface extensively via the API. Summary:

  • Each permission only unlocks its own actions; no privilege escalation between the three.
  • Collection contents are never exposed to custom users — only metadata (names); cipher endpoints and /sync return nothing for the org.
  • Full cross-organization isolation.
  • Sensitive operations against Owners (revoke/delete/reset-password) are denied.

Several real privilege-escalation issues were found and fixed during testing, all around collection access: a manage_users user could originally grant themselves the other permissions, assign collections directly / in bulk / on invite, create collections, or grant collection access indirectly by assigning a member to a group that has collections. All are now blocked server-side.

Read separation too: a group's collection assignments and member list are only readable by Admin/Owner or manage_groups members, and policy configuration only by Admin/Owner or manage_policies members — a member who only manages users cannot read group internals or policies.

This is thorough targeted testing of the changed surface, not a full audit. Permission systems are easy to get subtly wrong; a maintainer review of the permission checks would be very valuable, and there may be paths I haven't considered (e.g. interaction with SSO, emergency access, or concurrent requests).

Honest disclosure

Most of this work was done with the help of AI. I'm relatively new to the Vaultwarden codebase (and to Rust), so I can't claim to fully understand every implication on my own. Everything in the summary above I verified myself against a running instance — but I'd genuinely appreciate a careful review, and I'm happy to adjust or drop anything that doesn't fit the project's direction. If anything is unclear, just ask.

Notes

  • cargo clippy --features sqlite passes cleanly.
  • Not yet runtime-tested against mysql / postgresql (migrations are provided, but only SQLite was exercised at runtime).

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/dani-garcia/vaultwarden/pull/7397 **Author:** [@tom27052006](https://github.com/tom27052006) **Created:** 7/1/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feature/custom-role-permissions` --- ### 📝 Commits (10+) - [`1f6d457`](https://github.com/dani-garcia/vaultwarden/commit/1f6d45753389d8dad991771499bc935a56cf0394) Add custom role permissions: Manage Users, Groups, Policies - [`df18711`](https://github.com/dani-garcia/vaultwarden/commit/df18711b49984a99de872ec62a9fce9568fccf27) Silence struct_excessive_bools lint for Membership - [`4d6b667`](https://github.com/dani-garcia/vaultwarden/commit/4d6b667ffcf72d8b3e90df8bcc6f35859c3e8d5e) Server-side collection/group permission hardening; works without web-vault changes - [`e72c97c`](https://github.com/dani-garcia/vaultwarden/commit/e72c97c30d8ffca4b6f04a63823393fc4a96f0e3) Harden custom-role permissions: block indirect collection access via groups; block manage_users revoke/restore of admins; cargo fmt - [`9f45aa7`](https://github.com/dani-garcia/vaultwarden/commit/9f45aa77e7998411dc45f831226a22d42ac859a3) Fix privilege escalation: gate access_all in edit_member - [`43fb19f`](https://github.com/dani-garcia/vaultwarden/commit/43fb19f190e75b00ab45cba31effe95bee50252f) Harden custom-role permission checks - [`02b6c2c`](https://github.com/dani-garcia/vaultwarden/commit/02b6c2c205f1d673b9c7d68cb3f3413050779888) Restrict group reads to manage_groups and hide policy contents without manage_policies - [`3b07e1e`](https://github.com/dani-garcia/vaultwarden/commit/3b07e1ed836634ceec8ad6808ab4842235d7cdf9) Merge remote-tracking branch 'upstream/main' into feature/custom-role-permissions - [`1dcd3ea`](https://github.com/dani-garcia/vaultwarden/commit/1dcd3ea26f32d73cab9ff181e55acb2c31037591) Block collection access via group assignment in edit_member and send_invite - [`81b76d9`](https://github.com/dani-garcia/vaultwarden/commit/81b76d9782aa84cfb8e5a9dbf5b7342456b8139f) Persist manage_* permission flags on invite ### 📊 Changes **15 files changed** (+732 additions, -164 deletions) <details> <summary>View changed files</summary> ➕ `migrations/mysql/2026-06-30-120000_add_custom_role_permissions/down.sql` (+6 -0) ➕ `migrations/mysql/2026-06-30-120000_add_custom_role_permissions/up.sql` (+9 -0) ➕ `migrations/postgresql/2026-06-30-120000_add_custom_role_permissions/down.sql` (+6 -0) ➕ `migrations/postgresql/2026-06-30-120000_add_custom_role_permissions/up.sql` (+9 -0) ➕ `migrations/sqlite/2026-06-30-120000_add_custom_role_permissions/down.sql` (+6 -0) ➕ `migrations/sqlite/2026-06-30-120000_add_custom_role_permissions/up.sql` (+9 -0) 📝 `src/api/admin.rs` (+7 -0) 📝 `src/api/core/organizations.rs` (+513 -111) 📝 `src/auth.rs` (+83 -0) 📝 `src/db/models/collection.rs` (+6 -5) 📝 `src/db/models/organization.rs` (+62 -46) 📝 `src/db/schema.rs` (+3 -0) 📝 `src/static/scripts/admin_users.js` (+5 -1) 📝 `src/static/templates/admin/users.hbs` (+3 -0) 📝 `src/static/templates/scss/vaultwarden.scss.hbs` (+5 -1) </details> ### 📄 Description ## Summary This PR implements three granular custom-role permissions for organization members with the **Custom** role: - Manage Users - Manage Groups - Manage Policies A member with the Custom role can be granted any combination of these three permissions. Each permission unlocks only its corresponding management actions (inviting/editing members, creating/editing groups, or editing policies) **without** granting access to collection contents. Previously the Custom role was silently mapped to Manager and these permission checkboxes were hidden in the web vault because the server didn't support them. **This is a server-only change** — it works with the standard, unmodified web-vault. (An earlier version of this PR required a web-vault patch; that is no longer necessary.) ## Motivation Currently, letting someone manage members or groups requires giving them an Admin role, which also grants visibility of every collection. This PR makes it possible to delegate specific administrative tasks without exposing collection contents — e.g. "this person may manage members, but must not see or manage collections". ## Changes - Migration adding `manage_users`, `manage_groups`, `manage_policies` boolean columns to `users_organizations` (sqlite / mysql / postgresql). - `MembershipType::Custom` is now a real, persisted variant instead of being downgraded to Manager. Its ordering stays at Manager level; elevated capabilities come solely from the explicit flags. - Three new request guards (`ManageUsersHeaders`, `ManageGroupsHeaders`, `ManagePoliciesHeaders`) that allow Admin/Owner or a Custom user with the matching flag. - Write endpoints (member/group/policy management) are gated by the new guards. The list/bootstrap read endpoints the Admin Console needs to load (organization collection list, group list) stay on the looser Manager guard. Per-group read endpoints (a single group's details and its member list) require the manage_groups guard, and the policy-list endpoint returns policy contents only to Admin/Owner or manage_policies members (an empty list otherwise) — so the console still loads without disclosing group internals or policy configuration to members who only manage users. - The collection-details endpoint returns collection metadata (names/ids only) to custom users with a manage permission, so the standard web-vault can render member/group collection assignment without crashing. Cipher contents remain gated by the existing cipher endpoints. - Collection write paths are restricted so that a custom user without real collection rights cannot add/remove collection access — directly, in bulk, on invite, or indirectly via group assignment. - Narrowed the injected CSS rule that hid the custom-role checkboxes so the three supported ones are shown. ## Security testing I tested the introduced attack surface extensively via the API. Summary: - Each permission only unlocks its own actions; no privilege escalation between the three. - Collection **contents** are never exposed to custom users — only metadata (names); cipher endpoints and `/sync` return nothing for the org. - Full cross-organization isolation. - Sensitive operations against Owners (revoke/delete/reset-password) are denied. **Several real privilege-escalation issues were found and fixed during testing**, all around collection access: a `manage_users` user could originally grant themselves the other permissions, assign collections directly / in bulk / on invite, create collections, or grant collection access indirectly by assigning a member to a group that has collections. All are now blocked server-side. Read separation too: a group's collection assignments and member list are only readable by Admin/Owner or manage_groups members, and policy configuration only by Admin/Owner or manage_policies members — a member who only manages users cannot read group internals or policies. This is thorough targeted testing of the changed surface, **not a full audit**. Permission systems are easy to get subtly wrong; a maintainer review of the permission checks would be very valuable, and there may be paths I haven't considered (e.g. interaction with SSO, emergency access, or concurrent requests). ## Honest disclosure Most of this work was done with the help of AI. I'm relatively new to the Vaultwarden codebase (and to Rust), so I can't claim to fully understand every implication on my own. Everything in the summary above I verified myself against a running instance — but I'd genuinely appreciate a careful review, and I'm happy to adjust or drop anything that doesn't fit the project's direction. If anything is unclear, just ask. ## Notes - `cargo clippy --features sqlite` passes cleanly. - Not yet runtime-tested against mysql / postgresql (migrations are provided, but only SQLite was exercised at runtime). --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-07-13 21:35:01 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/vaultwarden#37074