Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-19332] Create InitPendingOrganizationCommand #5584

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
private readonly IPricingClient _pricingClient;
private readonly IConfirmOrganizationUserCommand _confirmOrganizationUserCommand;
private readonly IRestoreOrganizationUserCommand _restoreOrganizationUserCommand;
private readonly IInitPendingOrganizationCommand _initPendingOrganizationCommand;

public OrganizationUsersController(
IOrganizationRepository organizationRepository,
Expand All @@ -89,7 +90,8 @@
IFeatureService featureService,
IPricingClient pricingClient,
IConfirmOrganizationUserCommand confirmOrganizationUserCommand,
IRestoreOrganizationUserCommand restoreOrganizationUserCommand)
IRestoreOrganizationUserCommand restoreOrganizationUserCommand,
IInitPendingOrganizationCommand initPendingOrganizationCommand)
{
_organizationRepository = organizationRepository;
_organizationUserRepository = organizationUserRepository;
Expand All @@ -116,6 +118,7 @@
_pricingClient = pricingClient;
_confirmOrganizationUserCommand = confirmOrganizationUserCommand;
_restoreOrganizationUserCommand = restoreOrganizationUserCommand;
_initPendingOrganizationCommand = initPendingOrganizationCommand;
}

[HttpGet("{id}")]
Expand Down Expand Up @@ -313,7 +316,7 @@
throw new UnauthorizedAccessException();
}

await _organizationService.InitPendingOrganization(user.Id, orgId, organizationUserId, model.Keys.PublicKey, model.Keys.EncryptedPrivateKey, model.CollectionName);
await _initPendingOrganizationCommand.InitPendingOrganizationAsync(user.Id, orgId, organizationUserId, model.Keys.PublicKey, model.Keys.EncryptedPrivateKey, model.CollectionName);

Check warning on line 319 in src/Api/AdminConsole/Controllers/OrganizationUsersController.cs

View check run for this annotation

Codecov / codecov/patch

src/Api/AdminConsole/Controllers/OrganizationUsersController.cs#L319

Added line #L319 was not covered by tests
await _acceptOrgUserCommand.AcceptOrgUserByEmailTokenAsync(organizationUserId, user, model.Token, _userService);
await _confirmOrganizationUserCommand.ConfirmUserAsync(orgId, organizationUserId, model.Key, user.Id);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
๏ปฟusing Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Commands;
using Bit.Core.Models.Data;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;

namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers;

public class InitPendingOrganizationCommand : IInitPendingOrganizationCommand
{

private readonly IOrganizationService _organizationService;
private readonly ICollectionRepository _collectionRepository;
private readonly IOrganizationRepository _organizationRepository;

public InitPendingOrganizationCommand(
IOrganizationService organizationService,
ICollectionRepository collectionRepository,
IOrganizationRepository organizationRepository
)
{
_organizationService = organizationService;
_collectionRepository = collectionRepository;
_organizationRepository = organizationRepository;
}

Check warning on line 28 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L19-L28

Added lines #L19 - L28 were not covered by tests

public async Task<CommandResult> InitPendingOrganizationAsync(Guid userId, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName)
{
await _organizationService.ValidateSignUpPoliciesAsync(userId);

Check warning on line 32 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L31-L32

Added lines #L31 - L32 were not covered by tests

var org = await _organizationRepository.GetByIdAsync(organizationId);

Check warning on line 34 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L34

Added line #L34 was not covered by tests

if (org.Enabled)
{
throw new BadRequestException("Organization is already enabled.");

Check warning on line 38 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L37-L38

Added lines #L37 - L38 were not covered by tests
}

if (org.Status != OrganizationStatusType.Pending)
{
throw new BadRequestException("Organization is not on a Pending status.");

Check warning on line 43 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L42-L43

Added lines #L42 - L43 were not covered by tests
}

if (!string.IsNullOrEmpty(org.PublicKey))
{
throw new BadRequestException("Organization already has a Public Key.");

Check warning on line 48 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L47-L48

Added lines #L47 - L48 were not covered by tests
}

if (!string.IsNullOrEmpty(org.PrivateKey))
{
throw new BadRequestException("Organization already has a Private Key.");

Check warning on line 53 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L52-L53

Added lines #L52 - L53 were not covered by tests
}

org.Enabled = true;
org.Status = OrganizationStatusType.Created;
org.PublicKey = publicKey;
org.PrivateKey = privateKey;

Check warning on line 59 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L56-L59

Added lines #L56 - L59 were not covered by tests

await _organizationService.UpdateAsync(org);

Check warning on line 61 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L61

Added line #L61 was not covered by tests

if (!string.IsNullOrWhiteSpace(collectionName))
{

Check warning on line 64 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L64

Added line #L64 was not covered by tests
// give the owner Can Manage access over the default collection
List<CollectionAccessSelection> defaultOwnerAccess =
[new CollectionAccessSelection { Id = organizationUserId, HidePasswords = false, ReadOnly = false, Manage = true }];

Check warning on line 67 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L66-L67

Added lines #L66 - L67 were not covered by tests

var defaultCollection = new Collection
{
Name = collectionName,
OrganizationId = org.Id
};
await _collectionRepository.CreateAsync(defaultCollection, null, defaultOwnerAccess);
}

Check warning on line 75 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L69-L75

Added lines #L69 - L75 were not covered by tests

return new CommandResult();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to return a CommandResult. Then I would suggest you return a Success<InitializePendingOrganizationResponse> and create a new empty record model InitializePendingOrganizationResponse.

This way if we want to return something later, we can simply add a property to the model and populate it.

}

Check warning on line 78 in src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs

View check run for this annotation

Codecov / codecov/patch

src/Core/AdminConsole/OrganizationFeatures/Organizations/InitPendingOrganizationCommand.cs#L77-L78

Added lines #L77 - L78 were not covered by tests
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
๏ปฟusing Bit.Core.Models.Commands;
namespace Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;

public interface IInitPendingOrganizationCommand
{
/// <summary>
/// Accept an invitation to initialize and join an organization created via the Admin Portal
/// </summary>
Task<CommandResult> InitPendingOrganizationAsync(Guid userId, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName);
}
1 change: 1 addition & 0 deletions src/Core/AdminConsole/Services/IOrganizationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,5 @@ Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId
Task ValidateOrganizationUserUpdatePermissions(Guid organizationId, OrganizationUserType newType,
OrganizationUserType? oldType, Permissions permissions);
Task ValidateOrganizationCustomPermissionsEnabledAsync(Guid organizationId, OrganizationUserType newType);
Task ValidateSignUpPoliciesAsync(Guid ownerId);
Copy link
Member

@eliykat eliykat Apr 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is just a single policy check - I don't think it needs to be reused. I suggest copying it into the new command rather than exposing it as public here. One less tie back to OrganizationService. (It's also what we did in the cloud org signup command)

}
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ await _referenceEventService.RaiseEventAsync(
return returnValue;
}

private async Task ValidateSignUpPoliciesAsync(Guid ownerId)
public async Task ValidateSignUpPoliciesAsync(Guid ownerId)
{
var anySingleOrgPolicies = await _policyService.AnyPoliciesApplicableToUserAsync(ownerId, PolicyType.SingleOrg);
if (anySingleOrgPolicies)
Expand Down
Loading