1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-09 20:50:31 -05:00

Merge branch 'main' into test-docker-stuff

This commit is contained in:
tangowithfoxtrot 2025-04-08 13:32:30 -07:00 committed by GitHub
commit fc951f885f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 347 additions and 301 deletions

View File

@ -184,7 +184,7 @@ public class UsersController : Controller
private async Task<bool?> AccountDeprovisioningEnabled(Guid userId) private async Task<bool?> AccountDeprovisioningEnabled(Guid userId)
{ {
return _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) return _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
? await _userService.IsManagedByAnyOrganizationAsync(userId) ? await _userService.IsClaimedByAnyOrganizationAsync(userId)
: null; : null;
} }
} }

View File

@ -56,8 +56,8 @@ public class OrganizationUsersController : Controller
private readonly IOrganizationUserUserDetailsQuery _organizationUserUserDetailsQuery; private readonly IOrganizationUserUserDetailsQuery _organizationUserUserDetailsQuery;
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery; private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand; private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
private readonly IDeleteManagedOrganizationUserAccountCommand _deleteManagedOrganizationUserAccountCommand; private readonly IDeleteClaimedOrganizationUserAccountCommand _deleteClaimedOrganizationUserAccountCommand;
private readonly IGetOrganizationUsersManagementStatusQuery _getOrganizationUsersManagementStatusQuery; private readonly IGetOrganizationUsersClaimedStatusQuery _getOrganizationUsersClaimedStatusQuery;
private readonly IPolicyRequirementQuery _policyRequirementQuery; private readonly IPolicyRequirementQuery _policyRequirementQuery;
private readonly IFeatureService _featureService; private readonly IFeatureService _featureService;
private readonly IPricingClient _pricingClient; private readonly IPricingClient _pricingClient;
@ -83,8 +83,8 @@ public class OrganizationUsersController : Controller
IOrganizationUserUserDetailsQuery organizationUserUserDetailsQuery, IOrganizationUserUserDetailsQuery organizationUserUserDetailsQuery,
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery, ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
IRemoveOrganizationUserCommand removeOrganizationUserCommand, IRemoveOrganizationUserCommand removeOrganizationUserCommand,
IDeleteManagedOrganizationUserAccountCommand deleteManagedOrganizationUserAccountCommand, IDeleteClaimedOrganizationUserAccountCommand deleteClaimedOrganizationUserAccountCommand,
IGetOrganizationUsersManagementStatusQuery getOrganizationUsersManagementStatusQuery, IGetOrganizationUsersClaimedStatusQuery getOrganizationUsersClaimedStatusQuery,
IPolicyRequirementQuery policyRequirementQuery, IPolicyRequirementQuery policyRequirementQuery,
IFeatureService featureService, IFeatureService featureService,
IPricingClient pricingClient, IPricingClient pricingClient,
@ -109,8 +109,8 @@ public class OrganizationUsersController : Controller
_organizationUserUserDetailsQuery = organizationUserUserDetailsQuery; _organizationUserUserDetailsQuery = organizationUserUserDetailsQuery;
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery; _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
_removeOrganizationUserCommand = removeOrganizationUserCommand; _removeOrganizationUserCommand = removeOrganizationUserCommand;
_deleteManagedOrganizationUserAccountCommand = deleteManagedOrganizationUserAccountCommand; _deleteClaimedOrganizationUserAccountCommand = deleteClaimedOrganizationUserAccountCommand;
_getOrganizationUsersManagementStatusQuery = getOrganizationUsersManagementStatusQuery; _getOrganizationUsersClaimedStatusQuery = getOrganizationUsersClaimedStatusQuery;
_policyRequirementQuery = policyRequirementQuery; _policyRequirementQuery = policyRequirementQuery;
_featureService = featureService; _featureService = featureService;
_pricingClient = pricingClient; _pricingClient = pricingClient;
@ -127,11 +127,11 @@ public class OrganizationUsersController : Controller
throw new NotFoundException(); throw new NotFoundException();
} }
var managedByOrganization = await GetManagedByOrganizationStatusAsync( var claimedByOrganizationStatus = await GetClaimedByOrganizationStatusAsync(
organizationUser.OrganizationId, organizationUser.OrganizationId,
[organizationUser.Id]); [organizationUser.Id]);
var response = new OrganizationUserDetailsResponseModel(organizationUser, managedByOrganization[organizationUser.Id], collections); var response = new OrganizationUserDetailsResponseModel(organizationUser, claimedByOrganizationStatus[organizationUser.Id], collections);
if (includeGroups) if (includeGroups)
{ {
@ -175,13 +175,13 @@ public class OrganizationUsersController : Controller
} }
); );
var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(organizationUsers); var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(organizationUsers);
var organizationUsersManagementStatus = await GetManagedByOrganizationStatusAsync(orgId, organizationUsers.Select(o => o.Id)); var organizationUsersClaimedStatus = await GetClaimedByOrganizationStatusAsync(orgId, organizationUsers.Select(o => o.Id));
var responses = organizationUsers var responses = organizationUsers
.Select(o => .Select(o =>
{ {
var userTwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == o.Id).twoFactorIsEnabled; var userTwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == o.Id).twoFactorIsEnabled;
var managedByOrganization = organizationUsersManagementStatus[o.Id]; var claimedByOrganization = organizationUsersClaimedStatus[o.Id];
var orgUser = new OrganizationUserUserDetailsResponseModel(o, userTwoFactorEnabled, managedByOrganization); var orgUser = new OrganizationUserUserDetailsResponseModel(o, userTwoFactorEnabled, claimedByOrganization);
return orgUser; return orgUser;
}); });
@ -591,7 +591,7 @@ public class OrganizationUsersController : Controller
throw new UnauthorizedAccessException(); throw new UnauthorizedAccessException();
} }
await _deleteManagedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id); await _deleteClaimedOrganizationUserAccountCommand.DeleteUserAsync(orgId, id, currentUser.Id);
} }
[RequireFeature(FeatureFlagKeys.AccountDeprovisioning)] [RequireFeature(FeatureFlagKeys.AccountDeprovisioning)]
@ -610,7 +610,7 @@ public class OrganizationUsersController : Controller
throw new UnauthorizedAccessException(); throw new UnauthorizedAccessException();
} }
var results = await _deleteManagedOrganizationUserAccountCommand.DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id); var results = await _deleteClaimedOrganizationUserAccountCommand.DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id);
return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r => return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage))); new OrganizationUserBulkResponseModel(r.OrganizationUserId, r.ErrorMessage)));
@ -717,14 +717,14 @@ public class OrganizationUsersController : Controller
new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2))); new OrganizationUserBulkResponseModel(r.Item1.Id, r.Item2)));
} }
private async Task<IDictionary<Guid, bool>> GetManagedByOrganizationStatusAsync(Guid orgId, IEnumerable<Guid> userIds) private async Task<IDictionary<Guid, bool>> GetClaimedByOrganizationStatusAsync(Guid orgId, IEnumerable<Guid> userIds)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
{ {
return userIds.ToDictionary(kvp => kvp, kvp => false); return userIds.ToDictionary(kvp => kvp, kvp => false);
} }
var usersOrganizationManagementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(orgId, userIds); var usersOrganizationClaimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgId, userIds);
return usersOrganizationManagementStatus; return usersOrganizationClaimedStatus;
} }
} }

View File

@ -140,10 +140,10 @@ public class OrganizationsController : Controller
var organizations = await _organizationUserRepository.GetManyDetailsByUserAsync(userId, var organizations = await _organizationUserRepository.GetManyDetailsByUserAsync(userId,
OrganizationUserStatusType.Confirmed); OrganizationUserStatusType.Confirmed);
var organizationManagingActiveUser = await _userService.GetOrganizationsManagingUserAsync(userId); var organizationsClaimingActiveUser = await _userService.GetOrganizationsClaimingUserAsync(userId);
var organizationIdsManagingActiveUser = organizationManagingActiveUser.Select(o => o.Id); var organizationIdsClaimingActiveUser = organizationsClaimingActiveUser.Select(o => o.Id);
var responses = organizations.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsManagingActiveUser)); var responses = organizations.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsClaimingActiveUser));
return new ListResponseModel<ProfileOrganizationResponseModel>(responses); return new ListResponseModel<ProfileOrganizationResponseModel>(responses);
} }
@ -277,9 +277,9 @@ public class OrganizationsController : Controller
} }
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
&& (await _userService.GetOrganizationsManagingUserAsync(user.Id)).Any(x => x.Id == id)) && (await _userService.GetOrganizationsClaimingUserAsync(user.Id)).Any(x => x.Id == id))
{ {
throw new BadRequestException("Managed user account cannot leave managing organization. Contact your organization administrator for additional details."); throw new BadRequestException("Claimed user account cannot leave claiming organization. Contact your organization administrator for additional details.");
} }
await _removeOrganizationUserCommand.UserLeaveAsync(id, user.Id); await _removeOrganizationUserCommand.UserLeaveAsync(id, user.Id);

View File

@ -66,24 +66,30 @@ public class OrganizationUserDetailsResponseModel : OrganizationUserResponseMode
{ {
public OrganizationUserDetailsResponseModel( public OrganizationUserDetailsResponseModel(
OrganizationUser organizationUser, OrganizationUser organizationUser,
bool managedByOrganization, bool claimedByOrganization,
IEnumerable<CollectionAccessSelection> collections) IEnumerable<CollectionAccessSelection> collections)
: base(organizationUser, "organizationUserDetails") : base(organizationUser, "organizationUserDetails")
{ {
ManagedByOrganization = managedByOrganization; ClaimedByOrganization = claimedByOrganization;
Collections = collections.Select(c => new SelectionReadOnlyResponseModel(c)); Collections = collections.Select(c => new SelectionReadOnlyResponseModel(c));
} }
public OrganizationUserDetailsResponseModel(OrganizationUserUserDetails organizationUser, public OrganizationUserDetailsResponseModel(OrganizationUserUserDetails organizationUser,
bool managedByOrganization, bool claimedByOrganization,
IEnumerable<CollectionAccessSelection> collections) IEnumerable<CollectionAccessSelection> collections)
: base(organizationUser, "organizationUserDetails") : base(organizationUser, "organizationUserDetails")
{ {
ManagedByOrganization = managedByOrganization; ClaimedByOrganization = claimedByOrganization;
Collections = collections.Select(c => new SelectionReadOnlyResponseModel(c)); Collections = collections.Select(c => new SelectionReadOnlyResponseModel(c));
} }
public bool ManagedByOrganization { get; set; } [Obsolete("Please use ClaimedByOrganization instead. This property will be removed in a future version.")]
public bool ManagedByOrganization
{
get => ClaimedByOrganization;
set => ClaimedByOrganization = value;
}
public bool ClaimedByOrganization { get; set; }
public IEnumerable<SelectionReadOnlyResponseModel> Collections { get; set; } public IEnumerable<SelectionReadOnlyResponseModel> Collections { get; set; }
@ -117,7 +123,7 @@ public class OrganizationUserUserMiniDetailsResponseModel : ResponseModel
public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponseModel public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponseModel
{ {
public OrganizationUserUserDetailsResponseModel(OrganizationUserUserDetails organizationUser, public OrganizationUserUserDetailsResponseModel(OrganizationUserUserDetails organizationUser,
bool twoFactorEnabled, bool managedByOrganization, string obj = "organizationUserUserDetails") bool twoFactorEnabled, bool claimedByOrganization, string obj = "organizationUserUserDetails")
: base(organizationUser, obj) : base(organizationUser, obj)
{ {
if (organizationUser == null) if (organizationUser == null)
@ -134,7 +140,7 @@ public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponse
Groups = organizationUser.Groups; Groups = organizationUser.Groups;
// Prevent reset password when using key connector. // Prevent reset password when using key connector.
ResetPasswordEnrolled = ResetPasswordEnrolled && !organizationUser.UsesKeyConnector; ResetPasswordEnrolled = ResetPasswordEnrolled && !organizationUser.UsesKeyConnector;
ManagedByOrganization = managedByOrganization; ClaimedByOrganization = claimedByOrganization;
} }
public string Name { get; set; } public string Name { get; set; }
@ -142,11 +148,17 @@ public class OrganizationUserUserDetailsResponseModel : OrganizationUserResponse
public string AvatarColor { get; set; } public string AvatarColor { get; set; }
public bool TwoFactorEnabled { get; set; } public bool TwoFactorEnabled { get; set; }
public bool SsoBound { get; set; } public bool SsoBound { get; set; }
[Obsolete("Please use ClaimedByOrganization instead. This property will be removed in a future version.")]
public bool ManagedByOrganization
{
get => ClaimedByOrganization;
set => ClaimedByOrganization = value;
}
/// <summary> /// <summary>
/// Indicates if the organization manages the user. If a user is "managed" by an organization, /// Indicates if the organization claimed the user. If a user is "claimed" by an organization,
/// the organization has greater control over their account, and some user actions are restricted. /// the organization has greater control over their account, and some user actions are restricted.
/// </summary> /// </summary>
public bool ManagedByOrganization { get; set; } public bool ClaimedByOrganization { get; set; }
public IEnumerable<SelectionReadOnlyResponseModel> Collections { get; set; } public IEnumerable<SelectionReadOnlyResponseModel> Collections { get; set; }
public IEnumerable<Guid> Groups { get; set; } public IEnumerable<Guid> Groups { get; set; }
} }

View File

@ -18,7 +18,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
public ProfileOrganizationResponseModel( public ProfileOrganizationResponseModel(
OrganizationUserOrganizationDetails organization, OrganizationUserOrganizationDetails organization,
IEnumerable<Guid> organizationIdsManagingUser) IEnumerable<Guid> organizationIdsClaimingUser)
: this("profileOrganization") : this("profileOrganization")
{ {
Id = organization.OrganizationId; Id = organization.OrganizationId;
@ -51,7 +51,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId); SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId);
Identifier = organization.Identifier; Identifier = organization.Identifier;
Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organization.Permissions); Permissions = CoreHelpers.LoadClassFromJsonData<Permissions>(organization.Permissions);
ResetPasswordEnrolled = organization.ResetPasswordKey != null; ResetPasswordEnrolled = !string.IsNullOrWhiteSpace(organization.ResetPasswordKey);
UserId = organization.UserId; UserId = organization.UserId;
OrganizationUserId = organization.OrganizationUserId; OrganizationUserId = organization.OrganizationUserId;
ProviderId = organization.ProviderId; ProviderId = organization.ProviderId;
@ -70,7 +70,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
LimitCollectionDeletion = organization.LimitCollectionDeletion; LimitCollectionDeletion = organization.LimitCollectionDeletion;
LimitItemDeletion = organization.LimitItemDeletion; LimitItemDeletion = organization.LimitItemDeletion;
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems; AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
UserIsManagedByOrganization = organizationIdsManagingUser.Contains(organization.OrganizationId); UserIsClaimedByOrganization = organizationIdsClaimingUser.Contains(organization.OrganizationId);
UseRiskInsights = organization.UseRiskInsights; UseRiskInsights = organization.UseRiskInsights;
if (organization.SsoConfig != null) if (organization.SsoConfig != null)
@ -133,15 +133,26 @@ public class ProfileOrganizationResponseModel : ResponseModel
public bool LimitItemDeletion { get; set; } public bool LimitItemDeletion { get; set; }
public bool AllowAdminAccessToAllCollectionItems { get; set; } public bool AllowAdminAccessToAllCollectionItems { get; set; }
/// <summary> /// <summary>
/// Indicates if the organization manages the user. /// Obsolete.
///
/// See <see cref="UserIsClaimedByOrganization"/>
/// </summary>
[Obsolete("Please use UserIsClaimedByOrganization instead. This property will be removed in a future version.")]
public bool UserIsManagedByOrganization
{
get => UserIsClaimedByOrganization;
set => UserIsClaimedByOrganization = value;
}
/// <summary>
/// Indicates if the organization claims the user.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// An organization manages a user if the user's email domain is verified by the organization and the user is a member of it. /// An organization claims a user if the user's email domain is verified by the organization and the user is a member of it.
/// The organization must be enabled and able to have verified domains. /// The organization must be enabled and able to have verified domains.
/// </remarks> /// </remarks>
/// <returns> /// <returns>
/// False if the Account Deprovisioning feature flag is disabled. /// False if the Account Deprovisioning feature flag is disabled.
/// </returns> /// </returns>
public bool UserIsManagedByOrganization { get; set; } public bool UserIsClaimedByOrganization { get; set; }
public bool UseRiskInsights { get; set; } public bool UseRiskInsights { get; set; }
} }

