-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SM-923] Add project service accounts access policies management endp…
…oints (#3993) * Add new models * Update repositories * Add new authz handler * Add new query * Add new command * Add authz, command, and query to DI * Add new endpoint to controller * Add query unit tests * Add api unit tests * Add api integration tests
- Loading branch information
1 parent
e302ee1
commit 7f8cea5
Showing
23 changed files
with
1,559 additions
and
29 deletions.
There are no files selected for viewing
107 changes: 107 additions & 0 deletions
107
...zationHandlers/AccessPolicies/ProjectServiceAccountsAccessPoliciesAuthorizationHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
#nullable enable | ||
using Bit.Core.Context; | ||
using Bit.Core.Enums; | ||
using Bit.Core.SecretsManager.AuthorizationRequirements; | ||
using Bit.Core.SecretsManager.Enums.AccessPolicies; | ||
using Bit.Core.SecretsManager.Models.Data.AccessPolicyUpdates; | ||
using Bit.Core.SecretsManager.Queries.Interfaces; | ||
using Bit.Core.SecretsManager.Repositories; | ||
using Microsoft.AspNetCore.Authorization; | ||
|
||
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies; | ||
|
||
public class ProjectServiceAccountsAccessPoliciesAuthorizationHandler : AuthorizationHandler< | ||
ProjectServiceAccountsAccessPoliciesOperationRequirement, | ||
ProjectServiceAccountsAccessPoliciesUpdates> | ||
{ | ||
private readonly IAccessClientQuery _accessClientQuery; | ||
private readonly ICurrentContext _currentContext; | ||
private readonly IProjectRepository _projectRepository; | ||
private readonly IServiceAccountRepository _serviceAccountRepository; | ||
|
||
public ProjectServiceAccountsAccessPoliciesAuthorizationHandler(ICurrentContext currentContext, | ||
IAccessClientQuery accessClientQuery, | ||
IProjectRepository projectRepository, | ||
IServiceAccountRepository serviceAccountRepository) | ||
{ | ||
_currentContext = currentContext; | ||
_accessClientQuery = accessClientQuery; | ||
_serviceAccountRepository = serviceAccountRepository; | ||
_projectRepository = projectRepository; | ||
} | ||
|
||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, | ||
ProjectServiceAccountsAccessPoliciesOperationRequirement requirement, | ||
ProjectServiceAccountsAccessPoliciesUpdates resource) | ||
{ | ||
if (!_currentContext.AccessSecretsManager(resource.OrganizationId)) | ||
{ | ||
return; | ||
} | ||
|
||
// Only users and admins should be able to manipulate access policies | ||
var (accessClient, userId) = | ||
await _accessClientQuery.GetAccessClientAsync(context.User, resource.OrganizationId); | ||
if (accessClient != AccessClientType.User && accessClient != AccessClientType.NoAccessCheck) | ||
{ | ||
return; | ||
} | ||
|
||
switch (requirement) | ||
{ | ||
case not null when requirement == ProjectServiceAccountsAccessPoliciesOperations.Updates: | ||
await CanUpdateAsync(context, requirement, resource, accessClient, | ||
userId); | ||
break; | ||
default: | ||
throw new ArgumentException("Unsupported operation requirement type provided.", | ||
nameof(requirement)); | ||
} | ||
} | ||
|
||
private async Task CanUpdateAsync(AuthorizationHandlerContext context, | ||
ProjectServiceAccountsAccessPoliciesOperationRequirement requirement, | ||
ProjectServiceAccountsAccessPoliciesUpdates resource, | ||
AccessClientType accessClient, Guid userId) | ||
{ | ||
var access = | ||
await _projectRepository.AccessToProjectAsync(resource.ProjectId, userId, | ||
accessClient); | ||
if (!access.Write) | ||
{ | ||
return; | ||
} | ||
|
||
var serviceAccountIds = resource.ServiceAccountAccessPolicyUpdates.Select(update => | ||
update.AccessPolicy.ServiceAccountId!.Value).ToList(); | ||
|
||
var inSameOrganization = | ||
await _serviceAccountRepository.ServiceAccountsAreInOrganizationAsync(serviceAccountIds, | ||
resource.OrganizationId); | ||
if (!inSameOrganization) | ||
{ | ||
return; | ||
} | ||
|
||
// Users can only create access policies for service accounts they have access to. | ||
// User can delete and update any service account access policy if they have write access to the project. | ||
var serviceAccountIdsToCheck = resource.ServiceAccountAccessPolicyUpdates | ||
.Where(update => update.Operation == AccessPolicyOperation.Create).Select(update => | ||
update.AccessPolicy.ServiceAccountId!.Value).ToList(); | ||
|
||
if (serviceAccountIdsToCheck.Count == 0) | ||
{ | ||
context.Succeed(requirement); | ||
return; | ||
} | ||
|
||
var serviceAccountsAccess = | ||
await _serviceAccountRepository.AccessToServiceAccountsAsync(serviceAccountIdsToCheck, userId, | ||
accessClient); | ||
if (serviceAccountsAccess.Count == serviceAccountIdsToCheck.Count && | ||
serviceAccountsAccess.All(a => a.Value.Write)) | ||
{ | ||
context.Succeed(requirement); | ||
} | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
...cretsManager/Commands/AccessPolicies/UpdateProjectServiceAccountsAccessPoliciesCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#nullable enable | ||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces; | ||
using Bit.Core.SecretsManager.Models.Data.AccessPolicyUpdates; | ||
using Bit.Core.SecretsManager.Repositories; | ||
|
||
namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies; | ||
|
||
public class UpdateProjectServiceAccountsAccessPoliciesCommand : IUpdateProjectServiceAccountsAccessPoliciesCommand | ||
{ | ||
private readonly IAccessPolicyRepository _accessPolicyRepository; | ||
|
||
public UpdateProjectServiceAccountsAccessPoliciesCommand(IAccessPolicyRepository accessPolicyRepository) | ||
{ | ||
_accessPolicyRepository = accessPolicyRepository; | ||
} | ||
|
||
public async Task UpdateAsync(ProjectServiceAccountsAccessPoliciesUpdates accessPoliciesUpdates) | ||
{ | ||
if (!accessPoliciesUpdates.ServiceAccountAccessPolicyUpdates.Any()) | ||
{ | ||
return; | ||
} | ||
|
||
await _accessPolicyRepository.UpdateProjectServiceAccountsAccessPoliciesAsync(accessPoliciesUpdates); | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
...SecretsManager/Queries/AccessPolicies/ProjectServiceAccountsAccessPoliciesUpdatesQuery.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#nullable enable | ||
using Bit.Core.SecretsManager.Enums.AccessPolicies; | ||
using Bit.Core.SecretsManager.Models.Data; | ||
using Bit.Core.SecretsManager.Models.Data.AccessPolicyUpdates; | ||
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces; | ||
using Bit.Core.SecretsManager.Repositories; | ||
|
||
namespace Bit.Commercial.Core.SecretsManager.Queries.AccessPolicies; | ||
|
||
public class ProjectServiceAccountsAccessPoliciesUpdatesQuery : IProjectServiceAccountsAccessPoliciesUpdatesQuery | ||
{ | ||
private readonly IAccessPolicyRepository _accessPolicyRepository; | ||
|
||
public ProjectServiceAccountsAccessPoliciesUpdatesQuery(IAccessPolicyRepository accessPolicyRepository) | ||
{ | ||
_accessPolicyRepository = accessPolicyRepository; | ||
} | ||
|
||
public async Task<ProjectServiceAccountsAccessPoliciesUpdates> GetAsync( | ||
ProjectServiceAccountsAccessPolicies projectServiceAccountsAccessPolicies) | ||
{ | ||
var currentPolicies = | ||
await _accessPolicyRepository.GetProjectServiceAccountsAccessPoliciesAsync( | ||
projectServiceAccountsAccessPolicies.ProjectId); | ||
|
||
if (currentPolicies == null) | ||
{ | ||
return new ProjectServiceAccountsAccessPoliciesUpdates | ||
{ | ||
ProjectId = projectServiceAccountsAccessPolicies.ProjectId, | ||
OrganizationId = projectServiceAccountsAccessPolicies.OrganizationId, | ||
ServiceAccountAccessPolicyUpdates = | ||
projectServiceAccountsAccessPolicies.ServiceAccountAccessPolicies.Select(p => | ||
new ServiceAccountProjectAccessPolicyUpdate | ||
{ | ||
Operation = AccessPolicyOperation.Create, | ||
AccessPolicy = p | ||
}) | ||
}; | ||
} | ||
|
||
return currentPolicies.GetPolicyUpdates(projectServiceAccountsAccessPolicies); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.