mirror of
https://github.com/bitwarden/server.git
synced 2025-06-27 14:16:19 -05:00
[PM-21734] Read claims from ClaimsPrincipal
when creating self-hosted organization (#5953)
* Read claims from claims principal when creating self-hosted organization * Run dotnet format * Jared's feedback * Run dotnet format
This commit is contained in:
parent
1c3bf259e9
commit
b951b38c37
@ -97,7 +97,7 @@ public class OrganizationService : IOrganizationService
|
||||
IPricingClient pricingClient,
|
||||
IPolicyRequirementQuery policyRequirementQuery,
|
||||
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand
|
||||
)
|
||||
)
|
||||
{
|
||||
_organizationRepository = organizationRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
@ -198,6 +198,7 @@ public class OrganizationService : IOrganizationService
|
||||
{
|
||||
await AdjustSeatsAsync(organization, seatAdjustment);
|
||||
}
|
||||
|
||||
if (maxAutoscaleSeats != organization.MaxAutoscaleSeats)
|
||||
{
|
||||
await UpdateAutoscalingAsync(organization, maxAutoscaleSeats);
|
||||
@ -206,7 +207,6 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
private async Task UpdateAutoscalingAsync(Organization organization, int? maxAutoscaleSeats)
|
||||
{
|
||||
|
||||
if (maxAutoscaleSeats.HasValue &&
|
||||
organization.Seats.HasValue &&
|
||||
maxAutoscaleSeats.Value < organization.Seats.Value)
|
||||
@ -228,7 +228,8 @@ public class OrganizationService : IOrganizationService
|
||||
if (plan.PasswordManager.MaxSeats.HasValue && maxAutoscaleSeats.HasValue &&
|
||||
maxAutoscaleSeats > plan.PasswordManager.MaxSeats)
|
||||
{
|
||||
throw new BadRequestException(string.Concat($"Your plan has a seat limit of {plan.PasswordManager.MaxSeats}, ",
|
||||
throw new BadRequestException(string.Concat(
|
||||
$"Your plan has a seat limit of {plan.PasswordManager.MaxSeats}, ",
|
||||
$"but you have specified a max autoscale count of {maxAutoscaleSeats}.",
|
||||
"Reduce your max autoscale seat count."));
|
||||
}
|
||||
@ -249,7 +250,8 @@ public class OrganizationService : IOrganizationService
|
||||
return await AdjustSeatsAsync(organization, seatAdjustment);
|
||||
}
|
||||
|
||||
private async Task<string> AdjustSeatsAsync(Organization organization, int seatAdjustment, IEnumerable<string> ownerEmails = null)
|
||||
private async Task<string> AdjustSeatsAsync(Organization organization, int seatAdjustment,
|
||||
IEnumerable<string> ownerEmails = null)
|
||||
{
|
||||
if (organization.Seats == null)
|
||||
{
|
||||
@ -285,10 +287,11 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
var additionalSeats = newSeatTotal - plan.PasswordManager.BaseSeats;
|
||||
if (plan.PasswordManager.MaxAdditionalSeats.HasValue && additionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value)
|
||||
if (plan.PasswordManager.MaxAdditionalSeats.HasValue &&
|
||||
additionalSeats > plan.PasswordManager.MaxAdditionalSeats.Value)
|
||||
{
|
||||
throw new BadRequestException($"Organization plan allows a maximum of " +
|
||||
$"{plan.PasswordManager.MaxAdditionalSeats.Value} additional seats.");
|
||||
$"{plan.PasswordManager.MaxAdditionalSeats.Value} additional seats.");
|
||||
}
|
||||
|
||||
if (!organization.Seats.HasValue || organization.Seats.Value > newSeatTotal)
|
||||
@ -299,8 +302,9 @@ public class OrganizationService : IOrganizationService
|
||||
{
|
||||
if (organization.UseAdminSponsoredFamilies || seatCounts.Sponsored > 0)
|
||||
{
|
||||
throw new BadRequestException($"Your organization has {seatCounts.Users} members and {seatCounts.Sponsored} sponsored families. " +
|
||||
$"To decrease the seat count below {seatCounts.Total}, you must remove members or sponsorships.");
|
||||
throw new BadRequestException(
|
||||
$"Your organization has {seatCounts.Users} members and {seatCounts.Sponsored} sponsored families. " +
|
||||
$"To decrease the seat count below {seatCounts.Total}, you must remove members or sponsorships.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -319,7 +323,8 @@ public class OrganizationService : IOrganizationService
|
||||
organization.Seats = (short?)newSeatTotal;
|
||||
await ReplaceAndUpdateCacheAsync(organization);
|
||||
|
||||
if (organization.Seats.HasValue && organization.MaxAutoscaleSeats.HasValue && organization.Seats == organization.MaxAutoscaleSeats)
|
||||
if (organization.Seats.HasValue && organization.MaxAutoscaleSeats.HasValue &&
|
||||
organization.Seats == organization.MaxAutoscaleSeats)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -328,7 +333,9 @@ public class OrganizationService : IOrganizationService
|
||||
ownerEmails = (await _organizationUserRepository.GetManyByMinimumRoleAsync(organization.Id,
|
||||
OrganizationUserType.Owner)).Select(u => u.Email).Distinct();
|
||||
}
|
||||
await _mailService.SendOrganizationMaxSeatLimitReachedEmailAsync(organization, organization.MaxAutoscaleSeats.Value, ownerEmails);
|
||||
|
||||
await _mailService.SendOrganizationMaxSeatLimitReachedEmailAsync(organization,
|
||||
organization.MaxAutoscaleSeats.Value, ownerEmails);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -362,7 +369,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
var bankAccount = customer.Sources
|
||||
.FirstOrDefault(s => s is BankAccount && ((BankAccount)s).Status != "verified") as BankAccount;
|
||||
.FirstOrDefault(s => s is BankAccount && ((BankAccount)s).Status != "verified") as BankAccount;
|
||||
if (bankAccount == null)
|
||||
{
|
||||
throw new GatewayException("Cannot find an unverified bank account.");
|
||||
@ -389,7 +396,7 @@ public class OrganizationService : IOrganizationService
|
||||
if (anySingleOrgPolicies)
|
||||
{
|
||||
throw new BadRequestException("You may not create an organization. You belong to an organization " +
|
||||
"which has a policy that prohibits you from being a member of any other organization.");
|
||||
"which has a policy that prohibits you from being a member of any other organization.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,7 +410,7 @@ public class OrganizationService : IOrganizationService
|
||||
if (license.LicenseType != LicenseType.Organization)
|
||||
{
|
||||
throw new BadRequestException("Premium licenses cannot be applied to an organization. " +
|
||||
"Upload this license from your personal account settings page.");
|
||||
"Upload this license from your personal account settings page.");
|
||||
}
|
||||
|
||||
var claimsPrincipal = _licensingService.GetClaimsPrincipalFromLicense(license);
|
||||
@ -422,50 +429,11 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
await ValidateSignUpPoliciesAsync(owner.Id);
|
||||
|
||||
var organization = new Organization
|
||||
{
|
||||
Name = license.Name,
|
||||
BillingEmail = license.BillingEmail,
|
||||
BusinessName = license.BusinessName,
|
||||
PlanType = license.PlanType,
|
||||
Seats = license.Seats,
|
||||
MaxCollections = license.MaxCollections,
|
||||
MaxStorageGb = _globalSettings.SelfHosted ? 10240 : license.MaxStorageGb, // 10 TB
|
||||
UsePolicies = license.UsePolicies,
|
||||
UseSso = license.UseSso,
|
||||
UseKeyConnector = license.UseKeyConnector,
|
||||
UseScim = license.UseScim,
|
||||
UseGroups = license.UseGroups,
|
||||
UseDirectory = license.UseDirectory,
|
||||
UseEvents = license.UseEvents,
|
||||
UseTotp = license.UseTotp,
|
||||
Use2fa = license.Use2fa,
|
||||
UseApi = license.UseApi,
|
||||
UseResetPassword = license.UseResetPassword,
|
||||
Plan = license.Plan,
|
||||
SelfHost = license.SelfHost,
|
||||
UsersGetPremium = license.UsersGetPremium,
|
||||
UseCustomPermissions = license.UseCustomPermissions,
|
||||
Gateway = null,
|
||||
GatewayCustomerId = null,
|
||||
GatewaySubscriptionId = null,
|
||||
ReferenceData = owner.ReferenceData,
|
||||
Enabled = license.Enabled,
|
||||
ExpirationDate = license.Expires,
|
||||
LicenseKey = license.LicenseKey,
|
||||
PublicKey = publicKey,
|
||||
PrivateKey = privateKey,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Status = OrganizationStatusType.Created,
|
||||
UsePasswordManager = license.UsePasswordManager,
|
||||
UseSecretsManager = license.UseSecretsManager,
|
||||
SmSeats = license.SmSeats,
|
||||
SmServiceAccounts = license.SmServiceAccounts,
|
||||
UseRiskInsights = license.UseRiskInsights,
|
||||
UseOrganizationDomains = license.UseOrganizationDomains,
|
||||
UseAdminSponsoredFamilies = license.UseAdminSponsoredFamilies,
|
||||
};
|
||||
var organization = claimsPrincipal != null
|
||||
// If the ClaimsPrincipal exists (there's a token on the license), use it to build the organization.
|
||||
? OrganizationFactory.Create(owner, claimsPrincipal, publicKey, privateKey)
|
||||
// If there's no ClaimsPrincipal (there's no token on the license), use the license to build the organization.
|
||||
: OrganizationFactory.Create(owner, license, publicKey, privateKey);
|
||||
|
||||
var result = await SignUpAsync(organization, owner.Id, ownerKey, collectionName, false);
|
||||
|
||||
@ -480,8 +448,9 @@ public class OrganizationService : IOrganizationService
|
||||
/// Private helper method to create a new organization.
|
||||
/// This is common code used by both the cloud and self-hosted methods.
|
||||
/// </summary>
|
||||
private async Task<(Organization organization, OrganizationUser organizationUser, Collection defaultCollection)> SignUpAsync(Organization organization,
|
||||
Guid ownerId, string ownerKey, string collectionName, bool withPayment)
|
||||
private async Task<(Organization organization, OrganizationUser organizationUser, Collection defaultCollection)>
|
||||
SignUpAsync(Organization organization,
|
||||
Guid ownerId, string ownerKey, string collectionName, bool withPayment)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -537,7 +506,15 @@ public class OrganizationService : IOrganizationService
|
||||
if (orgUser != null)
|
||||
{
|
||||
defaultOwnerAccess =
|
||||
[new CollectionAccessSelection { Id = orgUser.Id, HidePasswords = false, ReadOnly = false, Manage = true }];
|
||||
[
|
||||
new CollectionAccessSelection
|
||||
{
|
||||
Id = orgUser.Id,
|
||||
HidePasswords = false,
|
||||
ReadOnly = false,
|
||||
Manage = true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
await _collectionRepository.CreateAsync(defaultCollection, null, defaultOwnerAccess);
|
||||
@ -573,7 +550,8 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Organization organization, bool updateBilling = false, EventType eventType = EventType.Organization_Updated)
|
||||
public async Task UpdateAsync(Organization organization, bool updateBilling = false,
|
||||
EventType eventType = EventType.Organization_Updated)
|
||||
{
|
||||
if (organization.Id == default(Guid))
|
||||
{
|
||||
@ -594,11 +572,12 @@ public class OrganizationService : IOrganizationService
|
||||
if (updateBilling && !string.IsNullOrWhiteSpace(organization.GatewayCustomerId))
|
||||
{
|
||||
var customerService = new CustomerService();
|
||||
await customerService.UpdateAsync(organization.GatewayCustomerId, new CustomerUpdateOptions
|
||||
{
|
||||
Email = organization.BillingEmail,
|
||||
Description = organization.DisplayBusinessName()
|
||||
});
|
||||
await customerService.UpdateAsync(organization.GatewayCustomerId,
|
||||
new CustomerUpdateOptions
|
||||
{
|
||||
Email = organization.BillingEmail,
|
||||
Description = organization.DisplayBusinessName()
|
||||
});
|
||||
}
|
||||
|
||||
if (eventType == EventType.Organization_CollectionManagement_Updated)
|
||||
@ -648,7 +627,8 @@ public class OrganizationService : IOrganizationService
|
||||
await UpdateAsync(organization);
|
||||
}
|
||||
|
||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, EventSystemUser? systemUser,
|
||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId,
|
||||
EventSystemUser? systemUser,
|
||||
OrganizationUserInvite invite, string externalId)
|
||||
{
|
||||
// Ideally OrganizationUserInvite should represent a single user so that this doesn't have to be a runtime check
|
||||
@ -661,7 +641,8 @@ public class OrganizationService : IOrganizationService
|
||||
var invalidAssociations = invite.Collections?.Where(cas => cas.Manage && (cas.ReadOnly || cas.HidePasswords));
|
||||
if (invalidAssociations?.Any() ?? false)
|
||||
{
|
||||
throw new BadRequestException("The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
||||
throw new BadRequestException(
|
||||
"The Manage property is mutually exclusive and cannot be true while the ReadOnly or HidePasswords properties are also true.");
|
||||
}
|
||||
|
||||
var results = await InviteUsersAsync(organizationId, invitingUserId, systemUser,
|
||||
@ -672,6 +653,7 @@ public class OrganizationService : IOrganizationService
|
||||
{
|
||||
throw new BadRequestException("This user has already been invited.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -683,7 +665,8 @@ public class OrganizationService : IOrganizationService
|
||||
/// <param name="systemUser">The system user which is sending the invite. Only used when inviting via SCIM; null if using a client app or Public API</param>
|
||||
/// <param name="invites">Details about the users being invited</param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, Guid? invitingUserId, EventSystemUser? systemUser,
|
||||
public async Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, Guid? invitingUserId,
|
||||
EventSystemUser? systemUser,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
{
|
||||
var inviteTypes = new HashSet<OrganizationUserType>(invites.Where(i => i.invite.Type.HasValue)
|
||||
@ -695,7 +678,8 @@ public class OrganizationService : IOrganizationService
|
||||
{
|
||||
foreach (var (invite, _) in invites)
|
||||
{
|
||||
await ValidateOrganizationUserUpdatePermissions(organizationId, invite.Type.Value, null, invite.Permissions);
|
||||
await ValidateOrganizationUserUpdatePermissions(organizationId, invite.Type.Value, null,
|
||||
invite.Permissions);
|
||||
await ValidateOrganizationCustomPermissionsEnabledAsync(organizationId, invite.Type.Value);
|
||||
}
|
||||
}
|
||||
@ -705,7 +689,8 @@ public class OrganizationService : IOrganizationService
|
||||
if (systemUser.HasValue)
|
||||
{
|
||||
// Log SCIM event
|
||||
await _eventService.LogOrganizationUserEventsAsync(events.Select(e => (e.Item1, e.Item2, systemUser.Value, e.Item3)));
|
||||
await _eventService.LogOrganizationUserEventsAsync(events.Select(e =>
|
||||
(e.Item1, e.Item2, systemUser.Value, e.Item3)));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -716,8 +701,10 @@ public class OrganizationService : IOrganizationService
|
||||
return organizationUsers;
|
||||
}
|
||||
|
||||
private async Task<(List<OrganizationUser> organizationUsers, List<(OrganizationUser, EventType, DateTime?)> events)> SaveUsersSendInvitesAsync(Guid organizationId,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
private async
|
||||
Task<(List<OrganizationUser> organizationUsers, List<(OrganizationUser, EventType, DateTime?)> events)>
|
||||
SaveUsersSendInvitesAsync(Guid organizationId,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
{
|
||||
var organization = await GetOrgById(organizationId);
|
||||
var initialSeatCount = organization.Seats;
|
||||
@ -727,7 +714,8 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
var existingEmails = new HashSet<string>(await _organizationUserRepository.SelectKnownEmailsAsync(
|
||||
organizationId, invites.SelectMany(i => i.invite.Emails), false), StringComparer.InvariantCultureIgnoreCase);
|
||||
organizationId, invites.SelectMany(i => i.invite.Emails), false),
|
||||
StringComparer.InvariantCultureIgnoreCase);
|
||||
|
||||
// Seat autoscaling
|
||||
var initialSmSeatCount = organization.SmSeats;
|
||||
@ -755,7 +743,8 @@ public class OrganizationService : IOrganizationService
|
||||
.SelectMany(i => i.invite.Emails)
|
||||
.Count(email => !existingEmails.Contains(email));
|
||||
|
||||
var additionalSmSeatsRequired = await _countNewSmSeatsRequiredQuery.CountNewSmSeatsRequiredAsync(organization.Id, inviteWithSmAccessCount);
|
||||
var additionalSmSeatsRequired =
|
||||
await _countNewSmSeatsRequiredQuery.CountNewSmSeatsRequiredAsync(organization.Id, inviteWithSmAccessCount);
|
||||
if (additionalSmSeatsRequired > 0)
|
||||
{
|
||||
var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
|
||||
@ -764,7 +753,9 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
var invitedAreAllOwners = invites.All(i => i.invite.Type == OrganizationUserType.Owner);
|
||||
if (!invitedAreAllOwners && !await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, new Guid[] { }, includeProvider: true))
|
||||
if (!invitedAreAllOwners &&
|
||||
!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationId, new Guid[] { },
|
||||
includeProvider: true))
|
||||
{
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
@ -887,7 +878,8 @@ public class OrganizationService : IOrganizationService
|
||||
await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(smSubscriptionUpdateRevert);
|
||||
}
|
||||
|
||||
if (initialSeatCount.HasValue && currentOrganization.Seats.HasValue && currentOrganization.Seats.Value != initialSeatCount.Value)
|
||||
if (initialSeatCount.HasValue && currentOrganization.Seats.HasValue &&
|
||||
currentOrganization.Seats.Value != initialSeatCount.Value)
|
||||
{
|
||||
await AdjustSeatsAsync(organization, initialSeatCount.Value - currentOrganization.Seats.Value);
|
||||
}
|
||||
@ -903,7 +895,8 @@ public class OrganizationService : IOrganizationService
|
||||
return (allOrgUsers, events);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId,
|
||||
public async Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId,
|
||||
Guid? invitingUserId,
|
||||
IEnumerable<Guid> organizationUsersId)
|
||||
{
|
||||
var orgUsers = await _organizationUserRepository.GetManyAsync(organizationUsersId);
|
||||
@ -925,7 +918,8 @@ public class OrganizationService : IOrganizationService
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId, bool initOrganization = false)
|
||||
public async Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId,
|
||||
bool initOrganization = false)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
||||
if (orgUser == null || orgUser.OrganizationId != organizationId ||
|
||||
@ -1012,7 +1006,9 @@ public class OrganizationService : IOrganizationService
|
||||
IEnumerable<string> ownerEmails;
|
||||
if (providerOrg != null)
|
||||
{
|
||||
ownerEmails = (await _providerUserRepository.GetManyDetailsByProviderAsync(providerOrg.ProviderId, ProviderUserStatusType.Confirmed))
|
||||
ownerEmails =
|
||||
(await _providerUserRepository.GetManyDetailsByProviderAsync(providerOrg.ProviderId,
|
||||
ProviderUserStatusType.Confirmed))
|
||||
.Select(u => u.Email).Distinct();
|
||||
}
|
||||
else
|
||||
@ -1020,6 +1016,7 @@ public class OrganizationService : IOrganizationService
|
||||
ownerEmails = (await _organizationUserRepository.GetManyByMinimumRoleAsync(organization.Id,
|
||||
OrganizationUserType.Owner)).Select(u => u.Email).Distinct();
|
||||
}
|
||||
|
||||
var initialSeatCount = organization.Seats.Value;
|
||||
|
||||
await AdjustSeatsAsync(organization, seatsToAdd, ownerEmails);
|
||||
@ -1034,8 +1031,8 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId)
|
||||
public async Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey,
|
||||
Guid? callingUserId)
|
||||
{
|
||||
// Org User must be the same as the calling user and the organization ID associated with the user must match passed org ID
|
||||
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId, userId);
|
||||
@ -1063,30 +1060,35 @@ public class OrganizationService : IOrganizationService
|
||||
// Block the user from withdrawal if auto enrollment is enabled
|
||||
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyRequirements))
|
||||
{
|
||||
var resetPasswordPolicyRequirement = await _policyRequirementQuery.GetAsync<ResetPasswordPolicyRequirement>(userId);
|
||||
var resetPasswordPolicyRequirement =
|
||||
await _policyRequirementQuery.GetAsync<ResetPasswordPolicyRequirement>(userId);
|
||||
if (resetPasswordKey == null && resetPasswordPolicyRequirement.AutoEnrollEnabled(organizationId))
|
||||
{
|
||||
throw new BadRequestException("Due to an Enterprise Policy, you are not allowed to withdraw from account recovery.");
|
||||
throw new BadRequestException(
|
||||
"Due to an Enterprise Policy, you are not allowed to withdraw from account recovery.");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (resetPasswordKey == null && resetPasswordPolicy.Data != null)
|
||||
{
|
||||
var data = JsonSerializer.Deserialize<ResetPasswordDataModel>(resetPasswordPolicy.Data, JsonHelpers.IgnoreCase);
|
||||
var data = JsonSerializer.Deserialize<ResetPasswordDataModel>(resetPasswordPolicy.Data,
|
||||
JsonHelpers.IgnoreCase);
|
||||
|
||||
if (data?.AutoEnrollEnabled ?? false)
|
||||
{
|
||||
throw new BadRequestException("Due to an Enterprise Policy, you are not allowed to withdraw from account recovery.");
|
||||
throw new BadRequestException(
|
||||
"Due to an Enterprise Policy, you are not allowed to withdraw from account recovery.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
orgUser.ResetPasswordKey = resetPasswordKey;
|
||||
await _organizationUserRepository.ReplaceAsync(orgUser);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser, resetPasswordKey != null ?
|
||||
EventType.OrganizationUser_ResetPassword_Enroll : EventType.OrganizationUser_ResetPassword_Withdraw);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser,
|
||||
resetPasswordKey != null
|
||||
? EventType.OrganizationUser_ResetPassword_Enroll
|
||||
: EventType.OrganizationUser_ResetPassword_Withdraw);
|
||||
}
|
||||
|
||||
public async Task ImportAsync(Guid organizationId,
|
||||
@ -1123,15 +1125,16 @@ public class OrganizationService : IOrganizationService
|
||||
var existingUsersDict = existingExternalUsers.ToDictionary(u => u.ExternalId);
|
||||
var removeUsersSet = new HashSet<string>(removeUserExternalIds)
|
||||
.Except(newUsersSet)
|
||||
.Where(u => existingUsersDict.TryGetValue(u, out var existingUser) && existingUser.Type != OrganizationUserType.Owner)
|
||||
.Where(u => existingUsersDict.TryGetValue(u, out var existingUser) &&
|
||||
existingUser.Type != OrganizationUserType.Owner)
|
||||
.Select(u => existingUsersDict[u]);
|
||||
|
||||
await _organizationUserRepository.DeleteManyAsync(removeUsersSet.Select(u => u.Id));
|
||||
events.AddRange(removeUsersSet.Select(u => (
|
||||
u,
|
||||
EventType.OrganizationUser_Removed,
|
||||
(DateTime?)DateTime.UtcNow
|
||||
))
|
||||
u,
|
||||
EventType.OrganizationUser_Removed,
|
||||
(DateTime?)DateTime.UtcNow
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
@ -1144,10 +1147,10 @@ public class OrganizationService : IOrganizationService
|
||||
existingExternalUsersIdDict.ContainsKey(u.ExternalId));
|
||||
await _organizationUserRepository.DeleteManyAsync(usersToDelete.Select(u => u.Id));
|
||||
events.AddRange(usersToDelete.Select(u => (
|
||||
u,
|
||||
EventType.OrganizationUser_Removed,
|
||||
(DateTime?)DateTime.UtcNow
|
||||
))
|
||||
u,
|
||||
EventType.OrganizationUser_Removed,
|
||||
(DateTime?)DateTime.UtcNow
|
||||
))
|
||||
);
|
||||
foreach (var deletedUser in usersToDelete)
|
||||
{
|
||||
@ -1175,6 +1178,7 @@ public class OrganizationService : IOrganizationService
|
||||
existingExternalUsersIdDict.Add(orgUser.ExternalId, orgUser.Id);
|
||||
}
|
||||
}
|
||||
|
||||
await _organizationUserRepository.UpsertManyAsync(usersToUpsert);
|
||||
|
||||
// Add new users
|
||||
@ -1185,7 +1189,8 @@ public class OrganizationService : IOrganizationService
|
||||
var enoughSeatsAvailable = true;
|
||||
if (organization.Seats.HasValue)
|
||||
{
|
||||
var seatCounts = await _organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
var seatCounts =
|
||||
await _organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id);
|
||||
seatsAvailable = organization.Seats.Value - seatCounts.Total;
|
||||
enoughSeatsAvailable = seatsAvailable >= usersToAdd.Count;
|
||||
}
|
||||
@ -1218,7 +1223,8 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
var invitedUsers = await InviteUsersAsync(organizationId, invitingUserId: null, systemUser: eventSystemUser, userInvites);
|
||||
var invitedUsers = await InviteUsersAsync(organizationId, invitingUserId: null, systemUser: eventSystemUser,
|
||||
userInvites);
|
||||
foreach (var invitedUser in invitedUsers)
|
||||
{
|
||||
existingExternalUsersIdDict.Add(invitedUser.ExternalId, invitedUser.Id);
|
||||
@ -1255,7 +1261,8 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
await _eventService.LogGroupEventsAsync(
|
||||
savedGroups.Select(g => (g, EventType.Group_Created, (EventSystemUser?)eventSystemUser, (DateTime?)DateTime.UtcNow)));
|
||||
savedGroups.Select(g => (g, EventType.Group_Created, (EventSystemUser?)eventSystemUser,
|
||||
(DateTime?)DateTime.UtcNow)));
|
||||
|
||||
var updateGroups = existingExternalGroups
|
||||
.Where(g => groupsDict.ContainsKey(g.ExternalId))
|
||||
@ -1282,11 +1289,11 @@ public class OrganizationService : IOrganizationService
|
||||
await UpdateUsersAsync(group, groupsDict[group.ExternalId].ExternalUserIds,
|
||||
existingExternalUsersIdDict,
|
||||
existingGroupUsers.ContainsKey(group.Id) ? existingGroupUsers[group.Id] : null);
|
||||
|
||||
}
|
||||
|
||||
await _eventService.LogGroupEventsAsync(
|
||||
updateGroups.Select(g => (g, EventType.Group_Updated, (EventSystemUser?)eventSystemUser, (DateTime?)DateTime.UtcNow)));
|
||||
updateGroups.Select(g => (g, EventType.Group_Updated, (EventSystemUser?)eventSystemUser,
|
||||
(DateTime?)DateTime.UtcNow)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1298,10 +1305,12 @@ public class OrganizationService : IOrganizationService
|
||||
await _ssoUserRepository.DeleteAsync(userId, organizationId);
|
||||
if (organizationId.HasValue)
|
||||
{
|
||||
var organizationUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId.Value, userId);
|
||||
var organizationUser =
|
||||
await _organizationUserRepository.GetByOrganizationAsync(organizationId.Value, userId);
|
||||
if (organizationUser != null)
|
||||
{
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UnlinkedSso);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser,
|
||||
EventType.OrganizationUser_UnlinkedSso);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1423,7 +1432,7 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
if ((plan.ProductTier == ProductTierType.TeamsStarter &&
|
||||
upgrade.AdditionalSmSeats.GetValueOrDefault() > plan.PasswordManager.BaseSeats) ||
|
||||
upgrade.AdditionalSmSeats.GetValueOrDefault() > plan.PasswordManager.BaseSeats) ||
|
||||
(plan.ProductTier != ProductTierType.TeamsStarter &&
|
||||
upgrade.AdditionalSmSeats.GetValueOrDefault() > upgrade.AdditionalSeats))
|
||||
{
|
||||
@ -1446,7 +1455,8 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType, OrganizationUserType? oldType, Permissions permissions)
|
||||
public async Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
|
||||
OrganizationUserType? oldType, Permissions permissions)
|
||||
{
|
||||
if (await _currentContext.OrganizationOwner(organizationId))
|
||||
{
|
||||
@ -1473,13 +1483,15 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Custom users can not manage Admins or Owners.");
|
||||
}
|
||||
|
||||
if (newType == OrganizationUserType.Custom && !await ValidateCustomPermissionsGrant(organizationId, permissions))
|
||||
if (newType == OrganizationUserType.Custom &&
|
||||
!await ValidateCustomPermissionsGrant(organizationId, permissions))
|
||||
{
|
||||
throw new BadRequestException("Custom users can only grant the same custom permissions that they have.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType)
|
||||
public async Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId,
|
||||
OrganizationUserType newType)
|
||||
{
|
||||
if (newType != OrganizationUserType.Custom)
|
||||
{
|
||||
@ -1494,7 +1506,8 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
if (!organization.UseCustomPermissions)
|
||||
{
|
||||
throw new BadRequestException("To enable custom permissions the organization must be on an Enterprise plan.");
|
||||
throw new BadRequestException(
|
||||
"To enable custom permissions the organization must be on an Enterprise plan.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1600,7 +1613,8 @@ public class OrganizationService : IOrganizationService
|
||||
EventSystemUser systemUser)
|
||||
{
|
||||
await RepositoryRevokeUserAsync(organizationUser);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, systemUser);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked,
|
||||
systemUser);
|
||||
|
||||
if (organizationUser.UserId.HasValue)
|
||||
{
|
||||
@ -1615,7 +1629,8 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Already revoked.");
|
||||
}
|
||||
|
||||
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, new[] { organizationUser.Id }, includeProvider: true))
|
||||
if (!await _hasConfirmedOwnersExceptQuery.HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId,
|
||||
new[] { organizationUser.Id }, includeProvider: true))
|
||||
{
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
@ -1663,7 +1678,8 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("You cannot revoke yourself.");
|
||||
}
|
||||
|
||||
if (organizationUser.Type == OrganizationUserType.Owner && revokingUserId.HasValue && !deletingUserIsOwner)
|
||||
if (organizationUser.Type == OrganizationUserType.Owner && revokingUserId.HasValue &&
|
||||
!deletingUserIsOwner)
|
||||
{
|
||||
throw new BadRequestException("Only owners can revoke other owners.");
|
||||
}
|
||||
|
114
src/Core/AdminConsole/Services/OrganizationFactory.cs
Normal file
114
src/Core/AdminConsole/Services/OrganizationFactory.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Billing.Enums;
|
||||
using Bit.Core.Billing.Licenses;
|
||||
using Bit.Core.Billing.Licenses.Extensions;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Business;
|
||||
|
||||
namespace Bit.Core.AdminConsole.Services;
|
||||
|
||||
public static class OrganizationFactory
|
||||
{
|
||||
public static Organization Create(
|
||||
User owner,
|
||||
ClaimsPrincipal claimsPrincipal,
|
||||
string publicKey,
|
||||
string privateKey) => new()
|
||||
{
|
||||
Name = claimsPrincipal.GetValue<string>(OrganizationLicenseConstants.Name),
|
||||
BillingEmail = claimsPrincipal.GetValue<string>(OrganizationLicenseConstants.BillingEmail),
|
||||
BusinessName = claimsPrincipal.GetValue<string>(OrganizationLicenseConstants.BusinessName),
|
||||
PlanType = claimsPrincipal.GetValue<PlanType>(OrganizationLicenseConstants.PlanType),
|
||||
Seats = claimsPrincipal.GetValue<int?>(OrganizationLicenseConstants.Seats),
|
||||
MaxCollections = claimsPrincipal.GetValue<short?>(OrganizationLicenseConstants.MaxCollections),
|
||||
MaxStorageGb = 10240,
|
||||
UsePolicies = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UsePolicies),
|
||||
UseSso = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseSso),
|
||||
UseKeyConnector = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseKeyConnector),
|
||||
UseScim = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseScim),
|
||||
UseGroups = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseGroups),
|
||||
UseDirectory = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseDirectory),
|
||||
UseEvents = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseEvents),
|
||||
UseTotp = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseTotp),
|
||||
Use2fa = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.Use2fa),
|
||||
UseApi = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseApi),
|
||||
UseResetPassword = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseResetPassword),
|
||||
Plan = claimsPrincipal.GetValue<string>(OrganizationLicenseConstants.Plan),
|
||||
SelfHost = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.SelfHost),
|
||||
UsersGetPremium = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UsersGetPremium),
|
||||
UseCustomPermissions =
|
||||
claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseCustomPermissions),
|
||||
Gateway = null,
|
||||
GatewayCustomerId = null,
|
||||
GatewaySubscriptionId = null,
|
||||
ReferenceData = owner.ReferenceData,
|
||||
Enabled = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.Enabled),
|
||||
ExpirationDate = claimsPrincipal.GetValue<DateTime?>(OrganizationLicenseConstants.Expires),
|
||||
LicenseKey = claimsPrincipal.GetValue<string>(OrganizationLicenseConstants.LicenseKey),
|
||||
PublicKey = publicKey,
|
||||
PrivateKey = privateKey,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Status = OrganizationStatusType.Created,
|
||||
UsePasswordManager = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UsePasswordManager),
|
||||
UseSecretsManager = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseSecretsManager),
|
||||
SmSeats = claimsPrincipal.GetValue<int?>(OrganizationLicenseConstants.SmSeats),
|
||||
SmServiceAccounts = claimsPrincipal.GetValue<int?>(OrganizationLicenseConstants.SmServiceAccounts),
|
||||
UseRiskInsights = claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseRiskInsights),
|
||||
UseOrganizationDomains =
|
||||
claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseOrganizationDomains),
|
||||
UseAdminSponsoredFamilies =
|
||||
claimsPrincipal.GetValue<bool>(OrganizationLicenseConstants.UseAdminSponsoredFamilies),
|
||||
};
|
||||
|
||||
public static Organization Create(
|
||||
User owner,
|
||||
OrganizationLicense license,
|
||||
string publicKey,
|
||||
string privateKey) => new()
|
||||
{
|
||||
Name = license.Name,
|
||||
BillingEmail = license.BillingEmail,
|
||||
BusinessName = license.BusinessName,
|
||||
PlanType = license.PlanType,
|
||||
Seats = license.Seats,
|
||||
MaxCollections = license.MaxCollections,
|
||||
MaxStorageGb = 10240,
|
||||
UsePolicies = license.UsePolicies,
|
||||
UseSso = license.UseSso,
|
||||
UseKeyConnector = license.UseKeyConnector,
|
||||
UseScim = license.UseScim,
|
||||
UseGroups = license.UseGroups,
|
||||
UseDirectory = license.UseDirectory,
|
||||
UseEvents = license.UseEvents,
|
||||
UseTotp = license.UseTotp,
|
||||
Use2fa = license.Use2fa,
|
||||
UseApi = license.UseApi,
|
||||
UseResetPassword = license.UseResetPassword,
|
||||
Plan = license.Plan,
|
||||
SelfHost = license.SelfHost,
|
||||
UsersGetPremium = license.UsersGetPremium,
|
||||
UseCustomPermissions = license.UseCustomPermissions,
|
||||
Gateway = null,
|
||||
GatewayCustomerId = null,
|
||||
GatewaySubscriptionId = null,
|
||||
ReferenceData = owner.ReferenceData,
|
||||
Enabled = license.Enabled,
|
||||
ExpirationDate = license.Expires,
|
||||
LicenseKey = license.LicenseKey,
|
||||
PublicKey = publicKey,
|
||||
PrivateKey = privateKey,
|
||||
CreationDate = DateTime.UtcNow,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
Status = OrganizationStatusType.Created,
|
||||
UsePasswordManager = license.UsePasswordManager,
|
||||
UseSecretsManager = license.UseSecretsManager,
|
||||
SmSeats = license.SmSeats,
|
||||
SmServiceAccounts = license.SmServiceAccounts,
|
||||
UseRiskInsights = license.UseRiskInsights,
|
||||
UseOrganizationDomains = license.UseOrganizationDomains,
|
||||
UseAdminSponsoredFamilies = license.UseAdminSponsoredFamilies,
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user