View File

@ -30,7 +30,7 @@ public class MemberResponseModel : MemberBaseModel, IResponseModel
Email = user.Email; Email = user.Email;
Status = user.Status; Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c)); Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
ResetPasswordEnrolled = user.ResetPasswordKey != null; ResetPasswordEnrolled = !string.IsNullOrWhiteSpace(user.ResetPasswordKey);
} }
[SetsRequiredMembers] [SetsRequiredMembers]
@ -49,7 +49,7 @@ public class MemberResponseModel : MemberBaseModel, IResponseModel
TwoFactorEnabled = twoFactorEnabled; TwoFactorEnabled = twoFactorEnabled;
Status = user.Status; Status = user.Status;
Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c)); Collections = collections?.Select(c => new AssociationWithPermissionsResponseModel(c));
ResetPasswordEnrolled = user.ResetPasswordKey != null; ResetPasswordEnrolled = !string.IsNullOrWhiteSpace(user.ResetPasswordKey);
SsoExternalId = user.SsoExternalId; SsoExternalId = user.SsoExternalId;
} }

View File

@ -124,11 +124,11 @@ public class AccountsController : Controller
throw new BadRequestException("MasterPasswordHash", "Invalid password."); throw new BadRequestException("MasterPasswordHash", "Invalid password.");
} }
var managedUserValidationResult = await _userService.ValidateManagedUserDomainAsync(user, model.NewEmail); var claimedUserValidationResult = await _userService.ValidateClaimedUserDomainAsync(user, model.NewEmail);
if (!managedUserValidationResult.Succeeded) if (!claimedUserValidationResult.Succeeded)
{ {
throw new BadRequestException(managedUserValidationResult.Errors); throw new BadRequestException(claimedUserValidationResult.Errors);
} }
await _userService.InitiateEmailChangeAsync(user, model.NewEmail); await _userService.InitiateEmailChangeAsync(user, model.NewEmail);
@ -437,11 +437,11 @@ public class AccountsController : Controller
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user); var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user); var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id); var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
var response = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails, var response = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
providerUserOrganizationDetails, twoFactorEnabled, providerUserOrganizationDetails, twoFactorEnabled,
hasPremiumFromOrg, organizationIdsManagingActiveUser); hasPremiumFromOrg, organizationIdsClaimingActiveUser);
return response; return response;
} }
@ -451,9 +451,9 @@ public class AccountsController : Controller
var userId = _userService.GetProperUserId(User); var userId = _userService.GetProperUserId(User);
var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(userId.Value, var organizationUserDetails = await _organizationUserRepository.GetManyDetailsByUserAsync(userId.Value,
OrganizationUserStatusType.Confirmed); OrganizationUserStatusType.Confirmed);
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(userId.Value); var organizationIdsClaimingUser = await GetOrganizationIdsClaimingUserAsync(userId.Value);
var responseData = organizationUserDetails.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsManagingActiveUser)); var responseData = organizationUserDetails.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsClaimingUser));
return new ListResponseModel<ProfileOrganizationResponseModel>(responseData); return new ListResponseModel<ProfileOrganizationResponseModel>(responseData);
} }
@ -471,9 +471,9 @@ public class AccountsController : Controller
var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user); var twoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user); var hasPremiumFromOrg = await _userService.HasPremiumFromOrganization(user);
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id); var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
var response = new ProfileResponseModel(user, null, null, null, twoFactorEnabled, hasPremiumFromOrg, organizationIdsManagingActiveUser); var response = new ProfileResponseModel(user, null, null, null, twoFactorEnabled, hasPremiumFromOrg, organizationIdsClaimingActiveUser);
return response; return response;
} }
@ -490,9 +490,9 @@ public class AccountsController : Controller
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user); var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user); var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id); var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
var response = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsManagingActiveUser); var response = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsClaimingActiveUser);
return response; return response;
} }
@ -560,9 +560,9 @@ public class AccountsController : Controller
} }
else else
{ {
// If Account Deprovisioning is enabled, we need to check if the user is managed by any organization. // If Account Deprovisioning is enabled, we need to check if the user is claimed by any organization.
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
&& await _userService.IsManagedByAnyOrganizationAsync(user.Id)) && await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
{ {
throw new BadRequestException("Cannot delete accounts owned by an organization. Contact your organization administrator for additional details."); throw new BadRequestException("Cannot delete accounts owned by an organization. Contact your organization administrator for additional details.");
} }
@ -763,9 +763,9 @@ public class AccountsController : Controller
await _userService.SaveUserAsync(user); await _userService.SaveUserAsync(user);
} }
private async Task<IEnumerable<Guid>> GetOrganizationIdsManagingUserAsync(Guid userId) private async Task<IEnumerable<Guid>> GetOrganizationIdsClaimingUserAsync(Guid userId)
{ {
var organizationManagingUser = await _userService.GetOrganizationsManagingUserAsync(userId); var organizationsClaimingUser = await _userService.GetOrganizationsClaimingUserAsync(userId);
return organizationManagingUser.Select(o => o.Id); return organizationsClaimingUser.Select(o => o.Id);
} }
} }

View File

@ -58,10 +58,10 @@ public class AccountsController(
var userTwoFactorEnabled = await userService.TwoFactorIsEnabledAsync(user); var userTwoFactorEnabled = await userService.TwoFactorIsEnabledAsync(user);
var userHasPremiumFromOrganization = await userService.HasPremiumFromOrganization(user); var userHasPremiumFromOrganization = await userService.HasPremiumFromOrganization(user);
var organizationIdsManagingActiveUser = await GetOrganizationIdsManagingUserAsync(user.Id); var organizationIdsClaimingActiveUser = await GetOrganizationIdsClaimingUserAsync(user.Id);
var profile = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled, var profile = new ProfileResponseModel(user, null, null, null, userTwoFactorEnabled,
userHasPremiumFromOrganization, organizationIdsManagingActiveUser); userHasPremiumFromOrganization, organizationIdsClaimingActiveUser);
return new PaymentResponseModel return new PaymentResponseModel
{ {
UserProfile = profile, UserProfile = profile,
@ -229,9 +229,9 @@ public class AccountsController(
await paymentService.SaveTaxInfoAsync(user, taxInfo); await paymentService.SaveTaxInfoAsync(user, taxInfo);
} }
private async Task<IEnumerable<Guid>> GetOrganizationIdsManagingUserAsync(Guid userId) private async Task<IEnumerable<Guid>> GetOrganizationIdsClaimingUserAsync(Guid userId)
{ {
var organizationManagingUser = await userService.GetOrganizationsManagingUserAsync(userId); var organizationsClaimingUser = await userService.GetOrganizationsClaimingUserAsync(userId);
return organizationManagingUser.Select(o => o.Id); return organizationsClaimingUser.Select(o => o.Id);
} }
} }

View File

@ -409,9 +409,9 @@ public class OrganizationsController(
organizationId, organizationId,
OrganizationUserStatusType.Confirmed); OrganizationUserStatusType.Confirmed);
var organizationIdsManagingActiveUser = (await userService.GetOrganizationsManagingUserAsync(userId)) var organizationIdsClaimingActiveUser = (await userService.GetOrganizationsClaimingUserAsync(userId))
.Select(o => o.Id); .Select(o => o.Id);
return new ProfileOrganizationResponseModel(organizationUserDetails, organizationIdsManagingActiveUser); return new ProfileOrganizationResponseModel(organizationUserDetails, organizationIdsClaimingActiveUser);
} }
} }

View File

@ -15,7 +15,7 @@ public class ProfileResponseModel : ResponseModel
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails, IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
bool twoFactorEnabled, bool twoFactorEnabled,
bool premiumFromOrganization, bool premiumFromOrganization,
IEnumerable<Guid> organizationIdsManagingUser) : base("profile") IEnumerable<Guid> organizationIdsClaimingUser) : base("profile")
{ {
if (user == null) if (user == null)
{ {
@ -38,7 +38,7 @@ public class ProfileResponseModel : ResponseModel
AvatarColor = user.AvatarColor; AvatarColor = user.AvatarColor;
CreationDate = user.CreationDate; CreationDate = user.CreationDate;
VerifyDevices = user.VerifyDevices; VerifyDevices = user.VerifyDevices;
Organizations = organizationsUserDetails?.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsManagingUser)); Organizations = organizationsUserDetails?.Select(o => new ProfileOrganizationResponseModel(o, organizationIdsClaimingUser));
Providers = providerUserDetails?.Select(p => new ProfileProviderResponseModel(p)); Providers = providerUserDetails?.Select(p => new ProfileProviderResponseModel(p));
ProviderOrganizations = ProviderOrganizations =
providerUserOrganizationDetails?.Select(po => new ProfileProviderOrganizationResponseModel(po)); providerUserOrganizationDetails?.Select(po => new ProfileProviderOrganizationResponseModel(po));

View File

@ -1091,9 +1091,9 @@ public class CiphersController : Controller
throw new BadRequestException(ModelState); throw new BadRequestException(ModelState);
} }
// If Account Deprovisioning is enabled, we need to check if the user is managed by any organization. // If Account Deprovisioning is enabled, we need to check if the user is claimed by any organization.
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
&& await _userService.IsManagedByAnyOrganizationAsync(user.Id)) && await _userService.IsClaimedByAnyOrganizationAsync(user.Id))
{ {
throw new BadRequestException("Cannot purge accounts owned by an organization. Contact your organization administrator for additional details."); throw new BadRequestException("Cannot purge accounts owned by an organization. Contact your organization administrator for additional details.");
} }

View File

