1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 08:32: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();
}
}
}

View File

@ -16,152 +16,152 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
public async Task<List<Core.SecretsManager.Entities.BaseAccessPolicy>> CreateManyAsync(List<Core.SecretsManager.Entities.BaseAccessPolicy> baseAccessPolicies)
{
using (var scope = ServiceScopeFactory.CreateScope())
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
foreach (var baseAccessPolicy in baseAccessPolicies)
{
var dbContext = GetDatabaseContext(scope);
foreach (var baseAccessPolicy in baseAccessPolicies)
{
baseAccessPolicy.SetNewId();
switch (baseAccessPolicy)
{
case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
{
var entity =
Mapper.Map<UserProjectAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy:
{
var entity =
Mapper.Map<UserServiceAccountAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
{
var entity = Mapper.Map<GroupProjectAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy:
{
var entity = Mapper.Map<GroupServiceAccountAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
{
var entity = Mapper.Map<ServiceAccountProjectAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
}
}
await dbContext.SaveChangesAsync();
return baseAccessPolicies;
}
}
public async Task<bool> AccessPolicyExists(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
baseAccessPolicy.SetNewId();
switch (baseAccessPolicy)
{
case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
{
var policy = await dbContext.UserProjectAccessPolicy
.Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId &&
c.GrantedProjectId == accessPolicy.GrantedProjectId)
.FirstOrDefaultAsync();
return policy != null;
var entity =
Mapper.Map<UserProjectAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy:
{
var entity =
Mapper.Map<UserServiceAccountAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
{
var policy = await dbContext.GroupProjectAccessPolicy
.Where(c => c.GroupId == accessPolicy.GroupId &&
c.GrantedProjectId == accessPolicy.GrantedProjectId)
.FirstOrDefaultAsync();
return policy != null;
var entity = Mapper.Map<GroupProjectAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy:
{
var entity = Mapper.Map<GroupServiceAccountAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
{
var policy = await dbContext.ServiceAccountProjectAccessPolicy
.Where(c => c.ServiceAccountId == accessPolicy.ServiceAccountId &&
c.GrantedProjectId == accessPolicy.GrantedProjectId)
.FirstOrDefaultAsync();
return policy != null;
var entity = Mapper.Map<ServiceAccountProjectAccessPolicy>(accessPolicy);
await dbContext.AddAsync(entity);
break;
}
default:
throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy));
}
}
await dbContext.SaveChangesAsync();
return baseAccessPolicies;
}
public async Task<bool> AccessPolicyExists(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
{
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
switch (baseAccessPolicy)
{
case Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy:
{
var policy = await dbContext.UserProjectAccessPolicy
.Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId &&
c.GrantedProjectId == accessPolicy.GrantedProjectId)
.FirstOrDefaultAsync();
return policy != null;
}
case Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy:
{
var policy = await dbContext.GroupProjectAccessPolicy
.Where(c => c.GroupId == accessPolicy.GroupId &&
c.GrantedProjectId == accessPolicy.GrantedProjectId)
.FirstOrDefaultAsync();
return policy != null;
}
case Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy:
{
var policy = await dbContext.ServiceAccountProjectAccessPolicy
.Where(c => c.ServiceAccountId == accessPolicy.ServiceAccountId &&
c.GrantedProjectId == accessPolicy.GrantedProjectId)
.FirstOrDefaultAsync();
return policy != null;
}
case Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy:
{
var policy = await dbContext.UserServiceAccountAccessPolicy
.Where(c => c.OrganizationUserId == accessPolicy.OrganizationUserId &&
c.GrantedServiceAccountId == accessPolicy.GrantedServiceAccountId)
.FirstOrDefaultAsync();
return policy != null;
}
case Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy:
{
var policy = await dbContext.GroupServiceAccountAccessPolicy
.Where(c => c.GroupId == accessPolicy.GroupId &&
c.GrantedServiceAccountId == accessPolicy.GrantedServiceAccountId)
.FirstOrDefaultAsync();
return policy != null;
}
default:
throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy));
}
}
public async Task<Core.SecretsManager.Entities.BaseAccessPolicy?> GetByIdAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.AccessPolicies.Where(ap => ap.Id == id)
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
.Include(ap => ((UserProjectAccessPolicy)ap).GrantedProject)
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
.Include(ap => ((GroupProjectAccessPolicy)ap).GrantedProject)
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).GrantedProject)
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
.Include(ap => ((UserServiceAccountAccessPolicy)ap).GrantedServiceAccount)
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccount)
.FirstOrDefaultAsync();
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.AccessPolicies.Where(ap => ap.Id == id)
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
.Include(ap => ((UserProjectAccessPolicy)ap).GrantedProject)
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
.Include(ap => ((GroupProjectAccessPolicy)ap).GrantedProject)
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).GrantedProject)
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
.Include(ap => ((UserServiceAccountAccessPolicy)ap).GrantedServiceAccount)
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccount)
.FirstOrDefaultAsync();
if (entity == null)
{
return null;
}
return MapToCore(entity);
}
return entity == null ? null : MapToCore(entity);
}
public async Task ReplaceAsync(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
{
using (var scope = ServiceScopeFactory.CreateScope())
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.AccessPolicies.FindAsync(baseAccessPolicy.Id);
if (entity != null)
{
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.AccessPolicies.FindAsync(baseAccessPolicy.Id);
if (entity != null)
{
dbContext.AccessPolicies.Attach(entity);
entity.Write = baseAccessPolicy.Write;
entity.Read = baseAccessPolicy.Read;
entity.RevisionDate = baseAccessPolicy.RevisionDate;
await dbContext.SaveChangesAsync();
}
dbContext.AccessPolicies.Attach(entity);
entity.Write = baseAccessPolicy.Write;
entity.Read = baseAccessPolicy.Read;
entity.RevisionDate = baseAccessPolicy.RevisionDate;
await dbContext.SaveChangesAsync();
}
}
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
var entities = await dbContext.AccessPolicies.Where(ap =>
((UserProjectAccessPolicy)ap).GrantedProjectId == id ||
((GroupProjectAccessPolicy)ap).GrantedProjectId == id ||
((ServiceAccountProjectAccessPolicy)ap).GrantedProjectId == id)
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
.ToListAsync();
return entities.Select(MapToCore);
}
var entities = await dbContext.AccessPolicies.Where(ap =>
((UserProjectAccessPolicy)ap).GrantedProjectId == id ||
((GroupProjectAccessPolicy)ap).GrantedProjectId == id ||
((ServiceAccountProjectAccessPolicy)ap).GrantedProjectId == id)
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
.Include(ap => ((ServiceAccountProjectAccessPolicy)ap).ServiceAccount)
.ToListAsync();
return entities.Select(MapToCore);
}
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id)
@ -181,15 +181,13 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
public async Task DeleteAsync(Guid id)
{
using (var scope = ServiceScopeFactory.CreateScope())
using var scope = ServiceScopeFactory.CreateScope();
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.AccessPolicies.FindAsync(id);
if (entity != null)
{
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.AccessPolicies.FindAsync(id);
if (entity != null)
{
dbContext.Remove(entity);
await dbContext.SaveChangesAsync();
}
dbContext.Remove(entity);
await dbContext.SaveChangesAsync();
}
}

