Skip to content

Commit 7e99dff

Browse files
committed
refactor: update organization handling in SCIM user models and commands
- Replaced references to InviteOrganization with Organization in SCIM user request models and related commands for consistency. - Enhanced null safety by ensuring proper handling of organization properties across various components. - Updated tests to reflect changes in organization handling and ensure functionality remains intact.
1 parent 5ad7d0d commit 7e99dff

6 files changed

Lines changed: 121 additions & 56 deletions

File tree

bitwarden_license/src/Scim/Models/ScimUserRequestModel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// FIXME: Update this file to be null safe and then delete the line below
22
#nullable disable
33

4+
using Bit.Core.AdminConsole.Entities;
45
using Bit.Core.AdminConsole.Enums;
5-
using Bit.Core.AdminConsole.Models.Business;
66
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
77
using Bit.Core.Enums;
88
using Bit.Core.Exceptions;
@@ -34,7 +34,7 @@ public OrganizationUserInvite ToOrganizationUserInvite(ScimProviderType scimProv
3434

3535
public InviteOrganizationUsersRequest ToRequest(
3636
ScimProviderType scimProvider,
37-
InviteOrganization inviteOrganization,
37+
Organization organization,
3838
DateTimeOffset performedAt)
3939
{
4040
var email = EmailForInvite(scimProvider);
@@ -52,7 +52,7 @@ public InviteOrganizationUsersRequest ToRequest(
5252
externalId: ExternalIdForInvite()
5353
)
5454
],
55-
inviteOrganization: inviteOrganization,
55+
organization: organization,
5656
performedBy: Guid.Empty, // SCIM does not have a user id
5757
performedAt: performedAt);
5858
}

bitwarden_license/src/Scim/Users/PostUserCommand.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22