@ -104,13 +104,13 @@ public class SyncController : Controller
var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user); var userTwoFactorEnabled = await _userService.TwoFactorIsEnabledAsync(user);
var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user); var userHasPremiumFromOrganization = await _userService.HasPremiumFromOrganization(user);
var organizationManagingActiveUser = await _userService.GetOrganizationsManagingUserAsync(user.Id); var organizationClaimingActiveUser = await _userService.GetOrganizationsClaimingUserAsync(user.Id);
var organizationIdsManagingActiveUser = organizationManagingActiveUser.Select(o => o.Id); var organizationIdsClaimingActiveUser = organizationClaimingActiveUser.Select(o => o.Id);
var organizationAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync(); var organizationAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationAbilities, var response = new SyncResponseModel(_globalSettings, user, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationAbilities,
organizationIdsManagingActiveUser, organizationUserDetails, providerUserDetails, providerUserOrganizationDetails, organizationIdsClaimingActiveUser, organizationUserDetails, providerUserDetails, providerUserOrganizationDetails,
folders, collections, ciphers, collectionCiphersGroupDict, excludeDomains, policies, sends); folders, collections, ciphers, collectionCiphersGroupDict, excludeDomains, policies, sends);
return response; return response;
} }

View File

@ -23,7 +23,7 @@ public class SyncResponseModel : ResponseModel
bool userTwoFactorEnabled, bool userTwoFactorEnabled,
bool userHasPremiumFromOrganization, bool userHasPremiumFromOrganization,
IDictionary<Guid, OrganizationAbility> organizationAbilities, IDictionary<Guid, OrganizationAbility> organizationAbilities,
IEnumerable<Guid> organizationIdsManagingUser, IEnumerable<Guid> organizationIdsClaimingingUser,
IEnumerable<OrganizationUserOrganizationDetails> organizationUserDetails, IEnumerable<OrganizationUserOrganizationDetails> organizationUserDetails,
IEnumerable<ProviderUserProviderDetails> providerUserDetails, IEnumerable<ProviderUserProviderDetails> providerUserDetails,
IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails, IEnumerable<ProviderUserOrganizationDetails> providerUserOrganizationDetails,
@ -37,7 +37,7 @@ public class SyncResponseModel : ResponseModel
: base("sync") : base("sync")
{ {
Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails, Profile = new ProfileResponseModel(user, organizationUserDetails, providerUserDetails,
providerUserOrganizationDetails, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsManagingUser); providerUserOrganizationDetails, userTwoFactorEnabled, userHasPremiumFromOrganization, organizationIdsClaimingingUser);
Folders = folders.Select(f => new FolderResponseModel(f)); Folders = folders.Select(f => new FolderResponseModel(f));
Ciphers = ciphers.Select(cipher => Ciphers = ciphers.Select(cipher =>
new CipherDetailsResponseModel( new CipherDetailsResponseModel(

View File

@ -154,6 +154,6 @@ public class VerifyOrganizationDomainCommand(
var organization = await organizationRepository.GetByIdAsync(domain.OrganizationId); var organization = await organizationRepository.GetByIdAsync(domain.OrganizationId);
await mailService.SendClaimedDomainUserEmailAsync(new ManagedUserDomainClaimedEmails(domainUserEmails, organization)); await mailService.SendClaimedDomainUserEmailAsync(new ClaimedUserDomainClaimedEmails(domainUserEmails, organization));
} }
} }

View File

@ -15,11 +15,11 @@ using Bit.Core.Tools.Services;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganizationUserAccountCommand public class DeleteClaimedOrganizationUserAccountCommand : IDeleteClaimedOrganizationUserAccountCommand
{ {
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly IEventService _eventService; private readonly IEventService _eventService;
private readonly IGetOrganizationUsersManagementStatusQuery _getOrganizationUsersManagementStatusQuery; private readonly IGetOrganizationUsersClaimedStatusQuery _getOrganizationUsersClaimedStatusQuery;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IUserRepository _userRepository; private readonly IUserRepository _userRepository;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
@ -28,10 +28,10 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
private readonly IPushNotificationService _pushService; private readonly IPushNotificationService _pushService;
private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationRepository _organizationRepository;
private readonly IProviderUserRepository _providerUserRepository; private readonly IProviderUserRepository _providerUserRepository;
public DeleteManagedOrganizationUserAccountCommand( public DeleteClaimedOrganizationUserAccountCommand(
IUserService userService, IUserService userService,
IEventService eventService, IEventService eventService,
IGetOrganizationUsersManagementStatusQuery getOrganizationUsersManagementStatusQuery, IGetOrganizationUsersClaimedStatusQuery getOrganizationUsersClaimedStatusQuery,
IOrganizationUserRepository organizationUserRepository, IOrganizationUserRepository organizationUserRepository,
IUserRepository userRepository, IUserRepository userRepository,
ICurrentContext currentContext, ICurrentContext currentContext,
@ -43,7 +43,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
{ {
_userService = userService; _userService = userService;
_eventService = eventService; _eventService = eventService;
_getOrganizationUsersManagementStatusQuery = getOrganizationUsersManagementStatusQuery; _getOrganizationUsersClaimedStatusQuery = getOrganizationUsersClaimedStatusQuery;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
_userRepository = userRepository; _userRepository = userRepository;
_currentContext = currentContext; _currentContext = currentContext;
@ -62,10 +62,10 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
throw new NotFoundException("Member not found."); throw new NotFoundException("Member not found.");
} }
var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, new[] { organizationUserId }); var claimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, new[] { organizationUserId });
var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }, includeProvider: true); var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, new[] { organizationUserId }, includeProvider: true);
await ValidateDeleteUserAsync(organizationId, organizationUser, deletingUserId, managementStatus, hasOtherConfirmedOwners); await ValidateDeleteUserAsync(organizationId, organizationUser, deletingUserId, claimedStatus, hasOtherConfirmedOwners);
var user = await _userRepository.GetByIdAsync(organizationUser.UserId!.Value); var user = await _userRepository.GetByIdAsync(organizationUser.UserId!.Value);
if (user == null) if (user == null)
@ -83,7 +83,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
var userIds = orgUsers.Where(ou => ou.UserId.HasValue).Select(ou => ou.UserId!.Value).ToList(); var userIds = orgUsers.Where(ou => ou.UserId.HasValue).Select(ou => ou.UserId!.Value).ToList();
var users = await _userRepository.GetManyAsync(userIds); var users = await _userRepository.GetManyAsync(userIds);
var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, orgUserIds); var claimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, orgUserIds);
var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, orgUserIds, includeProvider: true); var hasOtherConfirmedOwners = await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, orgUserIds, includeProvider: true);
var results = new List<(Guid OrganizationUserId, string? ErrorMessage)>(); var results = new List<(Guid OrganizationUserId, string? ErrorMessage)>();
@ -97,7 +97,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
throw new NotFoundException("Member not found."); throw new NotFoundException("Member not found.");
} }
await ValidateDeleteUserAsync(organizationId, orgUser, deletingUserId, managementStatus, hasOtherConfirmedOwners); await ValidateDeleteUserAsync(organizationId, orgUser, deletingUserId, claimedStatus, hasOtherConfirmedOwners);
var user = users.FirstOrDefault(u => u.Id == orgUser.UserId); var user = users.FirstOrDefault(u => u.Id == orgUser.UserId);
if (user == null) if (user == null)
@ -129,7 +129,7 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
return results; return results;
} }
private async Task ValidateDeleteUserAsync(Guid organizationId, OrganizationUser orgUser, Guid? deletingUserId, IDictionary<Guid, bool> managementStatus, bool hasOtherConfirmedOwners) private async Task ValidateDeleteUserAsync(Guid organizationId, OrganizationUser orgUser, Guid? deletingUserId, IDictionary<Guid, bool> claimedStatus, bool hasOtherConfirmedOwners)
{ {
if (!orgUser.UserId.HasValue || orgUser.Status == OrganizationUserStatusType.Invited) if (!orgUser.UserId.HasValue || orgUser.Status == OrganizationUserStatusType.Invited)
{ {
@ -159,10 +159,9 @@ public class DeleteManagedOrganizationUserAccountCommand : IDeleteManagedOrganiz
throw new BadRequestException("Custom users can not delete admins."); throw new BadRequestException("Custom users can not delete admins.");
} }
if (!claimedStatus.TryGetValue(orgUser.Id, out var isClaimed) || !isClaimed)
if (!managementStatus.TryGetValue(orgUser.Id, out var isManaged) || !isManaged)
{ {
throw new BadRequestException("Member is not managed by the organization."); throw new BadRequestException("Member is not claimed by the organization.");
} }
} }

View File

@ -4,12 +4,12 @@ using Bit.Core.Services;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;
public class GetOrganizationUsersManagementStatusQuery : IGetOrganizationUsersManagementStatusQuery public class GetOrganizationUsersClaimedStatusQuery : IGetOrganizationUsersClaimedStatusQuery
{ {
private readonly IApplicationCacheService _applicationCacheService; private readonly IApplicationCacheService _applicationCacheService;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
public GetOrganizationUsersManagementStatusQuery( public GetOrganizationUsersClaimedStatusQuery(
IApplicationCacheService applicationCacheService, IApplicationCacheService applicationCacheService,
IOrganizationUserRepository organizationUserRepository) IOrganizationUserRepository organizationUserRepository)
{ {
@ -17,11 +17,11 @@ public class GetOrganizationUsersManagementStatusQuery : IGetOrganizationUsersMa
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
} }
public async Task<IDictionary<Guid, bool>> GetUsersOrganizationManagementStatusAsync(Guid organizationId, IEnumerable<Guid> organizationUserIds) public async Task<IDictionary<Guid, bool>> GetUsersOrganizationClaimedStatusAsync(Guid organizationId, IEnumerable<Guid> organizationUserIds)
{ {
if (organizationUserIds.Any()) if (organizationUserIds.Any())
{ {
// Users can only be managed by an Organization that is enabled and can have organization domains // Users can only be claimed by an Organization that is enabled and can have organization domains
var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(organizationId); var organizationAbility = await _applicationCacheService.GetOrganizationAbilityAsync(organizationId);
// TODO: Replace "UseSso" with a new organization ability like "UseOrganizationDomains" (PM-11622). // TODO: Replace "UseSso" with a new organization ability like "UseOrganizationDomains" (PM-11622).
@ -31,7 +31,7 @@ public class GetOrganizationUsersManagementStatusQuery : IGetOrganizationUsersMa
// Get all organization users with claimed domains by the organization // Get all organization users with claimed domains by the organization
var organizationUsersWithClaimedDomain = await _organizationUserRepository.GetManyByOrganizationWithClaimedDomainsAsync(organizationId); var organizationUsersWithClaimedDomain = await _organizationUserRepository.GetManyByOrganizationWithClaimedDomainsAsync(organizationId);
// Create a dictionary with the OrganizationUserId and a boolean indicating if the user is managed by the organization // Create a dictionary with the OrganizationUserId and a boolean indicating if the user is claimed by the organization
return organizationUserIds.ToDictionary(ouId => ouId, ouId => organizationUsersWithClaimedDomain.Any(ou => ou.Id == ouId)); return organizationUserIds.ToDictionary(ouId => ouId, ouId => organizationUsersWithClaimedDomain.Any(ou => ou.Id == ouId));
} }
} }

View File

@ -2,7 +2,7 @@
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
public interface IDeleteManagedOrganizationUserAccountCommand public interface IDeleteClaimedOrganizationUserAccountCommand
{ {
/// <summary> /// <summary>
/// Removes a user from an organization and deletes all of their associated user data. /// Removes a user from an organization and deletes all of their associated user data.

View File

@ -1,19 +1,19 @@
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
public interface IGetOrganizationUsersManagementStatusQuery public interface IGetOrganizationUsersClaimedStatusQuery
{ {
/// <summary> /// <summary>
/// Checks whether each user in the provided list of organization user IDs is managed by the specified organization. /// Checks whether each user in the provided list of organization user IDs is claimed by the specified organization.
/// </summary> /// </summary>
/// <param name="organizationId">The unique identifier of the organization to check against.</param> /// <param name="organizationId">The unique identifier of the organization to check against.</param>
/// <param name="organizationUserIds">A list of OrganizationUserIds to be checked.</param> /// <param name="organizationUserIds">A list of OrganizationUserIds to be checked.</param>
/// <remarks> /// <remarks>
/// A managed user is a user whose email domain matches one of the Organization's verified domains. /// A claimed user is a user whose email domain matches one of the Organization's verified domains.
/// The organization must be enabled and be on an Enterprise plan. /// The organization must be enabled and be on an Enterprise plan.
/// </remarks> /// </remarks>
/// <returns> /// <returns>
/// A dictionary containing the OrganizationUserId and a boolean indicating if the user is managed by the organization. /// A dictionary containing the OrganizationUserId and a boolean indicating if the user is claimed by the organization.
/// </returns> /// </returns>
Task<IDictionary<Guid, bool>> GetUsersOrganizationManagementStatusAsync(Guid organizationId, Task<IDictionary<Guid, bool>> GetUsersOrganizationClaimedStatusAsync(Guid organizationId,
IEnumerable<Guid> organizationUserIds); IEnumerable<Guid> organizationUserIds);
} }

View File

@ -18,7 +18,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
private readonly IPushRegistrationService _pushRegistrationService; private readonly IPushRegistrationService _pushRegistrationService;
private readonly ICurrentContext _currentContext; private readonly ICurrentContext _currentContext;
private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery; private readonly IHasConfirmedOwnersExceptQuery _hasConfirmedOwnersExceptQuery;
private readonly IGetOrganizationUsersManagementStatusQuery _getOrganizationUsersManagementStatusQuery; private readonly IGetOrganizationUsersClaimedStatusQuery _getOrganizationUsersClaimedStatusQuery;
private readonly IFeatureService _featureService; private readonly IFeatureService _featureService;
private readonly TimeProvider _timeProvider; private readonly TimeProvider _timeProvider;
@ -38,7 +38,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
IPushRegistrationService pushRegistrationService, IPushRegistrationService pushRegistrationService,
ICurrentContext currentContext, ICurrentContext currentContext,
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery, IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
IGetOrganizationUsersManagementStatusQuery getOrganizationUsersManagementStatusQuery, IGetOrganizationUsersClaimedStatusQuery getOrganizationUsersClaimedStatusQuery,
IFeatureService featureService, IFeatureService featureService,
TimeProvider timeProvider) TimeProvider timeProvider)
{ {
@ -49,7 +49,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
_pushRegistrationService = pushRegistrationService; _pushRegistrationService = pushRegistrationService;
_currentContext = currentContext; _currentContext = currentContext;
_hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery; _hasConfirmedOwnersExceptQuery = hasConfirmedOwnersExceptQuery;
_getOrganizationUsersManagementStatusQuery = getOrganizationUsersManagementStatusQuery; _getOrganizationUsersClaimedStatusQuery = getOrganizationUsersClaimedStatusQuery;
_featureService = featureService; _featureService = featureService;
_timeProvider = timeProvider; _timeProvider = timeProvider;
} }
@ -161,8 +161,8 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && deletingUserId.HasValue && eventSystemUser == null) if (_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && deletingUserId.HasValue && eventSystemUser == null)
{ {
var managementStatus = await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(orgUser.OrganizationId, new[] { orgUser.Id }); var claimedStatus = await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(orgUser.OrganizationId, new[] { orgUser.Id });
if (managementStatus.TryGetValue(orgUser.Id, out var isManaged) && isManaged) if (claimedStatus.TryGetValue(orgUser.Id, out var isClaimed) && isClaimed)
{ {
throw new BadRequestException(RemoveClaimedAccountErrorMessage); throw new BadRequestException(RemoveClaimedAccountErrorMessage);
} }
@ -214,8 +214,8 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId); deletingUserIsOwner = await _currentContext.OrganizationOwner(organizationId);
} }
var managementStatus = _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && deletingUserId.HasValue && eventSystemUser == null var claimedStatus = _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) && deletingUserId.HasValue && eventSystemUser == null
? await _getOrganizationUsersManagementStatusQuery.GetUsersOrganizationManagementStatusAsync(organizationId, filteredUsers.Select(u => u.Id)) ? await _getOrganizationUsersClaimedStatusQuery.GetUsersOrganizationClaimedStatusAsync(organizationId, filteredUsers.Select(u => u.Id))
: filteredUsers.ToDictionary(u => u.Id, u => false); : filteredUsers.ToDictionary(u => u.Id, u => false);
var result = new List<(OrganizationUser OrganizationUser, string ErrorMessage)>(); var result = new List<(OrganizationUser OrganizationUser, string ErrorMessage)>();
foreach (var orgUser in filteredUsers) foreach (var orgUser in filteredUsers)
@ -232,7 +232,7 @@ public class RemoveOrganizationUserCommand : IRemoveOrganizationUserCommand
throw new BadRequestException(RemoveOwnerByNonOwnerErrorMessage); throw new BadRequestException(RemoveOwnerByNonOwnerErrorMessage);
} }
if (managementStatus.TryGetValue(orgUser.Id, out var isManaged) && isManaged) if (claimedStatus.TryGetValue(orgUser.Id, out var isClaimed) && isClaimed)
{ {
throw new BadRequestException(RemoveClaimedAccountErrorMessage); throw new BadRequestException(RemoveClaimedAccountErrorMessage);
} }