View File

@ -17,9 +17,127 @@ namespace Bit.Commercial.Core.Test.SecretsManager.AccessPolicies;
[ProjectCustomize]
public class CreateAccessPoliciesCommandTests
{
private static List<BaseAccessPolicy> MakeDuplicate(List<BaseAccessPolicy> data, AccessPolicyType accessPolicyType)
{
switch (accessPolicyType)
{
case AccessPolicyType.UserProjectAccessPolicy:
{
var mockAccessPolicy = new UserProjectAccessPolicy
{
OrganizationUserId = Guid.NewGuid(),
GrantedProjectId = Guid.NewGuid(),
};
data.Add(mockAccessPolicy);
// Add a duplicate policy
data.Add(mockAccessPolicy);
break;
}
case AccessPolicyType.GroupProjectAccessPolicy:
{
var mockAccessPolicy = new GroupProjectAccessPolicy
{
GroupId = Guid.NewGuid(),
GrantedProjectId = Guid.NewGuid(),
};
data.Add(mockAccessPolicy);
// Add a duplicate policy
data.Add(mockAccessPolicy);
break;
}
case AccessPolicyType.ServiceAccountProjectAccessPolicy:
{
var mockAccessPolicy = new ServiceAccountProjectAccessPolicy
{
ServiceAccountId = Guid.NewGuid(),
GrantedProjectId = Guid.NewGuid(),
};
data.Add(mockAccessPolicy);
// Add a duplicate policy
data.Add(mockAccessPolicy);
break;
}
case AccessPolicyType.UserServiceAccountAccessPolicy:
{
var mockAccessPolicy = new UserServiceAccountAccessPolicy
{
OrganizationUserId = Guid.NewGuid(),
GrantedServiceAccountId = Guid.NewGuid(),
};
data.Add(mockAccessPolicy);
// Add a duplicate policy
data.Add(mockAccessPolicy);
break;
}
case AccessPolicyType.GroupServiceAccountAccessPolicy:
{
var mockAccessPolicy = new GroupServiceAccountAccessPolicy
{
GroupId = Guid.NewGuid(),
GrantedServiceAccountId = Guid.NewGuid(),
};
data.Add(mockAccessPolicy);
// Add a duplicate policy
data.Add(mockAccessPolicy);
break;
}
}
return data;
}
private static void SetupAdmin(SutProvider<CreateAccessPoliciesCommand> sutProvider, Guid organizationId)
{
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(true);
}
private static void SetupUser(SutProvider<CreateAccessPoliciesCommand> sutProvider, Guid organizationId)
{
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(organizationId).Returns(true);
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(organizationId).Returns(false);
}
private static void SetupPermission(SutProvider<CreateAccessPoliciesCommand> sutProvider,
PermissionType permissionType, Project project, Guid userId)
{
switch (permissionType)
{
case PermissionType.RunAsAdmin:
SetupAdmin(sutProvider, project.OrganizationId);
break;
case PermissionType.RunAsUserWithPermission:
SetupUser(sutProvider, project.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId)
.Returns(true);
break;
}
}
private static void SetupPermission(SutProvider<CreateAccessPoliciesCommand> sutProvider,
PermissionType permissionType, ServiceAccount serviceAccount, Guid userId)
{
switch (permissionType)
{
case PermissionType.RunAsAdmin:
SetupAdmin(sutProvider, serviceAccount.OrganizationId);
break;
case PermissionType.RunAsUserWithPermission:
SetupUser(sutProvider, serviceAccount.OrganizationId);
sutProvider.GetDependency<IServiceAccountRepository>()
.UserHasWriteAccessToServiceAccount(serviceAccount.Id, userId).Returns(true);
break;
}
}
[Theory]
[BitAutoData]
public async Task CreateAsync_SmNotEnabled_Throws(
public async Task CreateForProject_SmNotEnabled_Throws(
Guid userId,
Project project,
List<UserProjectAccessPolicy> userProjectAccessPolicies,
@ -42,7 +160,7 @@ public class CreateAccessPoliciesCommandTests
[Theory]
[BitAutoData]
public async Task CreateAsync_AlreadyExists_Throws_BadRequestException(
public async Task CreateForProject_AlreadyExists_Throws_BadRequestException(
Guid userId,
Project project,
List<UserProjectAccessPolicy> userProjectAccessPolicies,
@ -55,10 +173,8 @@ public class CreateAccessPoliciesCommandTests
data.AddRange(groupProjectAccessPolicies);
data.AddRange(serviceAccountProjectAccessPolicies);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
SetupAdmin(sutProvider, project.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(project.OrganizationId).Returns(true);
sutProvider.GetDependency<IAccessPolicyRepository>().AccessPolicyExists(Arg.Any<BaseAccessPolicy>())
.Returns(true);
@ -69,17 +185,13 @@ public class CreateAccessPoliciesCommandTests
}
[Theory]
[BitAutoData(true, false, false)]
[BitAutoData(false, true, false)]
[BitAutoData(true, true, false)]
[BitAutoData(false, false, true)]
[BitAutoData(true, false, true)]
[BitAutoData(false, true, true)]
[BitAutoData(true, true, true)]
public async Task CreateAsync_NotUnique_ThrowsException(
bool testUserPolicies,
bool testGroupPolicies,
bool testServiceAccountPolicies,
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy)]
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy)]
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy)]
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy)]
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy)]
public async Task CreateForProjectAsync_NotUnique_ThrowsException(
AccessPolicyType accessPolicyType,
Guid userId,
Project project,
List<UserProjectAccessPolicy> userProjectAccessPolicies,
@ -92,64 +204,24 @@ public class CreateAccessPoliciesCommandTests
data.AddRange(userProjectAccessPolicies);
data.AddRange(groupProjectAccessPolicies);
data.AddRange(serviceAccountProjectAccessPolicies);
data = MakeDuplicate(data, accessPolicyType);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
SetupAdmin(sutProvider, project.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(project.OrganizationId).Returns(true);
if (testUserPolicies)
{
var mockUserPolicy = new UserProjectAccessPolicy
{
OrganizationUserId = Guid.NewGuid(),
GrantedProjectId = Guid.NewGuid(),
};
data.Add(mockUserPolicy);
// Add a duplicate policy
data.Add(mockUserPolicy);
}
if (testGroupPolicies)
{
var mockGroupPolicy = new GroupProjectAccessPolicy
{
GroupId = Guid.NewGuid(),
GrantedProjectId = Guid.NewGuid(),
};
data.Add(mockGroupPolicy);
// Add a duplicate policy
data.Add(mockGroupPolicy);
}
if (testServiceAccountPolicies)
{
var mockServiceAccountPolicy = new ServiceAccountProjectAccessPolicy
{
ServiceAccountId = Guid.NewGuid(),
GrantedProjectId = Guid.NewGuid(),
};
data.Add(mockServiceAccountPolicy);
// Add a duplicate policy
data.Add(mockServiceAccountPolicy);
}
sutProvider.GetDependency<IAccessPolicyRepository>().AccessPolicyExists(Arg.Any<BaseAccessPolicy>())
.Returns(true);
await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateForProjectAsync(project.Id, data, userId));
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().CreateManyAsync(default);
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
}
[Theory]
[BitAutoData(PermissionType.RunAsAdmin)]
[BitAutoData(PermissionType.RunAsUserWithPermission)]
public async Task CreateAsync_Success(
public async Task CreateForProject_Success(
PermissionType permissionType,
Guid userId,
Project project,
@ -163,18 +235,8 @@ public class CreateAccessPoliciesCommandTests
data.AddRange(groupProjectAccessPolicies);
data.AddRange(serviceAccountProjectAccessPolicies);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
switch (permissionType)
{
case PermissionType.RunAsAdmin:
sutProvider.GetDependency<ICurrentContext>().OrganizationAdmin(project.OrganizationId).Returns(true);
break;
case PermissionType.RunAsUserWithPermission:
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId).Returns(true);
break;
}
SetupPermission(sutProvider, permissionType, project, userId);
await sutProvider.Sut.CreateForProjectAsync(project.Id, data, userId);
@ -184,7 +246,7 @@ public class CreateAccessPoliciesCommandTests
[Theory]
[BitAutoData]
public async Task CreateAsync_User_NoPermission(
public async Task CreateForProject_UserNoPermission_ThrowsNotFound(
Guid userId,
Project project,
List<UserProjectAccessPolicy> userProjectAccessPolicies,
@ -197,13 +259,148 @@ public class CreateAccessPoliciesCommandTests
data.AddRange(groupProjectAccessPolicies);
data.AddRange(serviceAccountProjectAccessPolicies);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(true);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
SetupUser(sutProvider, project.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().UserHasWriteAccessToProject(project.Id, userId).Returns(false);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(project.Id).Returns(project);
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.CreateForProjectAsync(project.Id, data, userId));
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs().CreateManyAsync(default);
}
[Theory]
[BitAutoData]
public async Task CreateForServiceAccount_SmNotEnabled_Throws(
Guid userId,
ServiceAccount serviceAccount,
List<UserProjectAccessPolicy> userProjectAccessPolicies,
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
SutProvider<CreateAccessPoliciesCommand> sutProvider)
{
var data = new List<BaseAccessPolicy>();
data.AddRange(userProjectAccessPolicies);
data.AddRange(groupProjectAccessPolicies);
data.AddRange(serviceAccountProjectAccessPolicies);
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Any<Guid>()).Returns(false);
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.CreateForServiceAccountAsync(serviceAccount.Id, data, userId));
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
}
[Theory]
[BitAutoData]
public async Task CreateForServiceAccount_AlreadyExists_ThrowsBadRequestException(
Guid userId,
ServiceAccount serviceAccount,
List<UserProjectAccessPolicy> userProjectAccessPolicies,
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
SutProvider<CreateAccessPoliciesCommand> sutProvider)
{
var data = new List<BaseAccessPolicy>();
data.AddRange(userProjectAccessPolicies);
data.AddRange(groupProjectAccessPolicies);
data.AddRange(serviceAccountProjectAccessPolicies);
SetupAdmin(sutProvider, serviceAccount.OrganizationId);
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
sutProvider.GetDependency<IAccessPolicyRepository>().AccessPolicyExists(Arg.Any<BaseAccessPolicy>())
.Returns(true);
await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateForServiceAccountAsync(serviceAccount.Id, data, userId));
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
}
[Theory]
[BitAutoData(AccessPolicyType.UserProjectAccessPolicy)]
[BitAutoData(AccessPolicyType.GroupProjectAccessPolicy)]
[BitAutoData(AccessPolicyType.ServiceAccountProjectAccessPolicy)]
[BitAutoData(AccessPolicyType.UserServiceAccountAccessPolicy)]
[BitAutoData(AccessPolicyType.GroupServiceAccountAccessPolicy)]
public async Task CreateForServiceAccount_NotUnique_Throws(
AccessPolicyType accessPolicyType,
Guid userId,
ServiceAccount serviceAccount,
List<UserProjectAccessPolicy> userProjectAccessPolicies,
List<GroupProjectAccessPolicy> groupProjectAccessPolicies,
List<ServiceAccountProjectAccessPolicy> serviceAccountProjectAccessPolicies,
SutProvider<CreateAccessPoliciesCommand> sutProvider
)
{
var data = new List<BaseAccessPolicy>();
data.AddRange(userProjectAccessPolicies);
data.AddRange(groupProjectAccessPolicies);
data.AddRange(serviceAccountProjectAccessPolicies);
data = MakeDuplicate(data, accessPolicyType);
SetupAdmin(sutProvider, serviceAccount.OrganizationId);
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
sutProvider.GetDependency<IAccessPolicyRepository>().AccessPolicyExists(Arg.Any<BaseAccessPolicy>())
.Returns(true);
await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.CreateForServiceAccountAsync(serviceAccount.Id, data, userId));
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
}
[Theory]
[BitAutoData(PermissionType.RunAsAdmin)]
[BitAutoData(PermissionType.RunAsUserWithPermission)]
public async Task CreateForServiceAccount_Success(
PermissionType permissionType,
Guid userId,
ServiceAccount serviceAccount,
List<UserServiceAccountAccessPolicy> userServiceAccountAccessPolicies,
List<GroupServiceAccountAccessPolicy> groupServiceAccountAccessPolicies,
SutProvider<CreateAccessPoliciesCommand> sutProvider)
{
var data = new List<BaseAccessPolicy>();
data.AddRange(userServiceAccountAccessPolicies);
data.AddRange(groupServiceAccountAccessPolicies);
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
SetupPermission(sutProvider, permissionType, serviceAccount, userId);
await sutProvider.Sut.CreateForServiceAccountAsync(serviceAccount.Id, data, userId);
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
.CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
}
[Theory]
[BitAutoData]
public async Task CreateForServiceAccount_UserWithoutPermission_ThrowsNotFound(
Guid userId,
ServiceAccount serviceAccount,
List<UserServiceAccountAccessPolicy> userServiceAccountAccessPolicies,
List<GroupServiceAccountAccessPolicy> groupServiceAccountAccessPolicies,
SutProvider<CreateAccessPoliciesCommand> sutProvider)
{
var data = new List<BaseAccessPolicy>();
data.AddRange(userServiceAccountAccessPolicies);
data.AddRange(groupServiceAccountAccessPolicies);
SetupUser(sutProvider, serviceAccount.OrganizationId);
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(serviceAccount.Id).Returns(serviceAccount);
sutProvider.GetDependency<IServiceAccountRepository>()
.UserHasWriteAccessToServiceAccount(serviceAccount.Id, userId).Returns(false);
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.CreateForServiceAccountAsync(serviceAccount.Id, data, userId));
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
}
}