33
using Bit.Core;
44
using Bit.Core.AdminConsole.Enums;
5-
using Bit.Core.AdminConsole.Models.Business;
65
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
76
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Errors;
87
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
98
using Bit.Core.AdminConsole.Utilities.Commands;
10-
using Bit.Core.Billing.Pricing;
119
using Bit.Core.Billing.Services;
1210
using Bit.Core.Enums;
1311
using Bit.Core.Exceptions;
@@ -29,8 +27,7 @@ public class PostUserCommand(
2927
IScimContext scimContext,
3028
IFeatureService featureService,
3129
IInviteOrganizationUsersCommand inviteOrganizationUsersCommand,
32-
TimeProvider timeProvider,
33-
IPricingClient pricingClient)
30+
TimeProvider timeProvider)
3431
: IPostUserCommand
3532
{
3633
public async Task<OrganizationUserUserDetails?> PostUserAsync(Guid organizationId, ScimUserRequestModel model)
@@ -55,15 +52,13 @@ public class PostUserCommand(
5552
throw new NotFoundException();
5653
}
5754

58-
var plan = await pricingClient.GetPlanOrThrow(organization.PlanType);
59-
6055
var request = model.ToRequest(
6156
scimProvider: scimProvider,
62-
inviteOrganization: new InviteOrganization(organization, plan),
57+
organization: organization,
6358
performedAt: timeProvider.GetUtcNow());
6459

6560
var orgUsers = await organizationUserRepository
66-
.GetManyDetailsByOrganizationAsync(request.InviteOrganization.OrganizationId);
61+
.GetManyDetailsByOrganizationAsync(request.Organization.Id);
6762

6863
if (orgUsers.Any(existingUser =>
6964
request.Invites.First().Email.Equals(existingUser.Email, StringComparison.OrdinalIgnoreCase) ||

src/Api/AdminConsole/Public/Controllers/MembersController.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Bit.Api.AdminConsole.Public.Models.Response;
44
using Bit.Api.Models.Public.Response;
55
using Bit.Core;
6-
using Bit.Core.AdminConsole.Models.Business;
76
using Bit.Core.AdminConsole.Models.Data;
87
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
98
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
@@ -14,7 +13,6 @@
1413
using Bit.Core.AdminConsole.Repositories;
1514
using Bit.Core.AdminConsole.Utilities.Commands;
1615
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
17-
using Bit.Core.Billing.Pricing;
1816
using Bit.Core.Billing.Services;
1917
using Bit.Core.Context;
2018
using Bit.Core.Enums;
@@ -46,7 +44,6 @@ public class MembersController : Controller
4644
private readonly IRestoreOrganizationUserCommand _restoreOrganizationUserCommand;
4745
private readonly IFeatureService _featureService;
4846
private readonly IInviteOrganizationUsersCommand _inviteOrganizationUsersCommand;
49-
private readonly IPricingClient _pricingClient;
5047
private readonly TimeProvider _timeProvider;
5148

5249
public MembersController(
@@ -65,7 +62,6 @@ public MembersController(
6562
IRestoreOrganizationUserCommand restoreOrganizationUserCommand,
6663
IFeatureService featureService,
6764
IInviteOrganizationUsersCommand inviteOrganizationUsersCommand,
68-
IPricingClient pricingClient,
6965
TimeProvider timeProvider)
7066
{
7167
_organizationUserRepository = organizationUserRepository;
@@ -83,7 +79,6 @@ public MembersController(
8379
_restoreOrganizationUserCommand = restoreOrganizationUserCommand;
8480
_featureService = featureService;
8581
_inviteOrganizationUsersCommand = inviteOrganizationUsersCommand;
86-
_pricingClient = pricingClient;
8782
_timeProvider = timeProvider;
8883
}
8984

@@ -194,9 +189,7 @@ private async Task<IActionResult> PostInviteUserAsync_vNext(
194189
Core.AdminConsole.Entities.Organization organization,
195190
bool hasStandaloneSecretsManager)
196191
{
197-
var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
198-
var inviteOrganization = new InviteOrganization(organization, plan);
199-
var request = model.ToInviteRequest(inviteOrganization, hasStandaloneSecretsManager, Guid.Empty, _timeProvider.GetUtcNow());
192+
var request = model.ToInviteRequest(organization, hasStandaloneSecretsManager, Guid.Empty, _timeProvider.GetUtcNow());
200193

201194
var result = await _inviteOrganizationUsersCommand.InviteImportedOrganizationUsersAsync(request);
202195

src/Api/AdminConsole/Public/Models/Request/MemberCreateRequestModel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#nullable disable
33

44
using System.ComponentModel.DataAnnotations;
5-
using Bit.Core.AdminConsole.Models.Business;
5+
using Bit.Core.AdminConsole.Entities;
66
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
77
using Bit.Core.Entities;
88
using Bit.Core.Enums;
@@ -48,7 +48,7 @@ public OrganizationUserInvite ToOrganizationUserInvite()
4848
}
4949

5050
public InviteOrganizationUsersRequest ToInviteRequest(
51-
InviteOrganization inviteOrganization,
51+
Organization organization,
5252
bool accessSecretsManager,
5353
Guid performedBy,
5454
DateTimeOffset performedAt)
@@ -70,7 +70,7 @@ public InviteOrganizationUsersRequest ToInviteRequest(
7070
externalId: ExternalId,
7171
accessSecretsManager: accessSecretsManager)
7272
],
73-
inviteOrganization: inviteOrganization,
73+
organization: organization,
7474
performedBy: performedBy,
7575
performedAt: performedAt);
7676
}

src/Core/AdminConsole/OrganizationFeatures/OrganizationUsers/InviteUsers/InviteOrganizationUsersCommand.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
using Bit.Core.AdminConsole.Utilities.Commands;
1313
using Bit.Core.AdminConsole.Utilities.Errors;
1414
using Bit.Core.AdminConsole.Utilities.Validation;
15+
using Bit.Core.Billing.Pricing;
1516
using Bit.Core.Entities;
1617
using Bit.Core.Enums;
1718
using Bit.Core.Models.Business;
1819
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
1920
using Bit.Core.Repositories;
2021
using Bit.Core.Services;
22+
using Bit.Core.Settings;
2123
using Microsoft.Extensions.Logging;
2224

2325
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
@@ -32,7 +34,9 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
3234
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
3335
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand,
3436
IProviderOrganizationRepository providerOrganizationRepository,
35-
IProviderUserRepository providerUserRepository
37+
IProviderUserRepository providerUserRepository,
38+
IPricingClient pricingClient,
39+
IGlobalSettings globalSettings
3640
) : IInviteOrganizationUsersCommand
3741
{
3842

@@ -83,7 +87,7 @@ public async Task<CommandResult<InviteOrganizationUsersResponse>> InviteImported
8387
return new Failure<InviteOrganizationUsersResponse>(
8488
new Error<InviteOrganizationUsersResponse>(
8589
failure.Error.Message,
86-
new InviteOrganizationUsersResponse(failure.Error.ErroredValue.InvitedUsers, request.InviteOrganization.OrganizationId)
90+
new InviteOrganizationUsersResponse(failure.Error.ErroredValue.InvitedUsers, request.Organization.Id)
8791
)
8892
);
8993

@@ -97,34 +101,45 @@ public async Task<CommandResult<InviteOrganizationUsersResponse>> InviteImported
97101

98102
await eventService.LogOrganizationUserEventsAsync(events);
99103

100-
return new Success<InviteOrganizationUsersResponse>(new InviteOrganizationUsersResponse(success.Value.InvitedUsers, request.InviteOrganization.OrganizationId)
104+
return new Success<InviteOrganizationUsersResponse>(new InviteOrganizationUsersResponse(success.Value.InvitedUsers, request.Organization.Id)
101105
);
102106

103107
default:
104108
return new Failure<InviteOrganizationUsersResponse>(
105109
new InvalidResultTypeError<InviteOrganizationUsersResponse>(
106-
new InviteOrganizationUsersResponse(request.InviteOrganization.OrganizationId)));
110+
new InviteOrganizationUsersResponse(request.Organization.Id)));
107111
}
108112
}
109113

