1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -05:00

Only org policy (#962)

* added OnlyOrg to PolicyType enum

* blocked accepting new org invitations if OnlyOrg is relevant to the userOrg

* blocked creating new orgs if already in an org with OnlyOrg enabled

* created email alert for OnlyOrg policy

* removed users & sent alerts when appropriate for the OnlyOrg policy

* added method to noop mail service

* cleanup for OnlyOrg policy server logic

* blocked confirming new org users if they have violated the OnlyOrg policy since accepting

* added localization strings needed for the OnlyOrg policy

* allowed OnlyOrg policy configuration from the portal

* used correct localization key for onlyorg

* formatting and messaging changes for OnlyOrg

* formatting

* messaging change

* code review changes for onlyorg

* slimmed down a conditional

* optimized getting many orgUser records from many userIds

* removed a test file

* sql formatting

* weirdness

* trying to resolve git diff formatting issues
This commit is contained in:
Addison Beck
2020-10-20 02:48:10 -04:00
committed by GitHub
parent 50cf16a3fb
commit e872b4df9d
18 changed files with 218 additions and 20 deletions

View File

@ -326,6 +326,20 @@ namespace Bit.Core.Services
await _mailDeliveryService.SendEmailAsync(message);
}
public async Task SendOrganizationUserRemovedForPolicyOnlyOrgEmailAsync(string organizationName, string email)
{
var message = CreateDefaultMessage($"You have been removed from {organizationName}", email);
var model = new OrganizationUserRemovedForPolicyOnlyOrgViewModel
{
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName),
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
SiteName = _globalSettings.SiteName
};
await AddMessageContentAsync(message, "OrganizationUserRemovedForPolicyOnlyOrg", model);
message.Category = "OrganizationUserRemovedForPolicyOnlyOrg";
await _mailDeliveryService.SendEmailAsync(message);
}
private MailMessage CreateDefaultMessage(string subject, string toEmail)
{
return CreateDefaultMessage(subject, new List<string> { toEmail });

View File

@ -1135,10 +1135,34 @@ namespace Bit.Core.Services
}
}
ICollection<Policy> orgPolicies = null;
ICollection<Policy> userPolicies = null;
async Task<bool> hasPolicyAsync(PolicyType policyType, bool useUserPolicies = false)
{
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.OnlyOrg))
{
throw new BadRequestException("You may not join this organization until you leave or remove " +
"all other organizations.");
}
if (await hasPolicyAsync(PolicyType.OnlyOrg, true))
{
throw new BadRequestException("You cannot join this organization because you are a member of " +
"an organization which forbids it");
}
}
if (!await userService.TwoFactorIsEnabledAsync(user))
{
var policies = await _policyRepository.GetManyByOrganizationIdAsync(orgUser.OrganizationId);
if (policies.Any(p => p.Type == PolicyType.TwoFactorAuthentication && p.Enabled))
if (await hasPolicyAsync(PolicyType.TwoFactorAuthentication))
{
throw new BadRequestException("You cannot join this organization until you enable " +
"two-step login on your user account.");
@ -1185,6 +1209,16 @@ namespace Bit.Core.Services
throw new BadRequestException("User does not have two-step login enabled.");
}
var usingOnlyOrgPolicy = policies.Any(p => p.Type == PolicyType.OnlyOrg && p.Enabled);
if (usingOnlyOrgPolicy)
{
var userOrgs = await _organizationUserRepository.GetManyByUserAsync(user.Id);
if (userOrgs.Any(ou => ou.OrganizationId != organizationId && ou.Status != OrganizationUserStatusType.Invited))
{
throw new BadRequestException("User is a member of another organization.");
}
}
orgUser.Status = OrganizationUserStatusType.Confirmed;
orgUser.Key = key;
orgUser.Email = null;

View File

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Table;
using Bit.Core.Repositories;
@ -53,27 +55,44 @@ namespace Bit.Core.Services
var currentPolicy = await _policyRepository.GetByIdAsync(policy.Id);
if (!currentPolicy?.Enabled ?? true)
{
if (currentPolicy.Type == Enums.PolicyType.TwoFactorAuthentication)
Organization organization = null;
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(
policy.OrganizationId);
var removableOrgUsers = orgUsers.Where(ou =>
ou.Status != Enums.OrganizationUserStatusType.Invited &&
ou.Type != Enums.OrganizationUserType.Owner && ou.UserId != savingUserId);
switch (currentPolicy.Type)
{
Organization organization = null;
var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(
policy.OrganizationId);
foreach (var orgUser in orgUsers.Where(ou =>
ou.Status != Enums.OrganizationUserStatusType.Invited &&
ou.Type != Enums.OrganizationUserType.Owner))
{
if (orgUser.UserId != savingUserId && !await userService.TwoFactorIsEnabledAsync(orgUser))
case Enums.PolicyType.TwoFactorAuthentication:
foreach (var orgUser in removableOrgUsers)
{
if (organization == null)
if (!await userService.TwoFactorIsEnabledAsync(orgUser))
{
organization = await _organizationRepository.GetByIdAsync(policy.OrganizationId);
organization = organization ?? await _organizationRepository.GetByIdAsync(policy.OrganizationId);
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
savingUserId);
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
organization.Name, orgUser.Email);
}
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
savingUserId);
await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
organization.Name, orgUser.Email);
}
}
break;
case Enums.PolicyType.OnlyOrg:
var userOrgs = await _organizationUserRepository.GetManyByManyUsersAsync(
removableOrgUsers.Select(ou => ou.UserId.Value));
foreach (var orgUser in removableOrgUsers)
{
if (userOrgs.Any(ou => ou.UserId == orgUser.UserId && ou.Status != OrganizationUserStatusType.Invited))
{
organization = organization ?? await _organizationRepository.GetByIdAsync(policy.OrganizationId);
await organizationService.DeleteUserAsync(policy.OrganizationId, orgUser.Id,
savingUserId);
await _mailService.SendOrganizationUserRemovedForPolicyOnlyOrgEmailAsync(
organization.Name, orgUser.Email);
}
}
break;
default:
break;
}
}
}