diff --git a/bitwarden_license/src/Scim/Users/PostUserCommand.cs b/bitwarden_license/src/Scim/Users/PostUserCommand.cs index da81f9a543..d62de90d4f 100644 --- a/bitwarden_license/src/Scim/Users/PostUserCommand.cs +++ b/bitwarden_license/src/Scim/Users/PostUserCommand.cs @@ -1,9 +1,12 @@ #nullable enable using Bit.Core; +using Bit.Core.AdminConsole.Entities; +using Bit.Core.AdminConsole.Enums; using Bit.Core.AdminConsole.Models.Business; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models; +using Bit.Core.Billing.Pricing; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Commands; @@ -24,7 +27,9 @@ public class PostUserCommand( IScimContext scimContext, IFeatureService featureService, IInviteOrganizationUsersCommand inviteOrganizationUsersCommand, - TimeProvider timeProvider) + TimeProvider timeProvider, + IPricingClient pricingClient, + ILogger logger) : IPostUserCommand { public async Task PostUserAsync(Guid organizationId, ScimUserRequestModel model) @@ -54,16 +59,18 @@ public class PostUserCommand( } var organization = await organizationRepository.GetByIdAsync(organizationId); + + if (organization == null) + { + throw new NotFoundException(); + } + var hasStandaloneSecretsManager = await paymentService.HasSecretsManagerStandalone(organization); invite.AccessSecretsManager = hasStandaloneSecretsManager; if (featureService.IsEnabled(FeatureFlagKeys.ScimInviteUserOptimization)) { - return await InviteScimOrganizationUserAsync(model.ToRequest( - scimProvider: scimProvider, - hasSecretsManager: hasStandaloneSecretsManager, - organization: new OrganizationDto(organization), - performedAt: timeProvider.GetUtcNow())); + return await InviteScimOrganizationUserAsync(model, organization, scimProvider, hasStandaloneSecretsManager); } var invitedOrgUser = await organizationService.InviteUserAsync(organizationId, invitingUserId: null, EventSystemUser.SCIM, @@ -73,8 +80,22 @@ public class PostUserCommand( return orgUser; } - private async Task InviteScimOrganizationUserAsync(InviteScimOrganizationUserRequest request) + private async Task InviteScimOrganizationUserAsync(ScimUserRequestModel model, Organization organization, ScimProviderType scimProvider, bool hasStandaloneSecretsManager) { + var plan = await pricingClient.GetPlan(organization.PlanType); + + if (plan == null) + { + logger.LogError("Plan {planType} not found for organization {organizationId}", organization.PlanType, organization.Id); + return null; + } + + var request = model.ToRequest( + scimProvider: scimProvider, + hasSecretsManager: hasStandaloneSecretsManager, + organization: new OrganizationDto(organization, plan), + performedAt: timeProvider.GetUtcNow()); + var result = await inviteOrganizationUsersCommand.InviteScimOrganizationUserAsync(request); if (result is not Success successfulResponse) diff --git a/src/Core/AdminConsole/Models/Business/OrganizationDto.cs b/src/Core/AdminConsole/Models/Business/OrganizationDto.cs index 8558a8a97d..14f2cfee32 100644 --- a/src/Core/AdminConsole/Models/Business/OrganizationDto.cs +++ b/src/Core/AdminConsole/Models/Business/OrganizationDto.cs @@ -21,16 +21,21 @@ public record OrganizationDto } - public OrganizationDto(Organization organization) + + public OrganizationDto(Organization organization, Plan plan) { OrganizationId = organization.Id; Seats = organization.Seats; MaxAutoScaleSeats = organization.MaxAutoscaleSeats; SmSeats = organization.SmSeats; SmMaxAutoScaleSeats = organization.MaxAutoscaleSmSeats; - Plan = StaticStore.GetPlan(organization.PlanType); + Plan = plan; GatewayCustomerId = organization.GatewayCustomerId; GatewaySubscriptionId = organization.GatewaySubscriptionId; UseSecretsManager = organization.UseSecretsManager; } + + public OrganizationDto(Organization organization) : this(organization, StaticStore.GetPlan(organization.PlanType)) + { + } } diff --git a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs index 4824259c53..98e4357cb6 100644 --- a/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs +++ b/src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs @@ -94,7 +94,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService, .Select(MapToDataModel(request.PerformedAt)) .ToArray(); - var organization = await organizationRepository.GetByIdAsync(validatedRequest.Value.Organization.OrganizationId); + var organization = await organizationRepository.GetByIdAsync(validatedRequest!.Value.Organization.OrganizationId); try { await organizationUserRepository.CreateManyAsync(organizationUserCollection); @@ -142,7 +142,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService, { if (valid.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd < 0) { - var updateRevert = new SecretsManagerSubscriptionUpdate(organization, false) + var updateRevert = new SecretsManagerSubscriptionUpdate(organization, valid.Value.Organization.Plan, false) { SmSeats = valid.Value.SecretsManagerSubscriptionUpdate.Seats }; @@ -185,7 +185,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService, .Distinct(); await mailService.SendOrganizationMaxSeatLimitReachedEmailAsync(organization, - valid.Value.PasswordManagerSubscriptionUpdate.MaxAutoScaleSeats.Value, ownerEmails); + valid.Value.PasswordManagerSubscriptionUpdate.MaxAutoScaleSeats.Value!, ownerEmails); } catch (Exception ex) { @@ -200,7 +200,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService, return; } - var subscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, true) + var subscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, valid.Value.Organization.Plan, true) .AdjustSeats(valid.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd); await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(subscriptionUpdate);