From a18e1b7dca1e7163c35b53f85a7873c2c0422717 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Wed, 3 Mar 2021 08:15:42 +1000 Subject: [PATCH] Exempt owners and admins from single org and 2FA policy (#1171) * Fix single org policy when creating organization Exclude owners and admins from policy when creating new org * Fix single org and 2FA policy on accepting invite Exclude owners and admins from policies * Remove looped async calls * Fix code style and formatting --- .../Controllers/OrganizationsController.cs | 13 +++- .../Implementations/OrganizationService.cs | 66 +++++++++++-------- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index f4af7d6813..43a484facf 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -163,10 +163,19 @@ namespace Bit.Api.Controllers } var policies = await _policyRepository.GetManyByUserIdAsync(user.Id); - if (policies.Any(policy => policy.Enabled && policy.Type == PolicyType.SingleOrg)) + var orgUsers = await _organizationUserRepository.GetManyByUserAsync(user.Id); + + var orgsWithSingleOrgPolicy = policies.Where(p => p.Enabled && p.Type == PolicyType.SingleOrg) + .Select(p => p.OrganizationId); + var blockedBySingleOrgPolicy = orgUsers.Any(ou => ou.Type != OrganizationUserType.Owner && + ou.Type != OrganizationUserType.Admin && + ou.Status != OrganizationUserStatusType.Invited && + orgsWithSingleOrgPolicy.Contains(ou.OrganizationId)); + + if (blockedBySingleOrgPolicy) { throw new Exception("You may not create an organization. You belong to an organization " + - "which has a policy that prohibits you from being a member of any other organization."); + "which has a policy that prohibits you from being a member of any other organization."); } var organizationSignup = model.ToOrganizationSignup(user); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 5321fc6ad0..66ec725202 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -1155,38 +1155,48 @@ namespace Bit.Core.Services } } - ICollection orgPolicies = null; - ICollection userPolicies = null; - async Task hasPolicyAsync(PolicyType policyType, bool useUserPolicies = false) + bool notExempt(OrganizationUser organizationUser) { - var policies = useUserPolicies ? - userPolicies = userPolicies ?? await _policyRepository.GetManyByUserIdAsync(user.Id) : - orgPolicies = orgPolicies ?? await _policyRepository.GetManyByOrganizationIdAsync(orgUser.OrganizationId); - - return policies.Any(p => p.Type == policyType && p.Enabled); - } - var userOrgs = await _organizationUserRepository.GetManyByUserAsync(user.Id); - if (userOrgs.Any(ou => ou.OrganizationId != orgUser.OrganizationId && ou.Status != OrganizationUserStatusType.Invited)) - { - if (await hasPolicyAsync(PolicyType.SingleOrg)) - { - throw new BadRequestException("You may not join this organization until you leave or remove " + - "all other organizations."); - } - if (await hasPolicyAsync(PolicyType.SingleOrg, true)) - { - throw new BadRequestException("You cannot join this organization because you are a member of " + - "an organization which forbids it"); - } + return organizationUser.Type != OrganizationUserType.Owner && + organizationUser.Type != OrganizationUserType.Admin; } - if (!await userService.TwoFactorIsEnabledAsync(user)) + var allOrgUsers = await _organizationUserRepository.GetManyByUserAsync(user.Id); + + // Enforce Single Organization Policy of organization user is trying to join + var thisSingleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgUser.OrganizationId, PolicyType.SingleOrg); + if (thisSingleOrgPolicy != null && + thisSingleOrgPolicy.Enabled && + notExempt(orgUser) && + allOrgUsers.Any(ou => ou.OrganizationId != orgUser.OrganizationId)) { - if (await hasPolicyAsync(PolicyType.TwoFactorAuthentication)) - { - throw new BadRequestException("You cannot join this organization until you enable " + - "two-step login on your user account."); - } + throw new BadRequestException("You may not join this organization until you leave or remove " + + "all other organizations."); + } + + // Enforce Single Organization Policy of other organizations user is a member of + var policies = await _policyRepository.GetManyByUserIdAsync(user.Id); + + var orgsWithSingleOrgPolicy = policies.Where(p => p.Enabled && p.Type == PolicyType.SingleOrg) + .Select(p => p.OrganizationId); + var blockedBySingleOrgPolicy = allOrgUsers.Any(ou => notExempt(ou) && + ou.Status != OrganizationUserStatusType.Invited && + orgsWithSingleOrgPolicy.Contains(ou.OrganizationId)); + + if (blockedBySingleOrgPolicy) + { + throw new BadRequestException("You cannot join this organization because you are a member of " + + "an organization which forbids it"); + } + + var twoFactorPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(orgUser.OrganizationId, PolicyType.TwoFactorAuthentication); + if (!await userService.TwoFactorIsEnabledAsync(user) && + twoFactorPolicy != null && + twoFactorPolicy.Enabled && + notExempt(orgUser)) + { + throw new BadRequestException("You cannot join this organization until you enable " + + "two-step login on your user account."); } orgUser.Status = OrganizationUserStatusType.Accepted;