View File

@ -2,6 +2,6 @@
namespace Bit.Core.Auth.Models.Mail; namespace Bit.Core.Auth.Models.Mail;
public class CannotDeleteManagedAccountViewModel : BaseMailModel public class CannotDeleteClaimedAccountViewModel : BaseMailModel
{ {
} }

View File

@ -53,23 +53,10 @@ public class SendVerificationEmailForRegistrationCommand : ISendVerificationEmai
var user = await _userRepository.GetByEmailAsync(email); var user = await _userRepository.GetByEmailAsync(email);
var userExists = user != null; var userExists = user != null;
// Delays enabled by default; flag must be enabled to remove the delays.
var delaysEnabled = !_featureService.IsEnabled(FeatureFlagKeys.EmailVerificationDisableTimingDelays);
if (!_globalSettings.EnableEmailVerification) if (!_globalSettings.EnableEmailVerification)
{ {
if (userExists) if (userExists)
{ {
if (delaysEnabled)
{
// Add delay to prevent timing attacks
// Note: sub 140 ms feels responsive to users so we are using a random value between 100 - 130 ms
// as it should be long enough to prevent timing attacks but not too long to be noticeable to the user.
await Task.Delay(Random.Shared.Next(100, 130));
}
throw new BadRequestException($"Email {email} is already taken"); throw new BadRequestException($"Email {email} is already taken");
} }
@ -87,11 +74,6 @@ public class SendVerificationEmailForRegistrationCommand : ISendVerificationEmai
await _mailService.SendRegistrationVerificationEmailAsync(email, token); await _mailService.SendRegistrationVerificationEmailAsync(email, token);
} }
if (delaysEnabled)
{
// Add random delay between 100ms-130ms to prevent timing attacks
await Task.Delay(Random.Shared.Next(100, 130));
}
// User exists but we will return a 200 regardless of whether the email was sent or not; so return null // User exists but we will return a 200 regardless of whether the email was sent or not; so return null
return null; return null;
} }

View File

@ -622,11 +622,8 @@ public class SubscriberService(
await stripeAdapter.TaxIdDeleteAsync(customer.Id, taxId.Id); await stripeAdapter.TaxIdDeleteAsync(customer.Id, taxId.Id);
} }
if (string.IsNullOrWhiteSpace(taxInformation.TaxId)) if (!string.IsNullOrWhiteSpace(taxInformation.TaxId))
{ {
return;
}
var taxIdType = taxInformation.TaxIdType; var taxIdType = taxInformation.TaxIdType;
if (string.IsNullOrWhiteSpace(taxIdType)) if (string.IsNullOrWhiteSpace(taxIdType))
{ {
@ -665,6 +662,7 @@ public class SubscriberService(
throw new Exceptions.BadRequestException("billingTaxIdCreationError"); throw new Exceptions.BadRequestException("billingTaxIdCreationError");
} }
} }
}
if (featureService.IsEnabled(FeatureFlagKeys.PM19147_AutomaticTaxImprovements)) if (featureService.IsEnabled(FeatureFlagKeys.PM19147_AutomaticTaxImprovements))
{ {

View File

@ -105,7 +105,6 @@ public static class FeatureFlagKeys
public const string AccountDeprovisioning = "pm-10308-account-deprovisioning"; public const string AccountDeprovisioning = "pm-10308-account-deprovisioning";
public const string VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint"; public const string VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint";
public const string LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission"; public const string LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission";
public const string PushSyncOrgKeysOnRevokeRestore = "pm-17168-push-sync-org-keys-on-revoke-restore";
public const string PolicyRequirements = "pm-14439-policy-requirements"; public const string PolicyRequirements = "pm-14439-policy-requirements";
public const string SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility"; public const string SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility";
public const string ScimInviteUserOptimization = "pm-16811-optimize-invite-user-flow-to-fail-fast"; public const string ScimInviteUserOptimization = "pm-16811-optimize-invite-user-flow-to-fail-fast";
@ -115,7 +114,6 @@ public static class FeatureFlagKeys
public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence"; public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence";
public const string DuoRedirect = "duo-redirect"; public const string DuoRedirect = "duo-redirect";
public const string EmailVerification = "email-verification"; public const string EmailVerification = "email-verification";
public const string EmailVerificationDisableTimingDelays = "email-verification-disable-timing-delays";
public const string DeviceTrustLogging = "pm-8285-device-trust-logging"; public const string DeviceTrustLogging = "pm-8285-device-trust-logging";
public const string AuthenticatorTwoFactorToken = "authenticator-2fa-token"; public const string AuthenticatorTwoFactorToken = "authenticator-2fa-token";
public const string UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh"; public const string UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh";

View File

@ -4,9 +4,13 @@
// EncryptedStringAttribute // EncryptedStringAttribute
public enum EncryptionType : byte public enum EncryptionType : byte
{ {
// symmetric
AesCbc256_B64 = 0, AesCbc256_B64 = 0,
AesCbc128_HmacSha256_B64 = 1, AesCbc128_HmacSha256_B64 = 1,
AesCbc256_HmacSha256_B64 = 2, AesCbc256_HmacSha256_B64 = 2,
XChaCha20Poly1305_B64 = 7,
// asymmetric
Rsa2048_OaepSha256_B64 = 3, Rsa2048_OaepSha256_B64 = 3,
Rsa2048_OaepSha1_B64 = 4, Rsa2048_OaepSha1_B64 = 4,
Rsa2048_OaepSha256_HmacSha256_B64 = 5, Rsa2048_OaepSha256_HmacSha256_B64 = 5,

View File

@ -2,4 +2,4 @@
namespace Bit.Core.Models.Data.Organizations; namespace Bit.Core.Models.Data.Organizations;
public record ManagedUserDomainClaimedEmails(IEnumerable<string> EmailList, Organization Organization); public record ClaimedUserDomainClaimedEmails(IEnumerable<string> EmailList, Organization Organization);

View File

@ -121,7 +121,7 @@ public static class OrganizationServiceCollectionExtensions
services.AddScoped<IRevokeNonCompliantOrganizationUserCommand, RevokeNonCompliantOrganizationUserCommand>(); services.AddScoped<IRevokeNonCompliantOrganizationUserCommand, RevokeNonCompliantOrganizationUserCommand>();
services.AddScoped<IUpdateOrganizationUserCommand, UpdateOrganizationUserCommand>(); services.AddScoped<IUpdateOrganizationUserCommand, UpdateOrganizationUserCommand>();
services.AddScoped<IUpdateOrganizationUserGroupsCommand, UpdateOrganizationUserGroupsCommand>(); services.AddScoped<IUpdateOrganizationUserGroupsCommand, UpdateOrganizationUserGroupsCommand>();
services.AddScoped<IDeleteManagedOrganizationUserAccountCommand, DeleteManagedOrganizationUserAccountCommand>(); services.AddScoped<IDeleteClaimedOrganizationUserAccountCommand, DeleteClaimedOrganizationUserAccountCommand>();
services.AddScoped<IConfirmOrganizationUserCommand, ConfirmOrganizationUserCommand>(); services.AddScoped<IConfirmOrganizationUserCommand, ConfirmOrganizationUserCommand>();
} }
@ -172,7 +172,7 @@ public static class OrganizationServiceCollectionExtensions
services.AddScoped<ICountNewSmSeatsRequiredQuery, CountNewSmSeatsRequiredQuery>(); services.AddScoped<ICountNewSmSeatsRequiredQuery, CountNewSmSeatsRequiredQuery>();
services.AddScoped<IAcceptOrgUserCommand, AcceptOrgUserCommand>(); services.AddScoped<IAcceptOrgUserCommand, AcceptOrgUserCommand>();
services.AddScoped<IOrganizationUserUserDetailsQuery, OrganizationUserUserDetailsQuery>(); services.AddScoped<IOrganizationUserUserDetailsQuery, OrganizationUserUserDetailsQuery>();
services.AddScoped<IGetOrganizationUsersManagementStatusQuery, GetOrganizationUsersManagementStatusQuery>(); services.AddScoped<IGetOrganizationUsersClaimedStatusQuery, GetOrganizationUsersClaimedStatusQuery>();
services.AddScoped<IRestoreOrganizationUserCommand, RestoreOrganizationUserCommand>(); services.AddScoped<IRestoreOrganizationUserCommand, RestoreOrganizationUserCommand>();

View File

@ -53,6 +53,10 @@ public sealed class X509ChainOptions
return false; return false;
} }
// Do this outside of the callback so that we aren't opening the root store every request.
using var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine, OpenFlags.ReadOnly);
var rootCertificates = store.Certificates;
// Ref: https://github.com/dotnet/runtime/issues/39835#issuecomment-663020581 // Ref: https://github.com/dotnet/runtime/issues/39835#issuecomment-663020581
callback = (certificate, chain, errors) => callback = (certificate, chain, errors) =>
{ {
@ -62,6 +66,10 @@ public sealed class X509ChainOptions
} }
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
// We want our additional certificates to be in addition to the machines root store.
chain.ChainPolicy.CustomTrustStore.AddRange(rootCertificates);
foreach (var additionalCertificate in AdditionalCustomTrustCertificates) foreach (var additionalCertificate in AdditionalCustomTrustCertificates)
{ {
chain.ChainPolicy.CustomTrustStore.Add(additionalCertificate); chain.ChainPolicy.CustomTrustStore.Add(additionalCertificate);

View File

@ -21,7 +21,7 @@ public interface IMailService
ProductTierType productTier, ProductTierType productTier,
IEnumerable<ProductType> products); IEnumerable<ProductType> products);
Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token); Task SendVerifyDeleteEmailAsync(string email, Guid userId, string token);
Task SendCannotDeleteManagedAccountEmailAsync(string email); Task SendCannotDeleteClaimedAccountEmailAsync(string email);
Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail); Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail);
Task SendChangeEmailEmailAsync(string newEmailAddress, string token); Task SendChangeEmailEmailAsync(string newEmailAddress, string token);
Task SendTwoFactorEmailAsync(string email, string accountEmail, string token, string deviceIp, string deviceType, bool authentication = true); Task SendTwoFactorEmailAsync(string email, string accountEmail, string token, string deviceIp, string deviceType, bool authentication = true);
@ -97,7 +97,7 @@ public interface IMailService
Task SendRequestSMAccessToAdminEmailAsync(IEnumerable<string> adminEmails, string organizationName, string userRequestingAccess, string emailContent); Task SendRequestSMAccessToAdminEmailAsync(IEnumerable<string> adminEmails, string organizationName, string userRequestingAccess, string emailContent);
Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string email, string offerAcceptanceDate, string organizationId, Task SendFamiliesForEnterpriseRemoveSponsorshipsEmailAsync(string email, string offerAcceptanceDate, string organizationId,
string organizationName); string organizationName);
Task SendClaimedDomainUserEmailAsync(ManagedUserDomainClaimedEmails emailList); Task SendClaimedDomainUserEmailAsync(ClaimedUserDomainClaimedEmails emailList);
Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName); Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName);
Task SendBulkSecurityTaskNotificationsAsync(Organization org, IEnumerable<UserSecurityTasksCount> securityTaskNotifications, IEnumerable<string> adminOwnerEmails); Task SendBulkSecurityTaskNotificationsAsync(Organization org, IEnumerable<UserSecurityTasksCount> securityTaskNotifications, IEnumerable<string> adminOwnerEmails);
} }

