From 184cf75fb770ba070fa2fceb5011986e8d4d539a Mon Sep 17 00:00:00 2001 From: nipil Date: Sun, 18 Oct 2026 21:37:36 +0200 Subject: [PATCH 1/2] FIX: short-circuit GET api/organizations/{id} when non-Owner Route seem to require Owner access to organization https://github.com/dani-garcia/vaultwarden/blob/1.37.4/src/api/core/organizations.rs#L264 As an admin of the organization, i get a 401 error when calling the route without this fix Only drawback: BillingEmail is set to empty string because we have nothing to set it from --- src/vaultwarden/models/bitwarden.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/vaultwarden/models/bitwarden.py b/src/vaultwarden/models/bitwarden.py index 8e81b68..794e1b3 100644 --- a/src/vaultwarden/models/bitwarden.py +++ b/src/vaultwarden/models/bitwarden.py @@ -842,11 +842,30 @@ def key(self): def get_organization( - bitwarden_client, organisation_id: UUID | str + bitwarden_client, + organisation_id: UUID | str, + profile_org: "ProfileOrganization | None" = None, ) -> Organization: - resp = bitwarden_client.api_request( - "GET", f"api/organizations/{organisation_id}" - ) + """ + Fetch an organization by ID. + + If profile_org is provided and the user is not an Owner (Type != 0), + the GET api/organizations/{id} API call is skipped because that + endpoint is Owner-only. The Organization is built directly from + sync data (Id, Name) which is sufficient since collections(), + ciphers(), and key() use less restricted endpoints. + """ + + # ProfileOrganization.Type is int, not OrganizationUserType enum. + if profile_org is not None and profile_org.Type != 0: + return Organization( + Id=profile_org.Id, + Name=profile_org.Name, + BillingEmail="", # required but unknown + Object=None, + bitwarden_client=bitwarden_client, + ) + resp = bitwarden_client.api_request("GET", f"api/organizations/{organisation_id}") return Organization.model_validate_json( resp.text, context={"client": bitwarden_client, "parent_id": organisation_id}, From b097be97321309f4c706e8449e421b867eaa423a Mon Sep 17 00:00:00 2001 From: nipil Date: Sun, 18 Oct 2026 21:54:52 +0200 Subject: [PATCH 2/2] FIX: short-circuit GET api/organizations/{id} when non-Owner (auto) Same fix but automatic instead of explicit Choose at your convenance between this and the previous commit --- src/vaultwarden/models/bitwarden.py | 35 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/vaultwarden/models/bitwarden.py b/src/vaultwarden/models/bitwarden.py index 794e1b3..a6a5f5e 100644 --- a/src/vaultwarden/models/bitwarden.py +++ b/src/vaultwarden/models/bitwarden.py @@ -844,27 +844,34 @@ def key(self): def get_organization( bitwarden_client, organisation_id: UUID | str, - profile_org: "ProfileOrganization | None" = None, ) -> Organization: """ Fetch an organization by ID. - If profile_org is provided and the user is not an Owner (Type != 0), - the GET api/organizations/{id} API call is skipped because that - endpoint is Owner-only. The Organization is built directly from - sync data (Id, Name) which is sufficient since collections(), + Checks sync data first: if the org is found and the user is not an + Owner (Type != 0), the GET api/organizations/{id} API call is skipped + because that endpoint is Owner-only. The Organization is built directly + from sync data (Id, Name) which is sufficient since collections(), ciphers(), and key() use less restricted endpoints. + + If the user is an Owner, or the org is not in sync data, the normal + API call proceeds. """ + sync = bitwarden_client.sync() + for org in sync.Profile.Organizations: + if org.Id == organisation_id and org.Type != 0: + # Non-Owner: skip API call, build from sync data + return Organization( + Id=org.Id, + Name=org.Name, + BillingEmail="", + Object=None, + bitwarden_client=bitwarden_client, + ) + if org.Id == organisation_id: + # Owner: proceed with normal API call + break - # ProfileOrganization.Type is int, not OrganizationUserType enum. - if profile_org is not None and profile_org.Type != 0: - return Organization( - Id=profile_org.Id, - Name=profile_org.Name, - BillingEmail="", # required but unknown - Object=None, - bitwarden_client=bitwarden_client, - ) resp = bitwarden_client.api_request("GET", f"api/organizations/{organisation_id}") return Organization.model_validate_json( resp.text,