mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 16:42:50 -05:00
[SM-707] Refactor authorization for Access Policy Commands (#2905)
* Extract authorization from access policy commands * Use auto mapper to ignore unwanted properties ---------
This commit is contained in:
@ -0,0 +1,266 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
|
||||
public class AccessPolicyAuthorizationHandler : AuthorizationHandler<AccessPolicyOperationRequirement, BaseAccessPolicy>
|
||||
{
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IAccessClientQuery _accessClientQuery;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||
|
||||
public AccessPolicyAuthorizationHandler(ICurrentContext currentContext,
|
||||
IAccessClientQuery accessClientQuery,
|
||||
IGroupRepository groupRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProjectRepository projectRepository,
|
||||
IServiceAccountRepository serviceAccountRepository)
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_accessClientQuery = accessClientQuery;
|
||||
_groupRepository = groupRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_projectRepository = projectRepository;
|
||||
_serviceAccountRepository = serviceAccountRepository;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement,
|
||||
BaseAccessPolicy resource)
|
||||
{
|
||||
switch (requirement)
|
||||
{
|
||||
case not null when requirement == AccessPolicyOperations.Create:
|
||||
await CanCreateAccessPolicyAsync(context, requirement, resource);
|
||||
break;
|
||||
case not null when requirement == AccessPolicyOperations.Update:
|
||||
await CanUpdateAccessPolicyAsync(context, requirement, resource);
|
||||
break;
|
||||
case not null when requirement == AccessPolicyOperations.Delete:
|
||||
await CanDeleteAccessPolicyAsync(context, requirement, resource);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported operation requirement type provided.",
|
||||
nameof(requirement));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanCreateAccessPolicyAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, BaseAccessPolicy resource)
|
||||
{
|
||||
switch (resource)
|
||||
{
|
||||
case UserProjectAccessPolicy ap:
|
||||
await CanCreateAsync(context, requirement, ap);
|
||||
break;
|
||||
case GroupProjectAccessPolicy ap:
|
||||
await CanCreateAsync(context, requirement, ap);
|
||||
break;
|
||||
case ServiceAccountProjectAccessPolicy ap:
|
||||
await CanCreateAsync(context, requirement, ap);
|
||||
break;
|
||||
case UserServiceAccountAccessPolicy ap:
|
||||
await CanCreateAsync(context, requirement, ap);
|
||||
break;
|
||||
case GroupServiceAccountAccessPolicy ap:
|
||||
await CanCreateAsync(context, requirement, ap);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported access policy type provided.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanUpdateAccessPolicyAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, BaseAccessPolicy resource)
|
||||
{
|
||||
var access = await GetAccessPolicyAccessAsync(context, resource);
|
||||
|
||||
if (access.Write)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanDeleteAccessPolicyAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, BaseAccessPolicy resource)
|
||||
{
|
||||
var access = await GetAccessPolicyAccessAsync(context, resource);
|
||||
|
||||
if (access.Write)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanCreateAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, UserProjectAccessPolicy resource)
|
||||
{
|
||||
var user = await _organizationUserRepository.GetByIdAsync(resource.OrganizationUserId!.Value);
|
||||
if (user.OrganizationId != resource.GrantedProject?.OrganizationId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var access = await GetAccessAsync(context, resource.GrantedProject!.OrganizationId, resource.GrantedProjectId);
|
||||
|
||||
if (access.Write)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanCreateAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, GroupProjectAccessPolicy resource)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(resource.GroupId!.Value);
|
||||
if (group.OrganizationId != resource.GrantedProject?.OrganizationId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var access = await GetAccessAsync(context, resource.GrantedProject!.OrganizationId, resource.GrantedProjectId);
|
||||
|
||||
if (access.Write)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanCreateAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, ServiceAccountProjectAccessPolicy resource)
|
||||
{
|
||||
var projectOrganizationId = resource.GrantedProject?.OrganizationId;
|
||||
var serviceAccountOrgId = resource.ServiceAccount?.OrganizationId;
|
||||
|
||||
if (projectOrganizationId == null)
|
||||
{
|
||||
var project = await _projectRepository.GetByIdAsync(resource.GrantedProjectId!.Value);
|
||||
projectOrganizationId = project?.OrganizationId;
|
||||
}
|
||||
|
||||
if (serviceAccountOrgId == null)
|
||||
{
|
||||
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(resource.ServiceAccountId!.Value);
|
||||
serviceAccountOrgId = serviceAccount?.OrganizationId;
|
||||
}
|
||||
|
||||
if (!serviceAccountOrgId.HasValue || !projectOrganizationId.HasValue ||
|
||||
serviceAccountOrgId != projectOrganizationId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var access = await GetAccessAsync(context, projectOrganizationId.Value, resource.GrantedProjectId,
|
||||
resource.ServiceAccountId);
|
||||
|
||||
if (access.Write)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanCreateAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, UserServiceAccountAccessPolicy resource)
|
||||
{
|
||||
var user = await _organizationUserRepository.GetByIdAsync(resource.OrganizationUserId!.Value);
|
||||
if (user.OrganizationId != resource.GrantedServiceAccount!.OrganizationId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var access = await GetAccessAsync(context, resource.GrantedServiceAccount!.OrganizationId,
|
||||
serviceAccountIdToCheck: resource.GrantedServiceAccountId);
|
||||
|
||||
if (access.Write)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanCreateAsync(AuthorizationHandlerContext context,
|
||||
AccessPolicyOperationRequirement requirement, GroupServiceAccountAccessPolicy resource)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(resource.GroupId!.Value);
|
||||
if (group.OrganizationId != resource.GrantedServiceAccount!.OrganizationId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var access = await GetAccessAsync(context, resource.GrantedServiceAccount!.OrganizationId,
|
||||
serviceAccountIdToCheck: resource.GrantedServiceAccountId);
|
||||
|
||||
if (access.Write)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<(bool Read, bool Write)> GetAccessPolicyAccessAsync(AuthorizationHandlerContext context,
|
||||
BaseAccessPolicy resource) =>
|
||||
resource switch
|
||||
{
|
||||
UserProjectAccessPolicy ap => await GetAccessAsync(context, ap.GrantedProject!.OrganizationId,
|
||||
ap.GrantedProjectId),
|
||||
GroupProjectAccessPolicy ap => await GetAccessAsync(context, ap.GrantedProject!.OrganizationId,
|
||||
ap.GrantedProjectId),
|
||||
ServiceAccountProjectAccessPolicy ap => await GetAccessAsync(context, ap.GrantedProject!.OrganizationId,
|
||||
ap.GrantedProjectId),
|
||||
UserServiceAccountAccessPolicy ap => await GetAccessAsync(context, ap.GrantedServiceAccount!.OrganizationId,
|
||||
serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||
GroupServiceAccountAccessPolicy ap => await GetAccessAsync(context,
|
||||
ap.GrantedServiceAccount!.OrganizationId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||
_ => throw new ArgumentException("Unsupported access policy type provided."),
|
||||
};
|
||||
|
||||
private async Task<(bool Read, bool Write)> GetAccessAsync(AuthorizationHandlerContext context,
|
||||
Guid organizationId, Guid? projectIdToCheck = null,
|
||||
Guid? serviceAccountIdToCheck = null)
|
||||
{
|
||||
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||
{
|
||||
return (false, false);
|
||||
}
|
||||
|
||||
var (accessClient, userId) = await _accessClientQuery.GetAccessClientAsync(context.User, organizationId);
|
||||
|
||||
// Only users and admins should be able to manipulate access policies
|
||||
if (accessClient != AccessClientType.User && accessClient != AccessClientType.NoAccessCheck)
|
||||
{
|
||||
return (false, false);
|
||||
}
|
||||
|
||||
if (projectIdToCheck.HasValue && serviceAccountIdToCheck.HasValue)
|
||||
{
|
||||
var projectAccess =
|
||||
await _projectRepository.AccessToProjectAsync(projectIdToCheck.Value, userId, accessClient);
|
||||
var serviceAccountAccess =
|
||||
await _serviceAccountRepository.AccessToServiceAccountAsync(serviceAccountIdToCheck.Value, userId,
|
||||
accessClient);
|
||||
return (
|
||||
projectAccess.Read && serviceAccountAccess.Read,
|
||||
projectAccess.Write && serviceAccountAccess.Write);
|
||||
}
|
||||
|
||||
if (projectIdToCheck.HasValue)
|
||||
{
|
||||
return await _projectRepository.AccessToProjectAsync(projectIdToCheck.Value, userId, accessClient);
|
||||
}
|
||||
|
||||
if (serviceAccountIdToCheck.HasValue)
|
||||
{
|
||||
return await _serviceAccountRepository.AccessToServiceAccountAsync(serviceAccountIdToCheck.Value, userId,
|
||||
accessClient);
|
||||
}
|
||||
|
||||
throw new ArgumentException("No ID to check provided.");
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
@ -9,92 +8,18 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||
public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
|
||||
{
|
||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||
|
||||
public CreateAccessPoliciesCommand(
|
||||
IAccessPolicyRepository accessPolicyRepository,
|
||||
IProjectRepository projectRepository,
|
||||
IServiceAccountRepository serviceAccountRepository)
|
||||
public CreateAccessPoliciesCommand(IAccessPolicyRepository accessPolicyRepository)
|
||||
{
|
||||
_accessPolicyRepository = accessPolicyRepository;
|
||||
_projectRepository = projectRepository;
|
||||
_serviceAccountRepository = serviceAccountRepository;
|
||||
}
|
||||
|
||||
private static IEnumerable<Guid?> GetDistinctGrantedProjectIds(List<BaseAccessPolicy> accessPolicies)
|
||||
public async Task<IEnumerable<BaseAccessPolicy>> CreateManyAsync(List<BaseAccessPolicy> accessPolicies)
|
||||
{
|
||||
var userGrantedIds = accessPolicies.OfType<UserProjectAccessPolicy>().Select(ap => ap.GrantedProjectId);
|
||||
var groupGrantedIds = accessPolicies.OfType<GroupProjectAccessPolicy>().Select(ap => ap.GrantedProjectId);
|
||||
var saGrantedIds = accessPolicies.OfType<ServiceAccountProjectAccessPolicy>().Select(ap => ap.GrantedProjectId);
|
||||
return userGrantedIds.Concat(groupGrantedIds).Concat(saGrantedIds).Distinct();
|
||||
}
|
||||
|
||||
private static IEnumerable<Guid?> GetDistinctGrantedServiceAccountIds(List<BaseAccessPolicy> accessPolicies)
|
||||
{
|
||||
var userGrantedIds = accessPolicies.OfType<UserServiceAccountAccessPolicy>().Select(ap => ap.GrantedServiceAccountId);
|
||||
var groupGrantedIds = accessPolicies.OfType<GroupServiceAccountAccessPolicy>()
|
||||
.Select(ap => ap.GrantedServiceAccountId);
|
||||
return userGrantedIds.Concat(groupGrantedIds).Distinct();
|
||||
}
|
||||
|
||||
private static void CheckForDistinctAccessPolicies(IReadOnlyCollection<BaseAccessPolicy> accessPolicies)
|
||||
{
|
||||
var distinctAccessPolicies = accessPolicies.DistinctBy(baseAccessPolicy =>
|
||||
{
|
||||
return baseAccessPolicy switch
|
||||
{
|
||||
UserProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId, ap.GrantedProjectId),
|
||||
GroupProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedProjectId),
|
||||
ServiceAccountProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.ServiceAccountId,
|
||||
ap.GrantedProjectId),
|
||||
UserServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId,
|
||||
ap.GrantedServiceAccountId),
|
||||
GroupServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedServiceAccountId),
|
||||
_ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy)),
|
||||
};
|
||||
}).ToList();
|
||||
|
||||
if (accessPolicies.Count != distinctAccessPolicies.Count)
|
||||
{
|
||||
throw new BadRequestException("Resources must be unique");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<BaseAccessPolicy>> CreateManyAsync(List<BaseAccessPolicy> accessPolicies, Guid userId, AccessClientType accessType)
|
||||
{
|
||||
CheckForDistinctAccessPolicies(accessPolicies);
|
||||
await CheckAccessPoliciesDoNotExistAsync(accessPolicies);
|
||||
await CheckCanCreateAsync(accessPolicies, userId, accessType);
|
||||
return await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
||||
}
|
||||
|
||||
private async Task CheckCanCreateAsync(List<BaseAccessPolicy> accessPolicies, Guid userId, AccessClientType accessType)
|
||||
{
|
||||
var projectIds = GetDistinctGrantedProjectIds(accessPolicies).ToList();
|
||||
var serviceAccountIds = GetDistinctGrantedServiceAccountIds(accessPolicies).ToList();
|
||||
|
||||
if (projectIds.Any())
|
||||
{
|
||||
foreach (var projectId in projectIds)
|
||||
{
|
||||
await CheckPermissionAsync(accessType, userId, projectId);
|
||||
}
|
||||
}
|
||||
if (serviceAccountIds.Any())
|
||||
{
|
||||
foreach (var serviceAccountId in serviceAccountIds)
|
||||
{
|
||||
await CheckPermissionAsync(accessType, userId, serviceAccountIdToCheck: serviceAccountId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!projectIds.Any() && !serviceAccountIds.Any())
|
||||
{
|
||||
throw new BadRequestException("No granted IDs specified");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckAccessPoliciesDoNotExistAsync(List<BaseAccessPolicy> accessPolicies)
|
||||
{
|
||||
foreach (var accessPolicy in accessPolicies)
|
||||
@ -105,41 +30,4 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckPermissionAsync(AccessClientType accessClient, Guid userId, Guid? projectIdToCheck = null,
|
||||
Guid? serviceAccountIdToCheck = null)
|
||||
{
|
||||
bool hasAccess;
|
||||
switch (accessClient)
|
||||
{
|
||||
case AccessClientType.NoAccessCheck:
|
||||
hasAccess = true;
|
||||
break;
|
||||
case AccessClientType.User:
|
||||
if (projectIdToCheck.HasValue)
|
||||
{
|
||||
hasAccess = (await _projectRepository.AccessToProjectAsync(projectIdToCheck.Value, userId, accessClient)).Write;
|
||||
}
|
||||
else if (serviceAccountIdToCheck.HasValue)
|
||||
{
|
||||
hasAccess =
|
||||
await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
|
||||
serviceAccountIdToCheck.Value, userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("No ID to check provided.");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
hasAccess = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasAccess)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
|
||||
namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||
@ -10,98 +6,14 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||
public class DeleteAccessPolicyCommand : IDeleteAccessPolicyCommand
|
||||
{
|
||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||
|
||||
public DeleteAccessPolicyCommand(
|
||||
IAccessPolicyRepository accessPolicyRepository,
|
||||
ICurrentContext currentContext,
|
||||
IProjectRepository projectRepository,
|
||||
IServiceAccountRepository serviceAccountRepository)
|
||||
public DeleteAccessPolicyCommand(IAccessPolicyRepository accessPolicyRepository)
|
||||
{
|
||||
_projectRepository = projectRepository;
|
||||
_serviceAccountRepository = serviceAccountRepository;
|
||||
_accessPolicyRepository = accessPolicyRepository;
|
||||
_currentContext = currentContext;
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(Guid id, Guid userId)
|
||||
public async Task DeleteAsync(Guid id)
|
||||
{
|
||||
var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
|
||||
if (accessPolicy == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!await IsAllowedToDeleteAsync(accessPolicy, userId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _accessPolicyRepository.DeleteAsync(id);
|
||||
}
|
||||
|
||||
private async Task<bool> IsAllowedToDeleteAsync(BaseAccessPolicy baseAccessPolicy, Guid userId) =>
|
||||
baseAccessPolicy switch
|
||||
{
|
||||
UserProjectAccessPolicy ap => await HasPermissionAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||
ap.GrantedProjectId),
|
||||
GroupProjectAccessPolicy ap => await HasPermissionAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||
ap.GrantedProjectId),
|
||||
ServiceAccountProjectAccessPolicy ap => await HasPermissionAsync(ap.GrantedProject!.OrganizationId,
|
||||
userId, ap.GrantedProjectId),
|
||||
UserServiceAccountAccessPolicy ap => await HasPermissionAsync(
|
||||
ap.GrantedServiceAccount!.OrganizationId,
|
||||
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||
GroupServiceAccountAccessPolicy ap => await HasPermissionAsync(
|
||||
ap.GrantedServiceAccount!.OrganizationId,
|
||||
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||
_ => throw new ArgumentException("Unsupported access policy type provided."),
|
||||
};
|
||||
|
||||
private async Task<bool> HasPermissionAsync(
|
||||
Guid organizationId,
|
||||
Guid userId,
|
||||
Guid? projectIdToCheck = null,
|
||||
Guid? serviceAccountIdToCheck = null)
|
||||
{
|
||||
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||
|
||||
bool hasAccess;
|
||||
switch (accessClient)
|
||||
{
|
||||
case AccessClientType.NoAccessCheck:
|
||||
hasAccess = true;
|
||||
break;
|
||||
case AccessClientType.User:
|
||||
if (projectIdToCheck.HasValue)
|
||||
{
|
||||
hasAccess = (await _projectRepository.AccessToProjectAsync(projectIdToCheck.Value, userId, accessClient)).Write;
|
||||
}
|
||||
else if (serviceAccountIdToCheck.HasValue)
|
||||
{
|
||||
hasAccess =
|
||||
await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
|
||||
serviceAccountIdToCheck.Value, userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("No ID to check provided.");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
hasAccess = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return hasAccess;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
@ -10,23 +8,13 @@ namespace Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||
public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand
|
||||
{
|
||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||
|
||||
public UpdateAccessPolicyCommand(
|
||||
IAccessPolicyRepository accessPolicyRepository,
|
||||
ICurrentContext currentContext,
|
||||
IProjectRepository projectRepository,
|
||||
IServiceAccountRepository serviceAccountRepository)
|
||||
public UpdateAccessPolicyCommand(IAccessPolicyRepository accessPolicyRepository)
|
||||
{
|
||||
_accessPolicyRepository = accessPolicyRepository;
|
||||
_currentContext = currentContext;
|
||||
_projectRepository = projectRepository;
|
||||
_serviceAccountRepository = serviceAccountRepository;
|
||||
}
|
||||
|
||||
public async Task<BaseAccessPolicy> UpdateAsync(Guid id, bool read, bool write, Guid userId)
|
||||
public async Task<BaseAccessPolicy> UpdateAsync(Guid id, bool read, bool write)
|
||||
{
|
||||
var accessPolicy = await _accessPolicyRepository.GetByIdAsync(id);
|
||||
if (accessPolicy == null)
|
||||
@ -34,78 +22,10 @@ public class UpdateAccessPolicyCommand : IUpdateAccessPolicyCommand
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (!await IsAllowedToUpdateAsync(accessPolicy, userId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
accessPolicy.Read = read;
|
||||
accessPolicy.Write = write;
|
||||
accessPolicy.RevisionDate = DateTime.UtcNow;
|
||||
await _accessPolicyRepository.ReplaceAsync(accessPolicy);
|
||||
return accessPolicy;
|
||||
}
|
||||
|
||||
private async Task<bool> IsAllowedToUpdateAsync(BaseAccessPolicy baseAccessPolicy, Guid userId) =>
|
||||
baseAccessPolicy switch
|
||||
{
|
||||
UserProjectAccessPolicy ap => await HasPermissionsAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||
ap.GrantedProjectId),
|
||||
GroupProjectAccessPolicy ap => await HasPermissionsAsync(ap.GrantedProject!.OrganizationId, userId,
|
||||
ap.GrantedProjectId),
|
||||
ServiceAccountProjectAccessPolicy ap => await HasPermissionsAsync(ap.GrantedProject!.OrganizationId,
|
||||
userId, ap.GrantedProjectId),
|
||||
UserServiceAccountAccessPolicy ap => await HasPermissionsAsync(
|
||||
ap.GrantedServiceAccount!.OrganizationId,
|
||||
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||
GroupServiceAccountAccessPolicy ap => await HasPermissionsAsync(
|
||||
ap.GrantedServiceAccount!.OrganizationId,
|
||||
userId, serviceAccountIdToCheck: ap.GrantedServiceAccountId),
|
||||
_ => throw new ArgumentException("Unsupported access policy type provided."),
|
||||
};
|
||||
|
||||
private async Task<bool> HasPermissionsAsync(
|
||||
Guid organizationId,
|
||||
Guid userId,
|
||||
Guid? projectIdToCheck = null,
|
||||
Guid? serviceAccountIdToCheck = null)
|
||||
{
|
||||
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||
|
||||
bool hasAccess;
|
||||
switch (accessClient)
|
||||
{
|
||||
case AccessClientType.NoAccessCheck:
|
||||
hasAccess = true;
|
||||
break;
|
||||
case AccessClientType.User:
|
||||
if (projectIdToCheck.HasValue)
|
||||
{
|
||||
hasAccess = (await _projectRepository.AccessToProjectAsync(projectIdToCheck.Value, userId, accessClient)).Write;
|
||||
}
|
||||
else if (serviceAccountIdToCheck.HasValue)
|
||||
{
|
||||
hasAccess =
|
||||
await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
|
||||
serviceAccountIdToCheck.Value, userId);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("No ID to check provided.");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
hasAccess = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return hasAccess;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.Projects;
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.Projects;
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.Secrets;
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.ServiceAccounts;
|
||||
using Bit.Commercial.Core.SecretsManager.Commands.AccessPolicies;
|
||||
@ -29,6 +30,7 @@ public static class SecretsManagerCollectionExtensions
|
||||
services.AddScoped<IAuthorizationHandler, ProjectAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, SecretAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, ServiceAccountAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, AccessPolicyAuthorizationHandler>();
|
||||
services.AddScoped<IAccessClientQuery, AccessClientQuery>();
|
||||
services.AddScoped<ICreateSecretCommand, CreateSecretCommand>();
|
||||
services.AddScoped<IUpdateSecretCommand, UpdateSecretCommand>();
|
||||
|
Reference in New Issue
Block a user