1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 16:42:50 -05:00

[SM-473] Access Policies - Service Accounts (#2658)

* Add service account access policy endpoints

* Add unit & integration tests for new endpoints

* Fix formatting on response models

* Cleanup unit tests
This commit is contained in:
Thomas Avery
2023-02-07 14:30:22 -06:00
committed by GitHub
parent cf669286ed
commit 1ee14d93e6
11 changed files with 1030 additions and 305 deletions

View File

@ -12,15 +12,18 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
private readonly IAccessPolicyRepository _accessPolicyRepository;
private readonly ICurrentContext _currentContext;
private readonly IProjectRepository _projectRepository;
private readonly IServiceAccountRepository _serviceAccountRepository;
public CreateAccessPoliciesCommand(
IAccessPolicyRepository accessPolicyRepository,
ICurrentContext currentContext,
IProjectRepository projectRepository)
IProjectRepository projectRepository,
IServiceAccountRepository serviceAccountRepository)
{
_projectRepository = projectRepository;
_accessPolicyRepository = accessPolicyRepository;
_currentContext = currentContext;
_projectRepository = projectRepository;
_serviceAccountRepository = serviceAccountRepository;
}
public async Task<IEnumerable<BaseAccessPolicy>> CreateForProjectAsync(Guid projectId,
@ -32,21 +35,33 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
throw new NotFoundException();
}
var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
await CheckPermissionAsync(project.OrganizationId, userId, projectId);
CheckForDistinctAccessPolicies(accessPolicies);
await CheckAccessPoliciesDoNotExistAsync(accessPolicies);
var hasAccess = accessClient switch
{
AccessClientType.NoAccessCheck => true,
AccessClientType.User => await _projectRepository.UserHasWriteAccessToProject(project.Id, userId),
_ => false,
};
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
return await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(projectId);
}
if (!hasAccess)
public async Task<IEnumerable<BaseAccessPolicy>> CreateForServiceAccountAsync(Guid serviceAccountId,
List<BaseAccessPolicy> accessPolicies, Guid userId)
{
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(serviceAccountId);
if (serviceAccount == null || !_currentContext.AccessSecretsManager(serviceAccount.OrganizationId))
{
throw new NotFoundException();
}
await CheckPermissionAsync(serviceAccount.OrganizationId, userId, serviceAccountIdToCheck: serviceAccountId);
CheckForDistinctAccessPolicies(accessPolicies);
await CheckAccessPoliciesDoNotExistAsync(accessPolicies);
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
return await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(serviceAccountId);
}
private static void CheckForDistinctAccessPolicies(IReadOnlyCollection<BaseAccessPolicy> accessPolicies)
{
var distinctAccessPolicies = accessPolicies.DistinctBy(baseAccessPolicy =>
{
return baseAccessPolicy switch
@ -55,6 +70,9 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
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();
@ -63,7 +81,10 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
{
throw new BadRequestException("Resources must be unique");
}
}
private async Task CheckAccessPoliciesDoNotExistAsync(List<BaseAccessPolicy> accessPolicies)
{
foreach (var accessPolicy in accessPolicies)
{
if (await _accessPolicyRepository.AccessPolicyExists(accessPolicy))
@ -71,7 +92,46 @@ public class CreateAccessPoliciesCommand : ICreateAccessPoliciesCommand
throw new BadRequestException("Resource already exists");
}
}
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
return await _accessPolicyRepository.GetManyByGrantedProjectIdAsync(projectId);
}
private async Task CheckPermissionAsync(Guid organizationId,
Guid userId,
Guid? projectIdToCheck = null,
Guid? serviceAccountIdToCheck = null)
{
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 != null)
{
hasAccess = await _projectRepository.UserHasWriteAccessToProject(projectIdToCheck.Value, userId);
}
else if (serviceAccountIdToCheck != null)
{
hasAccess = await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(
serviceAccountIdToCheck.Value,
userId);
}
else
{
hasAccess = false;
}
break;
default:
hasAccess = false;
break;
}
if (!hasAccess)
{
throw new NotFoundException();
}
}
}