-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
๏ปฟ#nullable enable | ||
using Bit.Core.AdminConsole.OrganizationFeatures.Shared.Authorization; | ||
using Bit.Core.Context; | ||
using Microsoft.AspNetCore.Authorization; | ||
|
||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Authorization; | ||
|
||
public class OrganizationAuthorizationHandler | ||
: AuthorizationHandler<OrganizationOperationRequirement, OrganizationScope> | ||
{ | ||
private readonly ICurrentContext _currentContext; | ||
|
||
public OrganizationAuthorizationHandler(ICurrentContext currentContext) | ||
{ | ||
_currentContext = currentContext; | ||
} | ||
|
||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, | ||
OrganizationOperationRequirement requirement, OrganizationScope organizationScope) | ||
{ | ||
var authorized = false; | ||
|
||
switch (requirement) | ||
{ | ||
case not null when requirement.Name == nameof(OrganizationOperations.Update): | ||
authorized = await CanUpdateAsync(organizationScope); | ||
break; | ||
} | ||
|
||
if (authorized) | ||
{ | ||
context.Succeed(requirement!); | ||
} | ||
} | ||
|
||
private async Task<bool> CanUpdateAsync(Guid organizationId) | ||
{ | ||
var organization = _currentContext.GetOrganization(organizationId); | ||
if (organization != null) | ||
{ | ||
return true; | ||
} | ||
|
||
// Allow provider users to update organization data if they are a provider for the target organization | ||
return await _currentContext.ProviderUserForOrgAsync(organizationId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
๏ปฟusing Microsoft.AspNetCore.Authorization.Infrastructure; | ||
|
||
namespace Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Authorization; | ||
|
||
public class OrganizationOperationRequirement : OperationAuthorizationRequirement; | ||
|
||
public static class OrganizationOperations | ||
{ | ||
public static OrganizationOperationRequirement Update = new() { Name = nameof(Update) }; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
๏ปฟusing Bit.Core.Entities; | ||
using Bit.Core.Enums; | ||
using Bit.Core.Exceptions; | ||
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( | ||
jrmccannon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
IOrganizationService organizationService, | ||
ICollectionRepository collectionRepository, | ||
IOrganizationRepository organizationRepository | ||
) | ||
{ | ||
_organizationService = organizationService; | ||
_collectionRepository = collectionRepository; | ||
_organizationRepository = organizationRepository; | ||
} | ||
|
||
public async Task InitPendingOrganizationAsync(Guid userId, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName) | ||
{ | ||
await _organizationService.ValidateSignUpPoliciesAsync(userId); | ||
|
||
var org = await _organizationRepository.GetByIdAsync(organizationId); | ||
|
||
if (org.Enabled) | ||
{ | ||
throw new BadRequestException("Organization is already enabled."); | ||
} | ||
|
||
if (org.Status != OrganizationStatusType.Pending) | ||
{ | ||
throw new BadRequestException("Organization is not on a Pending status."); | ||
} | ||
|
||
if (!string.IsNullOrEmpty(org.PublicKey)) | ||
{ | ||
throw new BadRequestException("Organization already has a Public Key."); | ||
} | ||
|
||
if (!string.IsNullOrEmpty(org.PrivateKey)) | ||
{ | ||
throw new BadRequestException("Organization already has a Private Key."); | ||
} | ||
|
||
org.Enabled = true; | ||
org.Status = OrganizationStatusType.Created; | ||
org.PublicKey = publicKey; | ||
org.PrivateKey = privateKey; | ||
|
||
await _organizationService.UpdateAsync(org); | ||
|
||
if (!string.IsNullOrWhiteSpace(collectionName)) | ||
{ | ||
// give the owner Can Manage access over the default collection | ||
List<CollectionAccessSelection> defaultOwnerAccess = | ||
[new CollectionAccessSelection { Id = organizationUserId, HidePasswords = false, ReadOnly = false, Manage = true }]; | ||
|
||
var defaultCollection = new Collection | ||
{ | ||
Name = collectionName, | ||
OrganizationId = org.Id | ||
}; | ||
await _collectionRepository.CreateAsync(defaultCollection, null, defaultOwnerAccess); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
๏ปฟ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 InitPendingOrganizationAsync(Guid userId, Guid organizationId, Guid organizationUserId, string publicKey, string privateKey, string collectionName); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains; | ||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationDomains.Interfaces; | ||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations; | ||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Authorization; | ||
using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Interfaces; | ||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers; | ||
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Authorization; | ||
|
@@ -173,6 +174,7 @@ private static void AddOrganizationUserCommandsQueries(this IServiceCollection s | |
|
||
services.AddScoped<IAuthorizationHandler, OrganizationUserUserMiniDetailsAuthorizationHandler>(); | ||
services.AddScoped<IAuthorizationHandler, OrganizationUserUserDetailsAuthorizationHandler>(); | ||
services.AddScoped<IAuthorizationHandler, OrganizationAuthorizationHandler>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this goes here... Which method should contain this, |
||
services.AddScoped<IHasConfirmedOwnersExceptQuery, HasConfirmedOwnersExceptQuery>(); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not fully sure which checks here would really be best