1
0
mirror of https://github.com/bitwarden/server synced 2026-01-02 00:23:40 +00:00
Files
server/src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs
Jared McCannon e646b91a50 [PM-27131] Auto confirm policy requirement (#6649)
* Added Auto confirm policy enforcement requirement. Includes strict single org enforcement along with blocking provider users from joining orgs with auto confirm enabled.
2025-12-15 15:40:00 -06:00

151 lines
6.0 KiB
C#

// FIXME: Update this file to be null safe and then delete the line below
#nullable disable
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyRequirements;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Microsoft.AspNetCore.DataProtection;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
{
private readonly IOrganizationService _organizationService;
private readonly ICollectionRepository _collectionRepository;
private readonly IOrganizationRepository _organizationRepository;
private readonly IDataProtectorTokenFactory<OrgUserInviteTokenable> _orgUserInviteTokenDataFactory;
private readonly IDataProtector _dataProtector;
private readonly IGlobalSettings _globalSettings;
private readonly IPolicyService _policyService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IFeatureService _featureService;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
public InitPendingOrganizationCommand(
IOrganizationService organizationService,
ICollectionRepository collectionRepository,
IOrganizationRepository organizationRepository,
IDataProtectorTokenFactory<OrgUserInviteTokenable> orgUserInviteTokenDataFactory,
IDataProtectionProvider dataProtectionProvider,
IGlobalSettings globalSettings,
IPolicyService policyService,
IOrganizationUserRepository organizationUserRepository,
IFeatureService featureService,
IPolicyRequirementQuery policyRequirementQuery
)
{
_organizationService = organizationService;
_collectionRepository = collectionRepository;
_organizationRepository = organizationRepository;
_orgUserInviteTokenDataFactory = orgUserInviteTokenDataFactory;
_dataProtector = dataProtectionProvider.CreateProtector(OrgUserInviteTokenable.DataProtectorPurpose);
_globalSettings = globalSettings;
_policyService = policyService;
_organizationUserRepository = organizationUserRepository;
_featureService = featureService;
_policyRequirementQuery = policyRequirementQuery;
}
public async Task InitPendingOrganizationAsync(User user, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName, string emailToken)
{
await ValidateSignUpPoliciesAsync(user.Id);
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
if (orgUser == null)
{
throw new BadRequestException("User invalid.");
}
var tokenValid = ValidateInviteToken(orgUser, user, emailToken);
if (!tokenValid)
{
throw new BadRequestException("Invalid token");
}
var org = await _organizationRepository.GetByIdAsync(organizationId);
if (org.Enabled)
{
throw new BadRequestException("Organization is already enabled.");
}
if (org.Status != OrganizationStatusType.Pending)
{
throw new BadRequestException("Organization is not on a Pending status.");
}
if (!string.IsNullOrEmpty(org.PublicKey))
{
throw new BadRequestException("Organization already has a Public Key.");
}
if (!string.IsNullOrEmpty(org.PrivateKey))
{
throw new BadRequestException("Organization already has a Private Key.");
}
org.Enabled = true;
org.Status = OrganizationStatusType.Created;
org.PublicKey = publicKey;
org.PrivateKey = privateKey;
await _organizationService.UpdateAsync(org);
if (!string.IsNullOrWhiteSpace(collectionName))
{
// give the owner Can Manage access over the default collection
List<CollectionAccessSelection> defaultOwnerAccess =
[new CollectionAccessSelection { Id = orgUser.Id, HidePasswords = false, ReadOnly = false, Manage = true }];
var defaultCollection = new Collection
{
Name = collectionName,
OrganizationId = org.Id
};
await _collectionRepository.CreateAsync(defaultCollection, null, defaultOwnerAccess);
}
}
private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
{
if (_featureService.IsEnabled(FeatureFlagKeys.AutomaticConfirmUsers))
{
var requirement = await _policyRequirementQuery.GetAsync<AutomaticUserConfirmationPolicyRequirement>(ownerId);
if (requirement.CannotCreateNewOrganization())
{
throw new BadRequestException("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.");
}
}
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);
if (anySingleOrgPolicies)
{
throw new BadRequestException("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.");
}
}
private bool ValidateInviteToken(OrganizationUser orgUser, User user, string emailToken)
{
var tokenValid = OrgUserInviteTokenable.ValidateOrgUserInviteStringToken(
_orgUserInviteTokenDataFactory, emailToken, orgUser);
return tokenValid;
}
}