mirror of
https://github.com/bitwarden/server
synced 2025-12-17 16:53:23 +00:00
[PM-22098] Create default collection when organization member is confirmed (#5944)
* Add RequiresDefaultCollection method to PersonalOwnershipPolicyRequirement * Add CreateDefaultLocation feature flag to Constants.cs * Add DefaultUserCollectionName property to OrganizationUserConfirmRequestModel with encryption attributes * Update PersonalOwnershipPolicyRequirement instantiation in tests to use constructor with parameters instead of property assignment * Enhance ConfirmOrganizationUserCommand to support default user collection creation. Added logic to check if a default collection is required based on organization policies and feature flags. Updated ConfirmUserAsync method signature to include an optional defaultUserCollectionName parameter. Added corresponding tests to validate the new functionality. * Refactor Confirm method in OrganizationUsersController to use Guid parameters directly, simplifying the code. Updated ConfirmUserAsync call to include DefaultUserCollectionName from the input model. * Move logic for handling confirmation side effects into a separate method * Refactor PersonalOwnershipPolicyRequirement to use enum for ownership state - Introduced PersonalOwnershipState enum to represent allowed and restricted states. - Updated PersonalOwnershipPolicyRequirement constructor and properties to utilize the new enum. - Modified related classes and tests to reflect changes in ownership state handling.
This commit is contained in:
@@ -8,6 +8,7 @@ using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@@ -28,6 +29,7 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IPolicyRequirementQuery _policyRequirementQuery;
|
||||
private readonly IFeatureService _featureService;
|
||||
private readonly ICollectionRepository _collectionRepository;
|
||||
|
||||
public ConfirmOrganizationUserCommand(
|
||||
IOrganizationRepository organizationRepository,
|
||||
@@ -41,7 +43,8 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
IPolicyService policyService,
|
||||
IDeviceRepository deviceRepository,
|
||||
IPolicyRequirementQuery policyRequirementQuery,
|
||||
IFeatureService featureService)
|
||||
IFeatureService featureService,
|
||||
ICollectionRepository collectionRepository)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
@@ -55,10 +58,11 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
_deviceRepository = deviceRepository;
|
||||
_policyRequirementQuery = policyRequirementQuery;
|
||||
_featureService = featureService;
|
||||
_collectionRepository = collectionRepository;
|
||||
}
|
||||
|
||||
public async Task<OrganizationUser> ConfirmUserAsync(Guid organizationId, Guid organizationUserId, string key,
|
||||
Guid confirmingUserId)
|
||||
Guid confirmingUserId, string defaultUserCollectionName = null)
|
||||
{
|
||||
var result = await ConfirmUsersAsync(
|
||||
organizationId,
|
||||
@@ -75,6 +79,9 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
{
|
||||
throw new BadRequestException(error);
|
||||
}
|
||||
|
||||
await HandleConfirmationSideEffectsAsync(organizationId, orgUser, defaultUserCollectionName);
|
||||
|
||||
return orgUser;
|
||||
}
|
||||
|
||||
@@ -213,4 +220,54 @@ public class ConfirmOrganizationUserCommand : IConfirmOrganizationUserCommand
|
||||
.Where(d => !string.IsNullOrWhiteSpace(d.PushToken))
|
||||
.Select(d => d.Id.ToString());
|
||||
}
|
||||
|
||||
private async Task HandleConfirmationSideEffectsAsync(Guid organizationId, OrganizationUser organizationUser, string defaultUserCollectionName)
|
||||
{
|
||||
// Create DefaultUserCollection type collection for the user if the PersonalOwnership policy is enabled for the organization
|
||||
var requiresDefaultCollection = await OrganizationRequiresDefaultCollectionAsync(organizationId, organizationUser.UserId.Value, defaultUserCollectionName);
|
||||
if (requiresDefaultCollection)
|
||||
{
|
||||
await CreateDefaultCollectionAsync(organizationId, organizationUser.Id, defaultUserCollectionName);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> OrganizationRequiresDefaultCollectionAsync(Guid organizationId, Guid userId, string defaultUserCollectionName)
|
||||
{
|
||||
if (!_featureService.IsEnabled(FeatureFlagKeys.CreateDefaultLocation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if no collection name provided (backwards compatibility)
|
||||
if (string.IsNullOrWhiteSpace(defaultUserCollectionName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var personalOwnershipRequirement = await _policyRequirementQuery.GetAsync<PersonalOwnershipPolicyRequirement>(userId);
|
||||
return personalOwnershipRequirement.RequiresDefaultCollection(organizationId);
|
||||
}
|
||||
|
||||
private async Task CreateDefaultCollectionAsync(Guid organizationId, Guid organizationUserId, string defaultCollectionName)
|
||||
{
|
||||
var collection = new Collection
|
||||
{
|
||||
OrganizationId = organizationId,
|
||||
Name = defaultCollectionName,
|
||||
Type = CollectionType.DefaultUserCollection
|
||||
};
|
||||
|
||||
var userAccess = new List<CollectionAccessSelection>
|
||||
{
|
||||
new CollectionAccessSelection
|
||||
{
|
||||
Id = organizationUserId,
|
||||
ReadOnly = false,
|
||||
HidePasswords = false,
|
||||
Manage = true
|
||||
}
|
||||
};
|
||||
|
||||
await _collectionRepository.CreateAsync(collection, groups: null, users: userAccess);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user