View File

@ -134,7 +134,7 @@ public interface IUserService
/// <returns> /// <returns>
/// False if the Account Deprovisioning feature flag is disabled. /// False if the Account Deprovisioning feature flag is disabled.
/// </returns> /// </returns>
Task<bool> IsManagedByAnyOrganizationAsync(Guid userId); Task<bool> IsClaimedByAnyOrganizationAsync(Guid userId);
/// <summary> /// <summary>
/// Verify whether the new email domain meets the requirements for managed users. /// Verify whether the new email domain meets the requirements for managed users.
@ -144,7 +144,7 @@ public interface IUserService
/// <returns> /// <returns>
/// IdentityResult /// IdentityResult
/// </returns> /// </returns>
Task<IdentityResult> ValidateManagedUserDomainAsync(User user, string newEmail); Task<IdentityResult> ValidateClaimedUserDomainAsync(User user, string newEmail);
/// <summary> /// <summary>
/// Gets the organizations that manage the user. /// Gets the organizations that manage the user.
@ -152,6 +152,6 @@ public interface IUserService
/// <returns> /// <returns>
/// An empty collection if the Account Deprovisioning feature flag is disabled. /// An empty collection if the Account Deprovisioning feature flag is disabled.
/// </returns> /// </returns>
/// <inheritdoc cref="IsManagedByAnyOrganizationAsync(Guid)"/> /// <inheritdoc cref="IsClaimedByAnyOrganizationAsync"/>
Task<IEnumerable<Organization>> GetOrganizationsManagingUserAsync(Guid userId); Task<IEnumerable<Organization>> GetOrganizationsClaimingUserAsync(Guid userId);
} }

View File

@ -117,16 +117,16 @@ public class HandlebarsMailService : IMailService
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
public async Task SendCannotDeleteManagedAccountEmailAsync(string email) public async Task SendCannotDeleteClaimedAccountEmailAsync(string email)
{ {
var message = CreateDefaultMessage("Delete Your Account", email); var message = CreateDefaultMessage("Delete Your Account", email);
var model = new CannotDeleteManagedAccountViewModel var model = new CannotDeleteClaimedAccountViewModel
{ {
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash, WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
SiteName = _globalSettings.SiteName, SiteName = _globalSettings.SiteName,
}; };
await AddMessageContentAsync(message, "AdminConsole.CannotDeleteManagedAccount", model); await AddMessageContentAsync(message, "AdminConsole.CannotDeleteClaimedAccount", model);
message.Category = "CannotDeleteManagedAccount"; message.Category = "CannotDeleteClaimedAccount";
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
@ -474,7 +474,7 @@ public class HandlebarsMailService : IMailService
await _mailDeliveryService.SendEmailAsync(message); await _mailDeliveryService.SendEmailAsync(message);
} }
public async Task SendClaimedDomainUserEmailAsync(ManagedUserDomainClaimedEmails emailList) public async Task SendClaimedDomainUserEmailAsync(ClaimedUserDomainClaimedEmails emailList)
{ {
await EnqueueMailAsync(emailList.EmailList.Select(email => await EnqueueMailAsync(emailList.EmailList.Select(email =>
CreateMessage(email, emailList.Organization))); CreateMessage(email, emailList.Organization)));
@ -804,12 +804,10 @@ public class HandlebarsMailService : IMailService
return; return;
} }
var numeric = parameters[0]; if (int.TryParse(parameters[0].ToString(), out var number))
{
var singularText = parameters[1].ToString(); var singularText = parameters[1].ToString();
var pluralText = parameters[2].ToString(); var pluralText = parameters[2].ToString();
if (numeric is int number)
{
writer.WriteSafeString(number == 1 ? singularText : pluralText); writer.WriteSafeString(number == 1 ? singularText : pluralText);
} }
else else

View File

@ -314,9 +314,9 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return; return;
} }
if (await IsManagedByAnyOrganizationAsync(user.Id)) if (await IsClaimedByAnyOrganizationAsync(user.Id))
{ {
await _mailService.SendCannotDeleteManagedAccountEmailAsync(user.Email); await _mailService.SendCannotDeleteClaimedAccountEmailAsync(user.Email);
return; return;
} }
@ -545,11 +545,11 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch()); return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch());
} }
var managedUserValidationResult = await ValidateManagedUserDomainAsync(user, newEmail); var claimedUserValidationResult = await ValidateClaimedUserDomainAsync(user, newEmail);
if (!managedUserValidationResult.Succeeded) if (!claimedUserValidationResult.Succeeded)
{ {
return managedUserValidationResult; return claimedUserValidationResult;
} }
if (!await base.VerifyUserTokenAsync(user, _identityOptions.Tokens.ChangeEmailTokenProvider, if (!await base.VerifyUserTokenAsync(user, _identityOptions.Tokens.ChangeEmailTokenProvider,
@ -617,18 +617,18 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return IdentityResult.Success; return IdentityResult.Success;
} }
public async Task<IdentityResult> ValidateManagedUserDomainAsync(User user, string newEmail) public async Task<IdentityResult> ValidateClaimedUserDomainAsync(User user, string newEmail)
{ {
var managingOrganizations = await GetOrganizationsManagingUserAsync(user.Id); var claimingOrganization = await GetOrganizationsClaimingUserAsync(user.Id);
if (!managingOrganizations.Any()) if (!claimingOrganization.Any())
{ {
return IdentityResult.Success; return IdentityResult.Success;
} }
var newDomain = CoreHelpers.GetEmailDomain(newEmail); var newDomain = CoreHelpers.GetEmailDomain(newEmail);
var verifiedDomains = await _organizationDomainRepository.GetVerifiedDomainsByOrganizationIdsAsync(managingOrganizations.Select(org => org.Id)); var verifiedDomains = await _organizationDomainRepository.GetVerifiedDomainsByOrganizationIdsAsync(claimingOrganization.Select(org => org.Id));
if (verifiedDomains.Any(verifiedDomain => verifiedDomain.DomainName == newDomain)) if (verifiedDomains.Any(verifiedDomain => verifiedDomain.DomainName == newDomain))
{ {
@ -1366,13 +1366,13 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return IsLegacyUser(user); return IsLegacyUser(user);
} }
public async Task<bool> IsManagedByAnyOrganizationAsync(Guid userId) public async Task<bool> IsClaimedByAnyOrganizationAsync(Guid userId)
{ {
var managingOrganizations = await GetOrganizationsManagingUserAsync(userId); var organizationsClaimingUser = await GetOrganizationsClaimingUserAsync(userId);
return managingOrganizations.Any(); return organizationsClaimingUser.Any();
} }
public async Task<IEnumerable<Organization>> GetOrganizationsManagingUserAsync(Guid userId) public async Task<IEnumerable<Organization>> GetOrganizationsClaimingUserAsync(Guid userId)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning)) if (!_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning))
{ {

View File

@ -103,7 +103,7 @@ public class NoopMailService : IMailService
return Task.FromResult(0); return Task.FromResult(0);
} }
public Task SendCannotDeleteManagedAccountEmailAsync(string email) public Task SendCannotDeleteClaimedAccountEmailAsync(string email)
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }
@ -317,7 +317,7 @@ public class NoopMailService : IMailService
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }
public Task SendClaimedDomainUserEmailAsync(ManagedUserDomainClaimedEmails emailList) => Task.CompletedTask; public Task SendClaimedDomainUserEmailAsync(ClaimedUserDomainClaimedEmails emailList) => Task.CompletedTask;
public Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName) public Task SendDeviceApprovalRequestedNotificationEmailAsync(IEnumerable<string> adminEmails, Guid organizationId, string email, string userName)
{ {

View File

@ -16,6 +16,7 @@ public class EncryptedStringAttribute : ValidationAttribute
[EncryptionType.AesCbc256_B64] = 2, // iv|ct [EncryptionType.AesCbc256_B64] = 2, // iv|ct
[EncryptionType.AesCbc128_HmacSha256_B64] = 3, // iv|ct|mac [EncryptionType.AesCbc128_HmacSha256_B64] = 3, // iv|ct|mac
[EncryptionType.AesCbc256_HmacSha256_B64] = 3, // iv|ct|mac [EncryptionType.AesCbc256_HmacSha256_B64] = 3, // iv|ct|mac
[EncryptionType.XChaCha20Poly1305_B64] = 1, // cose bytes
[EncryptionType.Rsa2048_OaepSha256_B64] = 1, // rsaCt [EncryptionType.Rsa2048_OaepSha256_B64] = 1, // rsaCt
[EncryptionType.Rsa2048_OaepSha1_B64] = 1, // rsaCt [EncryptionType.Rsa2048_OaepSha1_B64] = 1, // rsaCt
[EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64] = 2, // rsaCt|mac [EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64] = 2, // rsaCt|mac

View File

@ -121,8 +121,7 @@ public class AccountsController : Controller
var user = model.ToUser(); var user = model.ToUser();
var identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash, var identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash,
model.Token, model.OrganizationUserId); model.Token, model.OrganizationUserId);
// delaysEnabled false is only for the new registration with email verification process return ProcessRegistrationResult(identityResult, user);
return await ProcessRegistrationResult(identityResult, user, delaysEnabled: true);
} }
[HttpPost("register/send-verification-email")] [HttpPost("register/send-verification-email")]
@ -188,7 +187,6 @@ public class AccountsController : Controller
// Users will either have an emailed token or an email verification token - not both. // Users will either have an emailed token or an email verification token - not both.
IdentityResult identityResult = null; IdentityResult identityResult = null;
var delaysEnabled = !_featureService.IsEnabled(FeatureFlagKeys.EmailVerificationDisableTimingDelays);
switch (model.GetTokenType()) switch (model.GetTokenType())
{ {
@ -197,32 +195,32 @@ public class AccountsController : Controller
await _registerUserCommand.RegisterUserViaEmailVerificationToken(user, model.MasterPasswordHash, await _registerUserCommand.RegisterUserViaEmailVerificationToken(user, model.MasterPasswordHash,
model.EmailVerificationToken); model.EmailVerificationToken);
return await ProcessRegistrationResult(identityResult, user, delaysEnabled); return ProcessRegistrationResult(identityResult, user);
break; break;
case RegisterFinishTokenType.OrganizationInvite: case RegisterFinishTokenType.OrganizationInvite:
identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash, identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash,
model.OrgInviteToken, model.OrganizationUserId); model.OrgInviteToken, model.OrganizationUserId);
return await ProcessRegistrationResult(identityResult, user, delaysEnabled); return ProcessRegistrationResult(identityResult, user);
break; break;
case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan:
identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, model.MasterPasswordHash, model.OrgSponsoredFreeFamilyPlanToken); identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, model.MasterPasswordHash, model.OrgSponsoredFreeFamilyPlanToken);
return await ProcessRegistrationResult(identityResult, user, delaysEnabled); return ProcessRegistrationResult(identityResult, user);
break; break;
case RegisterFinishTokenType.EmergencyAccessInvite: case RegisterFinishTokenType.EmergencyAccessInvite:
Debug.Assert(model.AcceptEmergencyAccessId.HasValue); Debug.Assert(model.AcceptEmergencyAccessId.HasValue);
identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken(user, model.MasterPasswordHash, identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken(user, model.MasterPasswordHash,
model.AcceptEmergencyAccessInviteToken, model.AcceptEmergencyAccessId.Value); model.AcceptEmergencyAccessInviteToken, model.AcceptEmergencyAccessId.Value);
return await ProcessRegistrationResult(identityResult, user, delaysEnabled); return ProcessRegistrationResult(identityResult, user);
break; break;
case RegisterFinishTokenType.ProviderInvite: case RegisterFinishTokenType.ProviderInvite:
Debug.Assert(model.ProviderUserId.HasValue); Debug.Assert(model.ProviderUserId.HasValue);
identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken(user, model.MasterPasswordHash, identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken(user, model.MasterPasswordHash,
model.ProviderInviteToken, model.ProviderUserId.Value); model.ProviderInviteToken, model.ProviderUserId.Value);
return await ProcessRegistrationResult(identityResult, user, delaysEnabled); return ProcessRegistrationResult(identityResult, user);
break; break;
default: default:
@ -230,7 +228,7 @@ public class AccountsController : Controller
} }
} }
private async Task<RegisterResponseModel> ProcessRegistrationResult(IdentityResult result, User user, bool delaysEnabled) private RegisterResponseModel ProcessRegistrationResult(IdentityResult result, User user)
{ {
if (result.Succeeded) if (result.Succeeded)
{ {
@ -243,10 +241,6 @@ public class AccountsController : Controller
ModelState.AddModelError(string.Empty, error.Description); ModelState.AddModelError(string.Empty, error.Description);
} }
if (delaysEnabled)
{
await Task.Delay(Random.Shared.Next(100, 130));
}
throw new BadRequestException(ModelState); throw new BadRequestException(ModelState);
} }