110114
private async Task<CommandResult<InviteOrganizationUsersResponse>> InviteOrganizationUsersAsync(InviteOrganizationUsersRequest request)
111115
{
116+
var plan = await pricingClient.GetPlan(request.Organization.PlanType);
117+
if (plan is null && !globalSettings.SelfHosted)
118+
{
119+
return new Failure<InviteOrganizationUsersResponse>(
120+
new Error<InviteOrganizationUsersResponse>(
121+
"Organization plan could not be found.",
122+
new InviteOrganizationUsersResponse(request.Organization.Id)));
123+
}
124+
125+
var inviteOrganization = new InviteOrganization(request.Organization, plan);
126+
112127
var invitesToSend = (await FilterExistingUsersAsync(request)).ToArray();
113128

114129
if (invitesToSend.Length == 0)
115130
{
116131
return new Failure<InviteOrganizationUsersResponse>(new NoUsersToInviteError(
117-
new InviteOrganizationUsersResponse(request.InviteOrganization.OrganizationId)));
132+
new InviteOrganizationUsersResponse(inviteOrganization.OrganizationId)));
118133
}
119134

120135
var validationResult = await inviteUsersValidator.ValidateAsync(new InviteOrganizationUsersValidationRequest
121136
{
122137
Invites = invitesToSend.ToArray(),
123-
InviteOrganization = request.InviteOrganization,
138+
InviteOrganization = inviteOrganization,
124139
PerformedBy = request.PerformedBy,
125140
PerformedAt = request.PerformedAt,
126-
OccupiedPmSeats = (await organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(request.InviteOrganization.OrganizationId)).Total,
127-
OccupiedSmSeats = await organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(request.InviteOrganization.OrganizationId)
141+
OccupiedPmSeats = (await organizationRepository.GetOccupiedSeatCountByOrganizationIdAsync(inviteOrganization.OrganizationId)).Total,
142+
OccupiedSmSeats = await organizationUserRepository.GetOccupiedSmSeatCountByOrganizationIdAsync(inviteOrganization.OrganizationId)
128143
});
129144

130145
if (validationResult is Invalid<InviteOrganizationUsersValidationRequest> invalid)
@@ -177,7 +192,7 @@ private async Task<CommandResult<InviteOrganizationUsersResponse>> InviteOrganiz
177192
private async Task<IEnumerable<OrganizationUserInviteCommandModel>> FilterExistingUsersAsync(InviteOrganizationUsersRequest request)
178193
{
179194
var existingEmails = new HashSet<string>(await organizationUserRepository.SelectKnownEmailsAsync(
180-
request.InviteOrganization.OrganizationId, request.Invites.Select(i => i.Email), false),
195+
request.Organization.Id, request.Invites.Select(i => i.Email), false),
181196
StringComparer.OrdinalIgnoreCase);
182197

183198
return request.Invites
@@ -198,7 +213,8 @@ private async Task RevertPasswordManagerChangesAsync(Valid<InviteOrganizationUse
198213

199214
private async Task RevertSecretsManagerChangesAsync(Valid<InviteOrganizationUsersValidationRequest> validatedResult, Organization organization, int? initialSmSeats)
200215
{
201-
if (validatedResult.Value.SecretsManagerSubscriptionUpdate?.SmSeatsChanged is true)
216+
if (validatedResult.Value.SecretsManagerSubscriptionUpdate?.SmSeatsChanged is true
217+
&& validatedResult.Value.InviteOrganization.Plan is not null)
202218
{
203219
var smSubscriptionUpdateRevert = new SecretsManagerSubscriptionUpdate(
204220
organization: organization,

0 commit comments

Comments
 (0)