From ec4d668bc4006db1b33c0ebfea24494ddb879eb3 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Fri, 18 Sep 2026 18:51:42 -0700 Subject: [PATCH] Multi-tenancy: impersonate has no effect inside a tenant (MT-1, MT-15) The ceiling always disables impersonate for principals in a tenant, so no tenant setting or grant lets them reach accounts beyond it. --- crates/common/src/auth/permissions.rs | 2 + tests/src/system/tenant.rs | 53 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/crates/common/src/auth/permissions.rs b/crates/common/src/auth/permissions.rs index eac86ef..d00a2d6 100644 --- a/crates/common/src/auth/permissions.rs +++ b/crates/common/src/auth/permissions.rs @@ -100,6 +100,8 @@ impl Server { }, }; ceiling(base, policy).apply(&mut permissions.enabled, &mut permissions.disabled); + // inbuxa: MT-1, MT-15: impersonation would reach beyond the tenant + permissions.disabled.set(Permission::Impersonate as usize); Ok(()) } diff --git a/tests/src/system/tenant.rs b/tests/src/system/tenant.rs index 0d006a8..aac05c2 100644 --- a/tests/src/system/tenant.rs +++ b/tests/src/system/tenant.rs @@ -523,6 +523,59 @@ pub async fn test(test: &mut TestServer) { "test 9, outside a tenant" ); + // MT-15: impersonate has no effect in a tenant, even when the tenant + // allows it and the user holds it + admin + .registry_update_object( + ObjectType::Tenant, + p_id, + json!({ + Property::Permissions: Permissions::Merge(PermissionsList { + enabled_permissions: Map::new(vec![Permission::Impersonate]), + disabled_permissions: Map::default(), + }) + }), + ) + .await; + admin + .registry_update_object( + ObjectType::Account, + p_user.id(), + json!({ + Property::Permissions: Permissions::Merge(PermissionsList { + enabled_permissions: Map::new(vec![Permission::Impersonate]), + disabled_permissions: Map::default(), + }) + }), + ) + .await; + assert!( + !test + .permissions_of(p_user.id()) + .await + .has_permission(Permission::Impersonate), + "MT-15: impersonate" + ); + assert_eq!( + p_user + .jmap_method_call( + "Mailbox/get", + json!({ "accountId": admin.id_string(), "ids": null }), + ) + .await + .method_response() + .text_field("type"), + "forbidden", + "MT-1: no reach into another account's mail" + ); + admin + .registry_update_object( + ObjectType::Tenant, + p_id, + json!({ Property::Permissions: Permissions::Inherit }), + ) + .await; + // Acceptance test 10: disabled wins over enabled in Replace (MT-14) admin .registry_update_object(