View File

@ -260,14 +260,15 @@ public class OrganizationUsersControllerTests
.GetDetailsByIdWithCollectionsAsync(organizationUser.Id) .GetDetailsByIdWithCollectionsAsync(organizationUser.Id)
.Returns((organizationUser, collections)); .Returns((organizationUser, collections));
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(organizationUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id))) .GetUsersOrganizationClaimedStatusAsync(organizationUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)))
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } }); .Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } });
var response = await sutProvider.Sut.Get(organizationUser.Id, false); var response = await sutProvider.Sut.Get(organizationUser.Id, false);
Assert.Equal(organizationUser.Id, response.Id); Assert.Equal(organizationUser.Id, response.Id);
Assert.Equal(accountDeprovisioningEnabled, response.ManagedByOrganization); Assert.Equal(accountDeprovisioningEnabled, response.ManagedByOrganization);
Assert.Equal(accountDeprovisioningEnabled, response.ClaimedByOrganization);
} }
[Theory] [Theory]
@ -331,7 +332,7 @@ public class OrganizationUsersControllerTests
await sutProvider.Sut.DeleteAccount(orgId, id); await sutProvider.Sut.DeleteAccount(orgId, id);
await sutProvider.GetDependency<IDeleteManagedOrganizationUserAccountCommand>() await sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountCommand>()
.Received(1) .Received(1)
.DeleteUserAsync(orgId, id, currentUser.Id); .DeleteUserAsync(orgId, id, currentUser.Id);
} }
@ -367,7 +368,7 @@ public class OrganizationUsersControllerTests
{ {
sutProvider.GetDependency<ICurrentContext>().ManageUsers(orgId).Returns(true); sutProvider.GetDependency<ICurrentContext>().ManageUsers(orgId).Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(currentUser); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(currentUser);
sutProvider.GetDependency<IDeleteManagedOrganizationUserAccountCommand>() sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountCommand>()
.DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id) .DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id)
.Returns(deleteResults); .Returns(deleteResults);
@ -375,7 +376,7 @@ public class OrganizationUsersControllerTests
Assert.Equal(deleteResults.Count, response.Data.Count()); Assert.Equal(deleteResults.Count, response.Data.Count());
Assert.True(response.Data.All(r => deleteResults.Any(res => res.Item1 == r.Id && res.Item2 == r.Error))); Assert.True(response.Data.All(r => deleteResults.Any(res => res.Item1 == r.Id && res.Item2 == r.Error)));
await sutProvider.GetDependency<IDeleteManagedOrganizationUserAccountCommand>() await sutProvider.GetDependency<IDeleteClaimedOrganizationUserAccountCommand>()
.Received(1) .Received(1)
.DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id); .DeleteManyUsersAsync(orgId, model.Ids, currentUser.Id);
} }

View File

@ -138,7 +138,7 @@ public class OrganizationsControllerTests : IDisposable
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig); _ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user); _userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
_userService.GetOrganizationsManagingUserAsync(user.Id).Returns(new List<Organization> { null }); _userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization> { null });
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId)); var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId));
Assert.Contains("Your organization's Single Sign-On settings prevent you from leaving.", Assert.Contains("Your organization's Single Sign-On settings prevent you from leaving.",
@ -168,10 +168,10 @@ public class OrganizationsControllerTests : IDisposable
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig); _ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user); _userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
_userService.GetOrganizationsManagingUserAsync(user.Id).Returns(new List<Organization> { { foundOrg } }); _userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization> { { foundOrg } });
var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId)); var exception = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Leave(orgId));
Assert.Contains("Managed user account cannot leave managing organization. Contact your organization administrator for additional details.", Assert.Contains("Claimed user account cannot leave claiming organization. Contact your organization administrator for additional details.",
exception.Message); exception.Message);
await _removeOrganizationUserCommand.DidNotReceiveWithAnyArgs().RemoveUserAsync(default, default); await _removeOrganizationUserCommand.DidNotReceiveWithAnyArgs().RemoveUserAsync(default, default);
@ -203,7 +203,7 @@ public class OrganizationsControllerTests : IDisposable
_ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig); _ssoConfigRepository.GetByOrganizationIdAsync(orgId).Returns(ssoConfig);
_userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user); _userService.GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
_userService.GetOrganizationsManagingUserAsync(user.Id).Returns(new List<Organization>()); _userService.GetOrganizationsClaimingUserAsync(user.Id).Returns(new List<Organization>());
await _sut.Leave(orgId); await _sut.Leave(orgId);

View File

