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:
@ -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 });
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user