@ -25,11 +25,16 @@ public class MemberResponseModelTests
Assert.True(sut.ResetPasswordEnrolled); Assert.True(sut.ResetPasswordEnrolled);
} }
[Fact] [Theory]
public void ResetPasswordEnrolled_ShouldBeFalse_WhenUserDoesNotHaveResetPasswordKey() [InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void ResetPasswordEnrolled_ShouldBeFalse_WhenResetPasswordKeyIsInvalid(string? resetPasswordKey)
{ {
// Arrange // Arrange
var user = Substitute.For<OrganizationUser>(); var user = Substitute.For<OrganizationUser>();
user.ResetPasswordKey = resetPasswordKey;
var collections = Substitute.For<IEnumerable<CollectionAccessSelection>>(); var collections = Substitute.For<IEnumerable<CollectionAccessSelection>>();
// Act // Act

View File

@ -120,7 +120,7 @@ public class AccountsControllerTests : IDisposable
ConfigureUserServiceToReturnValidPrincipalFor(user); ConfigureUserServiceToReturnValidPrincipalFor(user);
ConfigureUserServiceToAcceptPasswordFor(user); ConfigureUserServiceToAcceptPasswordFor(user);
const string newEmail = "example@user.com"; const string newEmail = "example@user.com";
_userService.ValidateManagedUserDomainAsync(user, newEmail).Returns(IdentityResult.Success); _userService.ValidateClaimedUserDomainAsync(user, newEmail).Returns(IdentityResult.Success);
// Act // Act
await _sut.PostEmailToken(new EmailTokenRequestModel { NewEmail = newEmail }); await _sut.PostEmailToken(new EmailTokenRequestModel { NewEmail = newEmail });
@ -130,7 +130,7 @@ public class AccountsControllerTests : IDisposable
} }
[Fact] [Fact]
public async Task PostEmailToken_WhenValidateManagedUserDomainAsyncFails_ShouldReturnError() public async Task PostEmailToken_WhenValidateClaimedUserDomainAsyncFails_ShouldReturnError()
{ {
// Arrange // Arrange
var user = GenerateExampleUser(); var user = GenerateExampleUser();
@ -139,7 +139,7 @@ public class AccountsControllerTests : IDisposable
const string newEmail = "example@user.com"; const string newEmail = "example@user.com";
_userService.ValidateManagedUserDomainAsync(user, newEmail) _userService.ValidateClaimedUserDomainAsync(user, newEmail)
.Returns(IdentityResult.Failed(new IdentityError .Returns(IdentityResult.Failed(new IdentityError
{ {
Code = "TestFailure", Code = "TestFailure",
@ -197,7 +197,7 @@ public class AccountsControllerTests : IDisposable
_userService.ChangeEmailAsync(user, default, default, default, default, default) _userService.ChangeEmailAsync(user, default, default, default, default, default)
.Returns(Task.FromResult(IdentityResult.Success)); .Returns(Task.FromResult(IdentityResult.Success));
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
_userService.IsManagedByAnyOrganizationAsync(user.Id).Returns(false); _userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(false);
await _sut.PostEmail(new EmailRequestModel()); await _sut.PostEmail(new EmailRequestModel());
@ -539,7 +539,7 @@ public class AccountsControllerTests : IDisposable
ConfigureUserServiceToReturnValidPrincipalFor(user); ConfigureUserServiceToReturnValidPrincipalFor(user);
ConfigureUserServiceToAcceptPasswordFor(user); ConfigureUserServiceToAcceptPasswordFor(user);
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
_userService.IsManagedByAnyOrganizationAsync(user.Id).Returns(true); _userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(true);
var result = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Delete(new SecretVerificationRequestModel())); var result = await Assert.ThrowsAsync<BadRequestException>(() => _sut.Delete(new SecretVerificationRequestModel()));
@ -553,7 +553,7 @@ public class AccountsControllerTests : IDisposable
ConfigureUserServiceToReturnValidPrincipalFor(user); ConfigureUserServiceToReturnValidPrincipalFor(user);
ConfigureUserServiceToAcceptPasswordFor(user); ConfigureUserServiceToAcceptPasswordFor(user);
_featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true); _featureService.IsEnabled(FeatureFlagKeys.AccountDeprovisioning).Returns(true);
_userService.IsManagedByAnyOrganizationAsync(user.Id).Returns(false); _userService.IsClaimedByAnyOrganizationAsync(user.Id).Returns(false);
_userService.DeleteAsync(user).Returns(IdentityResult.Success); _userService.DeleteAsync(user).Returns(IdentityResult.Success);
await _sut.Delete(new SecretVerificationRequestModel()); await _sut.Delete(new SecretVerificationRequestModel());

View File

@ -317,7 +317,7 @@ public class VerifyOrganizationDomainCommandTests
_ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain); _ = await sutProvider.Sut.UserVerifyOrganizationDomainAsync(domain);
await sutProvider.GetDependency<IMailService>().Received().SendClaimedDomainUserEmailAsync( await sutProvider.GetDependency<IMailService>().Received().SendClaimedDomainUserEmailAsync(
Arg.Is<ManagedUserDomainClaimedEmails>(x => Arg.Is<ClaimedUserDomainClaimedEmails>(x =>
x.EmailList.Count(e => e.EndsWith(domain.DomainName)) == mockedUsers.Count && x.EmailList.Count(e => e.EndsWith(domain.DomainName)) == mockedUsers.Count &&
x.Organization.Id == organization.Id)); x.Organization.Id == organization.Id));
} }

View File

@ -15,12 +15,12 @@ using Xunit;
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers; namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers;
[SutProviderCustomize] [SutProviderCustomize]
public class DeleteManagedOrganizationUserAccountCommandTests public class DeleteClaimedOrganizationUserAccountCommandTests
{ {
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_WithValidUser_DeletesUserAndLogsEvent( public async Task DeleteUserAsync_WithValidUser_DeletesUserAndLogsEvent(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, Guid deletingUserId, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user, Guid deletingUserId,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser) [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser)
{ {
// Arrange // Arrange
@ -34,8 +34,8 @@ public class DeleteManagedOrganizationUserAccountCommandTests
.GetByIdAsync(organizationUser.Id) .GetByIdAsync(organizationUser.Id)
.Returns(organizationUser); .Returns(organizationUser);
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync( .GetUsersOrganizationClaimedStatusAsync(
organizationUser.OrganizationId, organizationUser.OrganizationId,
Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id))) Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(organizationUser.Id)))
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } }); .Returns(new Dictionary<Guid, bool> { { organizationUser.Id, true } });
@ -59,7 +59,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_WithUserNotFound_ThrowsException( public async Task DeleteUserAsync_WithUserNotFound_ThrowsException(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
Guid organizationId, Guid organizationUserId) Guid organizationId, Guid organizationUserId)
{ {
// Arrange // Arrange
@ -81,7 +81,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_DeletingYourself_ThrowsException( public async Task DeleteUserAsync_DeletingYourself_ThrowsException(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
User user, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser,
Guid deletingUserId) Guid deletingUserId)
@ -110,7 +110,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_WhenUserIsInvited_ThrowsException( public async Task DeleteUserAsync_WhenUserIsInvited_ThrowsException(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser organizationUser) [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser organizationUser)
{ {
// Arrange // Arrange
@ -134,7 +134,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_WhenCustomUserDeletesAdmin_ThrowsException( public async Task DeleteUserAsync_WhenCustomUserDeletesAdmin_ThrowsException(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Admin)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Admin)] OrganizationUser organizationUser,
Guid deletingUserId) Guid deletingUserId)
{ {
@ -166,7 +166,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_DeletingOwnerWhenNotOwner_ThrowsException( public async Task DeleteUserAsync_DeletingOwnerWhenNotOwner_ThrowsException(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser,
Guid deletingUserId) Guid deletingUserId)
{ {
@ -198,7 +198,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_DeletingLastConfirmedOwner_ThrowsException( public async Task DeleteUserAsync_DeletingLastConfirmedOwner_ThrowsException(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser organizationUser,
Guid deletingUserId) Guid deletingUserId)
{ {
@ -237,7 +237,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteUserAsync_WithUserNotManaged_ThrowsException( public async Task DeleteUserAsync_WithUserNotManaged_ThrowsException(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser) [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser organizationUser)
{ {
// Arrange // Arrange
@ -250,8 +250,8 @@ public class DeleteManagedOrganizationUserAccountCommandTests
sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id) sutProvider.GetDependency<IUserRepository>().GetByIdAsync(user.Id)
.Returns(user); .Returns(user);
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(organizationUser.OrganizationId, Arg.Any<IEnumerable<Guid>>()) .GetUsersOrganizationClaimedStatusAsync(organizationUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { organizationUser.Id, false } }); .Returns(new Dictionary<Guid, bool> { { organizationUser.Id, false } });
// Act // Act
@ -259,7 +259,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, null)); sutProvider.Sut.DeleteUserAsync(organizationUser.OrganizationId, organizationUser.Id, null));
// Assert // Assert
Assert.Equal("Member is not managed by the organization.", exception.Message); Assert.Equal("Member is not claimed by the organization.", exception.Message);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>()); .LogOrganizationUserEventAsync(Arg.Any<OrganizationUser>(), Arg.Any<EventType>(), Arg.Any<DateTime?>());
@ -268,7 +268,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents( public async Task DeleteManyUsersAsync_WithValidUsers_DeletesUsersAndLogsEvents(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user1, User user2, Guid organizationId, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user1, User user2, Guid organizationId,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser2) [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser2)
{ {
@ -285,8 +285,8 @@ public class DeleteManagedOrganizationUserAccountCommandTests
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user2.Id))) .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user2.Id)))
.Returns(new[] { user1, user2 }); .Returns(new[] { user1, user2 });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>()) .GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser2.Id, true } }); .Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser2.Id, true } });
// Act // Act
@ -308,7 +308,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserNotFound_ReturnsErrorMessage( public async Task DeleteManyUsersAsync_WhenUserNotFound_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
Guid organizationId, Guid organizationId,
Guid orgUserId) Guid orgUserId)
{ {
@ -329,7 +329,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_WhenDeletingYourself_ReturnsErrorMessage( public async Task DeleteManyUsersAsync_WhenDeletingYourself_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
User user, [OrganizationUser] OrganizationUser orgUser, Guid deletingUserId) User user, [OrganizationUser] OrganizationUser orgUser, Guid deletingUserId)
{ {
// Arrange // Arrange
@ -358,7 +358,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserIsInvited_ReturnsErrorMessage( public async Task DeleteManyUsersAsync_WhenUserIsInvited_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider,
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser) [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser)
{ {
// Arrange // Arrange
@ -383,7 +383,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_WhenDeletingOwnerAsNonOwner_ReturnsErrorMessage( public async Task DeleteManyUsersAsync_WhenDeletingOwnerAsNonOwner_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
Guid deletingUserId) Guid deletingUserId)
{ {
@ -415,7 +415,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_WhenDeletingLastOwner_ReturnsErrorMessage( public async Task DeleteManyUsersAsync_WhenDeletingLastOwner_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser orgUser,
Guid deletingUserId) Guid deletingUserId)
{ {
@ -453,7 +453,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_WhenUserNotManaged_ReturnsErrorMessage( public async Task DeleteManyUsersAsync_WhenUserNotManaged_ReturnsErrorMessage(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser) [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser)
{ {
// Arrange // Arrange
@ -467,8 +467,8 @@ public class DeleteManagedOrganizationUserAccountCommandTests
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value))) .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(orgUser.UserId.Value)))
.Returns(new[] { user }); .Returns(new[] { user });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>()) .GetUsersOrganizationClaimedStatusAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, false } }); .Returns(new Dictionary<Guid, bool> { { orgUser.Id, false } });
// Act // Act
@ -477,7 +477,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
// Assert // Assert
Assert.Single(result); Assert.Single(result);
Assert.Equal(orgUser.Id, result.First().Item1); Assert.Equal(orgUser.Id, result.First().Item1);
Assert.Contains("Member is not managed by the organization.", result.First().Item2); Assert.Contains("Member is not claimed by the organization.", result.First().Item2);
await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>()); await sutProvider.GetDependency<IUserService>().Received(0).DeleteAsync(Arg.Any<User>());
await sutProvider.GetDependency<IEventService>().Received(0) await sutProvider.GetDependency<IEventService>().Received(0)
.LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>()); .LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
@ -486,7 +486,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task DeleteManyUsersAsync_MixedValidAndInvalidUsers_ReturnsAppropriateResults( public async Task DeleteManyUsersAsync_MixedValidAndInvalidUsers_ReturnsAppropriateResults(
SutProvider<DeleteManagedOrganizationUserAccountCommand> sutProvider, User user1, User user3, SutProvider<DeleteClaimedOrganizationUserAccountCommand> sutProvider, User user1, User user3,
Guid organizationId, Guid organizationId,
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser1,
[OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser2, [OrganizationUser(OrganizationUserStatusType.Invited, OrganizationUserType.User)] OrganizationUser orgUser2,
@ -506,8 +506,8 @@ public class DeleteManagedOrganizationUserAccountCommandTests
.GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user3.Id))) .GetManyAsync(Arg.Is<IEnumerable<Guid>>(ids => ids.Contains(user1.Id) && ids.Contains(user3.Id)))
.Returns(new[] { user1, user3 }); .Returns(new[] { user1, user3 });
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>()) .GetUsersOrganizationClaimedStatusAsync(organizationId, Arg.Any<IEnumerable<Guid>>())
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser3.Id, false } }); .Returns(new Dictionary<Guid, bool> { { orgUser1.Id, true }, { orgUser3.Id, false } });
// Act // Act
@ -517,7 +517,7 @@ public class DeleteManagedOrganizationUserAccountCommandTests
Assert.Equal(3, results.Count()); Assert.Equal(3, results.Count());
Assert.Empty(results.First(r => r.Item1 == orgUser1.Id).Item2); Assert.Empty(results.First(r => r.Item1 == orgUser1.Id).Item2);
Assert.Equal("You cannot delete a member with Invited status.", results.First(r => r.Item1 == orgUser2.Id).Item2); Assert.Equal("You cannot delete a member with Invited status.", results.First(r => r.Item1 == orgUser2.Id).Item2);
Assert.Equal("Member is not managed by the organization.", results.First(r => r.Item1 == orgUser3.Id).Item2); Assert.Equal("Member is not claimed by the organization.", results.First(r => r.Item1 == orgUser3.Id).Item2);
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync( await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(
Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events => Arg.Is<IEnumerable<(OrganizationUser, EventType, DateTime?)>>(events =>

View File

@ -12,14 +12,14 @@ using Xunit;
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers; namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers;
[SutProviderCustomize] [SutProviderCustomize]
public class GetOrganizationUsersManagementStatusQueryTests public class GetOrganizationUsersClaimedStatusQueryTests
{ {
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task GetUsersOrganizationManagementStatusAsync_WithNoUsers_ReturnsEmpty( public async Task GetUsersOrganizationManagementStatusAsync_WithNoUsers_ReturnsEmpty(
Organization organization, Organization organization,
SutProvider<GetOrganizationUsersManagementStatusQuery> sutProvider) SutProvider<GetOrganizationUsersClaimedStatusQuery> sutProvider)
{ {
var result = await sutProvider.Sut.GetUsersOrganizationManagementStatusAsync(organization.Id, new List<Guid>()); var result = await sutProvider.Sut.GetUsersOrganizationClaimedStatusAsync(organization.Id, new List<Guid>());
Assert.Empty(result); Assert.Empty(result);
} }
@ -28,7 +28,7 @@ public class GetOrganizationUsersManagementStatusQueryTests
public async Task GetUsersOrganizationManagementStatusAsync_WithUseSsoEnabled_Success( public async Task GetUsersOrganizationManagementStatusAsync_WithUseSsoEnabled_Success(
Organization organization, Organization organization,
ICollection<OrganizationUser> usersWithClaimedDomain, ICollection<OrganizationUser> usersWithClaimedDomain,
SutProvider<GetOrganizationUsersManagementStatusQuery> sutProvider) SutProvider<GetOrganizationUsersClaimedStatusQuery> sutProvider)
{ {
organization.Enabled = true; organization.Enabled = true;
organization.UseSso = true; organization.UseSso = true;
@ -44,7 +44,7 @@ public class GetOrganizationUsersManagementStatusQueryTests
.GetManyByOrganizationWithClaimedDomainsAsync(organization.Id) .GetManyByOrganizationWithClaimedDomainsAsync(organization.Id)
.Returns(usersWithClaimedDomain); .Returns(usersWithClaimedDomain);
var result = await sutProvider.Sut.GetUsersOrganizationManagementStatusAsync(organization.Id, userIdsToCheck); var result = await sutProvider.Sut.GetUsersOrganizationClaimedStatusAsync(organization.Id, userIdsToCheck);
Assert.All(usersWithClaimedDomain, ou => Assert.True(result[ou.Id])); Assert.All(usersWithClaimedDomain, ou => Assert.True(result[ou.Id]));
Assert.False(result[userIdWithoutClaimedDomain]); Assert.False(result[userIdWithoutClaimedDomain]);
@ -54,7 +54,7 @@ public class GetOrganizationUsersManagementStatusQueryTests
public async Task GetUsersOrganizationManagementStatusAsync_WithUseSsoDisabled_ReturnsAllFalse( public async Task GetUsersOrganizationManagementStatusAsync_WithUseSsoDisabled_ReturnsAllFalse(
Organization organization, Organization organization,
ICollection<OrganizationUser> usersWithClaimedDomain, ICollection<OrganizationUser> usersWithClaimedDomain,
SutProvider<GetOrganizationUsersManagementStatusQuery> sutProvider) SutProvider<GetOrganizationUsersClaimedStatusQuery> sutProvider)
{ {
organization.Enabled = true; organization.Enabled = true;
organization.UseSso = false; organization.UseSso = false;
@ -70,7 +70,7 @@ public class GetOrganizationUsersManagementStatusQueryTests
.GetManyByOrganizationWithClaimedDomainsAsync(organization.Id) .GetManyByOrganizationWithClaimedDomainsAsync(organization.Id)
.Returns(usersWithClaimedDomain); .Returns(usersWithClaimedDomain);
var result = await sutProvider.Sut.GetUsersOrganizationManagementStatusAsync(organization.Id, userIdsToCheck); var result = await sutProvider.Sut.GetUsersOrganizationClaimedStatusAsync(organization.Id, userIdsToCheck);
Assert.All(result, r => Assert.False(r.Value)); Assert.All(result, r => Assert.False(r.Value));
} }
@ -79,7 +79,7 @@ public class GetOrganizationUsersManagementStatusQueryTests
public async Task GetUsersOrganizationManagementStatusAsync_WithDisabledOrganization_ReturnsAllFalse( public async Task GetUsersOrganizationManagementStatusAsync_WithDisabledOrganization_ReturnsAllFalse(
Organization organization, Organization organization,
ICollection<OrganizationUser> usersWithClaimedDomain, ICollection<OrganizationUser> usersWithClaimedDomain,
SutProvider<GetOrganizationUsersManagementStatusQuery> sutProvider) SutProvider<GetOrganizationUsersClaimedStatusQuery> sutProvider)
{ {
organization.Enabled = false; organization.Enabled = false;
@ -94,7 +94,7 @@ public class GetOrganizationUsersManagementStatusQueryTests
.GetManyByOrganizationWithClaimedDomainsAsync(organization.Id) .GetManyByOrganizationWithClaimedDomainsAsync(organization.Id)
.Returns(usersWithClaimedDomain); .Returns(usersWithClaimedDomain);
var result = await sutProvider.Sut.GetUsersOrganizationManagementStatusAsync(organization.Id, userIdsToCheck); var result = await sutProvider.Sut.GetUsersOrganizationClaimedStatusAsync(organization.Id, userIdsToCheck);
Assert.All(result, r => Assert.False(r.Value)); Assert.All(result, r => Assert.False(r.Value));
} }

View File

@ -41,9 +41,9 @@ public class RemoveOrganizationUserCommandTests
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId); await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
// Assert // Assert
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.GetUsersOrganizationManagementStatusAsync(default, default); .GetUsersOrganizationClaimedStatusAsync(default, default);
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1) .Received(1)
.DeleteAsync(organizationUser); .DeleteAsync(organizationUser);
@ -78,9 +78,9 @@ public class RemoveOrganizationUserCommandTests
await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId); await sutProvider.Sut.RemoveUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
// Assert // Assert
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.Received(1) .Received(1)
.GetUsersOrganizationManagementStatusAsync( .GetUsersOrganizationClaimedStatusAsync(
organizationUser.OrganizationId, organizationUser.OrganizationId,
Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.Id))); Arg.Is<IEnumerable<Guid>>(i => i.Contains(organizationUser.Id)));
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
@ -247,17 +247,17 @@ public class RemoveOrganizationUserCommandTests
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByIdAsync(orgUser.Id) .GetByIdAsync(orgUser.Id)
.Returns(orgUser); .Returns(orgUser);
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(orgUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id))) .GetUsersOrganizationClaimedStatusAsync(orgUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id)))
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } }); .Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } });
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<BadRequestException>( var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.RemoveUserAsync(orgUser.OrganizationId, orgUser.Id, deletingUserId)); () => sutProvider.Sut.RemoveUserAsync(orgUser.OrganizationId, orgUser.Id, deletingUserId));
Assert.Contains(RemoveOrganizationUserCommand.RemoveClaimedAccountErrorMessage, exception.Message); Assert.Contains(RemoveOrganizationUserCommand.RemoveClaimedAccountErrorMessage, exception.Message);
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.Received(1) .Received(1)
.GetUsersOrganizationManagementStatusAsync(orgUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id))); .GetUsersOrganizationClaimedStatusAsync(orgUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id)));
} }
[Theory, BitAutoData] [Theory, BitAutoData]
@ -274,9 +274,9 @@ public class RemoveOrganizationUserCommandTests
await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser); await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser);
// Assert // Assert
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.GetUsersOrganizationManagementStatusAsync(default, default); .GetUsersOrganizationClaimedStatusAsync(default, default);
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1) .Received(1)
.DeleteAsync(organizationUser); .DeleteAsync(organizationUser);
@ -302,9 +302,9 @@ public class RemoveOrganizationUserCommandTests
await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser); await sutProvider.Sut.RemoveUserAsync(organizationUser.OrganizationId, organizationUser.Id, eventSystemUser);
// Assert // Assert
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.GetUsersOrganizationManagementStatusAsync(default, default); .GetUsersOrganizationClaimedStatusAsync(default, default);
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1) .Received(1)
.DeleteAsync(organizationUser); .DeleteAsync(organizationUser);
@ -490,8 +490,8 @@ public class RemoveOrganizationUserCommandTests
sutProvider.GetDependency<ICurrentContext>() sutProvider.GetDependency<ICurrentContext>()
.OrganizationOwner(deletingUser.OrganizationId) .OrganizationOwner(deletingUser.OrganizationId)
.Returns(true); .Returns(true);
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync( .GetUsersOrganizationClaimedStatusAsync(
deletingUser.OrganizationId, deletingUser.OrganizationId,
Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))) Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)))
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, false }, { orgUser2.Id, false } }); .Returns(new Dictionary<Guid, bool> { { orgUser1.Id, false }, { orgUser2.Id, false } });
@ -502,9 +502,9 @@ public class RemoveOrganizationUserCommandTests
// Assert // Assert
Assert.Equal(2, result.Count()); Assert.Equal(2, result.Count());
Assert.All(result, r => Assert.Empty(r.ErrorMessage)); Assert.All(result, r => Assert.Empty(r.ErrorMessage));
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.GetUsersOrganizationManagementStatusAsync(default, default); .GetUsersOrganizationClaimedStatusAsync(default, default);
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1) .Received(1)
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))); .DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));
@ -544,8 +544,8 @@ public class RemoveOrganizationUserCommandTests
sutProvider.GetDependency<ICurrentContext>() sutProvider.GetDependency<ICurrentContext>()
.OrganizationOwner(deletingUser.OrganizationId) .OrganizationOwner(deletingUser.OrganizationId)
.Returns(true); .Returns(true);
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync( .GetUsersOrganizationClaimedStatusAsync(
deletingUser.OrganizationId, deletingUser.OrganizationId,
Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))) Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)))
.Returns(new Dictionary<Guid, bool> { { orgUser1.Id, false }, { orgUser2.Id, false } }); .Returns(new Dictionary<Guid, bool> { { orgUser1.Id, false }, { orgUser2.Id, false } });
@ -556,9 +556,9 @@ public class RemoveOrganizationUserCommandTests
// Assert // Assert
Assert.Equal(2, result.Count()); Assert.Equal(2, result.Count());
Assert.All(result, r => Assert.Empty(r.ErrorMessage)); Assert.All(result, r => Assert.Empty(r.ErrorMessage));
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.Received(1) .Received(1)
.GetUsersOrganizationManagementStatusAsync( .GetUsersOrganizationClaimedStatusAsync(
deletingUser.OrganizationId, deletingUser.OrganizationId,
Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))); Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
@ -638,7 +638,7 @@ public class RemoveOrganizationUserCommandTests
} }
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task RemoveUsers_WithDeletingUserId_RemovingManagedUser_WithAccountDeprovisioningEnabled_ThrowsException( public async Task RemoveUsers_WithDeletingUserId_RemovingClaimedUser_WithAccountDeprovisioningEnabled_ThrowsException(
[OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser, [OrganizationUser(status: OrganizationUserStatusType.Confirmed, OrganizationUserType.User)] OrganizationUser orgUser,
OrganizationUser deletingUser, OrganizationUser deletingUser,
SutProvider<RemoveOrganizationUserCommand> sutProvider) SutProvider<RemoveOrganizationUserCommand> sutProvider)
@ -658,8 +658,8 @@ public class RemoveOrganizationUserCommandTests
.HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, Arg.Any<IEnumerable<Guid>>()) .HasConfirmedOwnersExceptAsync(orgUser.OrganizationId, Arg.Any<IEnumerable<Guid>>())
.Returns(true); .Returns(true);
sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.GetUsersOrganizationManagementStatusAsync(orgUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id))) .GetUsersOrganizationClaimedStatusAsync(orgUser.OrganizationId, Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser.Id)))
.Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } }); .Returns(new Dictionary<Guid, bool> { { orgUser.Id, true } });
// Act // Act
@ -723,9 +723,9 @@ public class RemoveOrganizationUserCommandTests
// Assert // Assert
Assert.Equal(2, result.Count()); Assert.Equal(2, result.Count());
Assert.All(result, r => Assert.Empty(r.ErrorMessage)); Assert.All(result, r => Assert.Empty(r.ErrorMessage));
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.GetUsersOrganizationManagementStatusAsync(default, default); .GetUsersOrganizationClaimedStatusAsync(default, default);
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1) .Received(1)
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))); .DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));
@ -768,9 +768,9 @@ public class RemoveOrganizationUserCommandTests
// Assert // Assert
Assert.Equal(2, result.Count()); Assert.Equal(2, result.Count());
Assert.All(result, r => Assert.Empty(r.ErrorMessage)); Assert.All(result, r => Assert.Empty(r.ErrorMessage));
await sutProvider.GetDependency<IGetOrganizationUsersManagementStatusQuery>() await sutProvider.GetDependency<IGetOrganizationUsersClaimedStatusQuery>()
.DidNotReceiveWithAnyArgs() .DidNotReceiveWithAnyArgs()
.GetUsersOrganizationManagementStatusAsync(default, default); .GetUsersOrganizationClaimedStatusAsync(default, default);
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1) .Received(1)
.DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id))); .DeleteManyAsync(Arg.Is<IEnumerable<Guid>>(i => i.Contains(orgUser1.Id) && i.Contains(orgUser2.Id)));

View File

@ -257,6 +257,41 @@ public class X509ChainCustomizationServiceCollectionExtensionsTests
Assert.Equal("Hi", response); Assert.Equal("Hi", response);
} }
[Fact]
public async Task CallHttp_ReachingOutToServerTrustedThroughSystemCA()
{
var services = CreateServices((gs, environment, config) => { }, services =>
{
services.Configure<X509ChainOptions>(options =>
{
options.AdditionalCustomTrustCertificates = [];
});
});
var httpClient = services.GetRequiredService<IHttpClientFactory>().CreateClient();
var response = await httpClient.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
}
[Fact]
public async Task CallHttpWithCustomTrustForSelfSigned_ReachingOutToServerTrustedThroughSystemCA()
{
var selfSignedCertificate = CreateSelfSignedCert("localhost");
var services = CreateServices((gs, environment, config) => { }, services =>
{
services.Configure<X509ChainOptions>(options =>
{
options.AdditionalCustomTrustCertificates = [selfSignedCertificate];
});
});
var httpClient = services.GetRequiredService<IHttpClientFactory>().CreateClient();
var response = await httpClient.GetAsync("https://example.com");
response.EnsureSuccessStatusCode();
}
private static async Task<IAsyncDisposable> CreateServerAsync(int port, Action<HttpsConnectionAdapterOptions> configure) private static async Task<IAsyncDisposable> CreateServerAsync(int port, Action<HttpsConnectionAdapterOptions> configure)
{ {
var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions()); var builder = WebApplication.CreateEmptyBuilder(new WebApplicationOptions());

View File

@ -341,19 +341,19 @@ public class UserServiceTests
} }
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningDisabled_ReturnsFalse( public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningDisabled_ReturnsFalse(
SutProvider<UserService> sutProvider, Guid userId) SutProvider<UserService> sutProvider, Guid userId)
{ {
sutProvider.GetDependency<IFeatureService>() sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.AccountDeprovisioning) .IsEnabled(FeatureFlagKeys.AccountDeprovisioning)
.Returns(false); .Returns(false);
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId); var result = await sutProvider.Sut.IsClaimedByAnyOrganizationAsync(userId);
Assert.False(result); Assert.False(result);
} }
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingEnabledOrganization_ReturnsTrue( public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingEnabledOrganization_ReturnsTrue(
SutProvider<UserService> sutProvider, Guid userId, Organization organization) SutProvider<UserService> sutProvider, Guid userId, Organization organization)
{ {
organization.Enabled = true; organization.Enabled = true;
@ -367,12 +367,12 @@ public class UserServiceTests
.GetByVerifiedUserEmailDomainAsync(userId) .GetByVerifiedUserEmailDomainAsync(userId)
.Returns(new[] { organization }); .Returns(new[] { organization });
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId); var result = await sutProvider.Sut.IsClaimedByAnyOrganizationAsync(userId);
Assert.True(result); Assert.True(result);
} }
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingDisabledOrganization_ReturnsFalse( public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithManagingDisabledOrganization_ReturnsFalse(
SutProvider<UserService> sutProvider, Guid userId, Organization organization) SutProvider<UserService> sutProvider, Guid userId, Organization organization)
{ {
organization.Enabled = false; organization.Enabled = false;
@ -386,12 +386,12 @@ public class UserServiceTests
.GetByVerifiedUserEmailDomainAsync(userId) .GetByVerifiedUserEmailDomainAsync(userId)
.Returns(new[] { organization }); .Returns(new[] { organization });
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId); var result = await sutProvider.Sut.IsClaimedByAnyOrganizationAsync(userId);
Assert.False(result); Assert.False(result);
} }
[Theory, BitAutoData] [Theory, BitAutoData]
public async Task IsManagedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithOrganizationUseSsoFalse_ReturnsFalse( public async Task IsClaimedByAnyOrganizationAsync_WithAccountDeprovisioningEnabled_WithOrganizationUseSsoFalse_ReturnsFalse(
SutProvider<UserService> sutProvider, Guid userId, Organization organization) SutProvider<UserService> sutProvider, Guid userId, Organization organization)
{ {
organization.Enabled = true; organization.Enabled = true;
@ -405,7 +405,7 @@ public class UserServiceTests
.GetByVerifiedUserEmailDomainAsync(userId) .GetByVerifiedUserEmailDomainAsync(userId)
.Returns(new[] { organization }); .Returns(new[] { organization });
var result = await sutProvider.Sut.IsManagedByAnyOrganizationAsync(userId); var result = await sutProvider.Sut.IsClaimedByAnyOrganizationAsync(userId);
Assert.False(result); Assert.False(result);
} }