mirror of
https://github.com/bitwarden/server.git
synced 2024-11-24 12:35:25 +01:00
[SM-909] Add service-account people access policy management endpoints (#3324)
* refactoring replace logic * model for policies + authz handler + unit tests * update AP repository * add new endpoints to controller * update unit tests and integration tests --------- Co-authored-by: cd-bitwarden <106776772+cd-bitwarden@users.noreply.github.com>
This commit is contained in:
parent
a589af3588
commit
f9232bcbb0
@ -1,9 +1,8 @@
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@ -11,25 +10,23 @@ using Microsoft.AspNetCore.Authorization;
|
||||
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
|
||||
public class
|
||||
ProjectPeopleAccessPoliciesAuthorizationHandler : AuthorizationHandler<ProjectPeopleAccessPoliciesOperationRequirement,
|
||||
ProjectPeopleAccessPoliciesAuthorizationHandler : AuthorizationHandler<
|
||||
ProjectPeopleAccessPoliciesOperationRequirement,
|
||||
ProjectPeopleAccessPolicies>
|
||||
{
|
||||
private readonly IAccessClientQuery _accessClientQuery;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
private readonly ISameOrganizationQuery _sameOrganizationQuery;
|
||||
|
||||
public ProjectPeopleAccessPoliciesAuthorizationHandler(ICurrentContext currentContext,
|
||||
IAccessClientQuery accessClientQuery,
|
||||
IGroupRepository groupRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
ISameOrganizationQuery sameOrganizationQuery,
|
||||
IProjectRepository projectRepository)
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_accessClientQuery = accessClientQuery;
|
||||
_groupRepository = groupRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_sameOrganizationQuery = sameOrganizationQuery;
|
||||
_projectRepository = projectRepository;
|
||||
}
|
||||
|
||||
@ -71,9 +68,7 @@ public class
|
||||
if (resource.UserAccessPolicies != null && resource.UserAccessPolicies.Any())
|
||||
{
|
||||
var orgUserIds = resource.UserAccessPolicies.Select(ap => ap.OrganizationUserId!.Value).ToList();
|
||||
var users = await _organizationUserRepository.GetManyAsync(orgUserIds);
|
||||
if (users.Any(user => user.OrganizationId != resource.OrganizationId) ||
|
||||
users.Count != orgUserIds.Count)
|
||||
if (!await _sameOrganizationQuery.OrgUsersInTheSameOrgAsync(orgUserIds, resource.OrganizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -82,9 +77,7 @@ public class
|
||||
if (resource.GroupAccessPolicies != null && resource.GroupAccessPolicies.Any())
|
||||
{
|
||||
var groupIds = resource.GroupAccessPolicies.Select(ap => ap.GroupId!.Value).ToList();
|
||||
var groups = await _groupRepository.GetManyByManyIds(groupIds);
|
||||
if (groups.Any(group => group.OrganizationId != resource.OrganizationId) ||
|
||||
groups.Count != groupIds.Count)
|
||||
if (!await _sameOrganizationQuery.GroupsInTheSameOrgAsync(groupIds, resource.OrganizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -0,0 +1,89 @@
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
|
||||
public class
|
||||
ServiceAccountPeopleAccessPoliciesAuthorizationHandler : AuthorizationHandler<
|
||||
ServiceAccountPeopleAccessPoliciesOperationRequirement,
|
||||
ServiceAccountPeopleAccessPolicies>
|
||||
{
|
||||
private readonly IAccessClientQuery _accessClientQuery;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ISameOrganizationQuery _sameOrganizationQuery;
|
||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||
|
||||
public ServiceAccountPeopleAccessPoliciesAuthorizationHandler(ICurrentContext currentContext,
|
||||
IAccessClientQuery accessClientQuery,
|
||||
ISameOrganizationQuery sameOrganizationQuery,
|
||||
IServiceAccountRepository serviceAccountRepository)
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_accessClientQuery = accessClientQuery;
|
||||
_sameOrganizationQuery = sameOrganizationQuery;
|
||||
_serviceAccountRepository = serviceAccountRepository;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
ServiceAccountPeopleAccessPoliciesOperationRequirement requirement,
|
||||
ServiceAccountPeopleAccessPolicies 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 == ServiceAccountPeopleAccessPoliciesOperations.Replace:
|
||||
await CanReplaceServiceAccountPeopleAsync(context, requirement, resource, accessClient, userId);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported operation requirement type provided.",
|
||||
nameof(requirement));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanReplaceServiceAccountPeopleAsync(AuthorizationHandlerContext context,
|
||||
ServiceAccountPeopleAccessPoliciesOperationRequirement requirement, ServiceAccountPeopleAccessPolicies resource,
|
||||
AccessClientType accessClient, Guid userId)
|
||||
{
|
||||
var access = await _serviceAccountRepository.AccessToServiceAccountAsync(resource.Id, userId, accessClient);
|
||||
if (access.Write)
|
||||
{
|
||||
if (resource.UserAccessPolicies != null && resource.UserAccessPolicies.Any())
|
||||
{
|
||||
var orgUserIds = resource.UserAccessPolicies.Select(ap => ap.OrganizationUserId!.Value).ToList();
|
||||
if (!await _sameOrganizationQuery.OrgUsersInTheSameOrgAsync(orgUserIds, resource.OrganizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.GroupAccessPolicies != null && resource.GroupAccessPolicies.Any())
|
||||
{
|
||||
var groupIds = resource.GroupAccessPolicies.Select(ap => ap.GroupId!.Value).ToList();
|
||||
if (!await _sameOrganizationQuery.GroupsInTheSameOrgAsync(groupIds, resource.OrganizationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||
|
||||
namespace Bit.Commercial.Core.SecretsManager.Queries.AccessPolicies;
|
||||
|
||||
public class SameOrganizationQuery : ISameOrganizationQuery
|
||||
{
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
|
||||
public SameOrganizationQuery(IOrganizationUserRepository organizationUserRepository,
|
||||
IGroupRepository groupRepository)
|
||||
{
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_groupRepository = groupRepository;
|
||||
}
|
||||
|
||||
public async Task<bool> OrgUsersInTheSameOrgAsync(List<Guid> organizationUserIds, Guid organizationId)
|
||||
{
|
||||
var users = await _organizationUserRepository.GetManyAsync(organizationUserIds);
|
||||
return users.All(user => user.OrganizationId == organizationId) &&
|
||||
users.Count == organizationUserIds.Count;
|
||||
}
|
||||
|
||||
public async Task<bool> GroupsInTheSameOrgAsync(List<Guid> groupIds, Guid organizationId)
|
||||
{
|
||||
var groups = await _groupRepository.GetManyByManyIds(groupIds);
|
||||
return groups.All(group => group.OrganizationId == organizationId) &&
|
||||
groups.Count == groupIds.Count;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ using Bit.Commercial.Core.SecretsManager.Commands.Secrets;
|
||||
using Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
||||
using Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
||||
using Bit.Commercial.Core.SecretsManager.Queries;
|
||||
using Bit.Commercial.Core.SecretsManager.Queries.AccessPolicies;
|
||||
using Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
||||
using Bit.Commercial.Core.SecretsManager.Queries.ServiceAccounts;
|
||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
||||
@ -19,6 +20,7 @@ using Bit.Core.SecretsManager.Commands.Projects.Interfaces;
|
||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||
using Bit.Core.SecretsManager.Commands.Trash.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces;
|
||||
@ -36,8 +38,10 @@ public static class SecretsManagerCollectionExtensions
|
||||
services.AddScoped<IAuthorizationHandler, ServiceAccountAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, AccessPolicyAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, ProjectPeopleAccessPoliciesAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, ServiceAccountPeopleAccessPoliciesAuthorizationHandler>();
|
||||
services.AddScoped<IAccessClientQuery, AccessClientQuery>();
|
||||
services.AddScoped<IMaxProjectsQuery, MaxProjectsQuery>();
|
||||
services.AddScoped<ISameOrganizationQuery, SameOrganizationQuery>();
|
||||
services.AddScoped<IServiceAccountSecretsDetailsQuery, ServiceAccountSecretsDetailsQuery>();
|
||||
services.AddScoped<ICreateSecretCommand, CreateSecretCommand>();
|
||||
services.AddScoped<IUpdateSecretCommand, UpdateSecretCommand>();
|
||||
|
@ -183,28 +183,6 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
||||
return entities.Select(e => MapToCore(e.ap, e.CurrentUserInGroup));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id, Guid userId)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var entities = await dbContext.AccessPolicies.Where(ap =>
|
||||
((UserServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id ||
|
||||
((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id)
|
||||
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
|
||||
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
|
||||
.Select(ap => new
|
||||
{
|
||||
ap,
|
||||
CurrentUserInGroup = ap is GroupServiceAccountAccessPolicy &&
|
||||
((GroupServiceAccountAccessPolicy)ap).Group.GroupUsers.Any(g =>
|
||||
g.OrganizationUser.User.Id == userId),
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return entities.Select(e => MapToCore(e.ap, e.CurrentUserInGroup));
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(Guid id)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
@ -352,6 +330,81 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
||||
return await GetPeoplePoliciesByGrantedProjectIdAsync(peopleAccessPolicies.Id, userId);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>>
|
||||
GetPeoplePoliciesByGrantedServiceAccountIdAsync(Guid id, Guid userId)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var entities = await dbContext.AccessPolicies.Where(ap =>
|
||||
((UserServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id ||
|
||||
((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId == id)
|
||||
.Include(ap => ((UserServiceAccountAccessPolicy)ap).OrganizationUser.User)
|
||||
.Include(ap => ((GroupServiceAccountAccessPolicy)ap).Group)
|
||||
.Select(ap => new
|
||||
{
|
||||
ap,
|
||||
CurrentUserInGroup = ap is GroupServiceAccountAccessPolicy &&
|
||||
((GroupServiceAccountAccessPolicy)ap).Group.GroupUsers.Any(g =>
|
||||
g.OrganizationUser.UserId == userId)
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
return entities.Select(e => MapToCore(e.ap, e.CurrentUserInGroup));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>> ReplaceServiceAccountPeopleAsync(
|
||||
ServiceAccountPeopleAccessPolicies peopleAccessPolicies, Guid userId)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var peoplePolicyEntities = await dbContext.AccessPolicies.Where(ap =>
|
||||
((UserServiceAccountAccessPolicy)ap).GrantedServiceAccountId == peopleAccessPolicies.Id ||
|
||||
((GroupServiceAccountAccessPolicy)ap).GrantedServiceAccountId == peopleAccessPolicies.Id).ToListAsync();
|
||||
|
||||
var userPolicyEntities =
|
||||
peoplePolicyEntities.Where(ap => ap.GetType() == typeof(UserServiceAccountAccessPolicy)).ToList();
|
||||
var groupPolicyEntities =
|
||||
peoplePolicyEntities.Where(ap => ap.GetType() == typeof(GroupServiceAccountAccessPolicy)).ToList();
|
||||
|
||||
|
||||
if (peopleAccessPolicies.UserAccessPolicies == null || !peopleAccessPolicies.UserAccessPolicies.Any())
|
||||
{
|
||||
dbContext.RemoveRange(userPolicyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var userPolicyEntity in userPolicyEntities.Where(entity =>
|
||||
peopleAccessPolicies.UserAccessPolicies.All(ap =>
|
||||
((Core.SecretsManager.Entities.UserServiceAccountAccessPolicy)ap).OrganizationUserId !=
|
||||
((UserServiceAccountAccessPolicy)entity).OrganizationUserId)))
|
||||
{
|
||||
dbContext.Remove(userPolicyEntity);
|
||||
}
|
||||
}
|
||||
|
||||
if (peopleAccessPolicies.GroupAccessPolicies == null || !peopleAccessPolicies.GroupAccessPolicies.Any())
|
||||
{
|
||||
dbContext.RemoveRange(groupPolicyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var groupPolicyEntity in groupPolicyEntities.Where(entity =>
|
||||
peopleAccessPolicies.GroupAccessPolicies.All(ap =>
|
||||
((Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy)ap).GroupId !=
|
||||
((GroupServiceAccountAccessPolicy)entity).GroupId)))
|
||||
{
|
||||
dbContext.Remove(groupPolicyEntity);
|
||||
}
|
||||
}
|
||||
|
||||
await UpsertPeoplePoliciesAsync(dbContext,
|
||||
peopleAccessPolicies.ToBaseAccessPolicies().Select(MapToEntity).ToList(), userPolicyEntities,
|
||||
groupPolicyEntities);
|
||||
await dbContext.SaveChangesAsync();
|
||||
return await GetPeoplePoliciesByGrantedServiceAccountIdAsync(peopleAccessPolicies.Id, userId);
|
||||
}
|
||||
|
||||
private static async Task UpsertPeoplePoliciesAsync(DatabaseContext dbContext,
|
||||
List<BaseAccessPolicy> policies, IReadOnlyCollection<AccessPolicy> userPolicyEntities,
|
||||
IReadOnlyCollection<AccessPolicy> groupPolicyEntities)
|
||||
|
@ -1,14 +1,11 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||
@ -38,26 +35,16 @@ public class ProjectPeopleAccessPoliciesAuthorizationHandlerTests
|
||||
}
|
||||
|
||||
private static void SetupOrganizationUsers(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ProjectPeopleAccessPolicies resource)
|
||||
{
|
||||
var orgUsers = resource.UserAccessPolicies.Select(userPolicy =>
|
||||
new OrganizationUser
|
||||
{
|
||||
OrganizationId = resource.OrganizationId,
|
||||
Id = userPolicy.OrganizationUserId!.Value
|
||||
}).ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(default)
|
||||
.ReturnsForAnyArgs(orgUsers);
|
||||
}
|
||||
ProjectPeopleAccessPolicies resource) =>
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.OrgUsersInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
private static void SetupGroups(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ProjectPeopleAccessPolicies resource)
|
||||
{
|
||||
var groups = resource.GroupAccessPolicies.Select(groupPolicy =>
|
||||
new Group { OrganizationId = resource.OrganizationId, Id = groupPolicy.GroupId!.Value }).ToList();
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(default)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
}
|
||||
ProjectPeopleAccessPolicies resource) =>
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.GroupsInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
[Fact]
|
||||
public void PeopleAccessPoliciesOperations_OnlyPublicStatic()
|
||||
@ -129,37 +116,10 @@ public class ProjectPeopleAccessPoliciesAuthorizationHandlerTests
|
||||
{
|
||||
var requirement = ProjectPeopleAccessPoliciesOperations.Replace;
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||
var orgUsers = resource.UserAccessPolicies.Select(userPolicy =>
|
||||
new OrganizationUser { OrganizationId = Guid.NewGuid(), Id = userPolicy.OrganizationUserId!.Value })
|
||||
.ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(default)
|
||||
.ReturnsForAnyArgs(orgUsers);
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.OrgUsersInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||
.Returns(false);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.User)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck)]
|
||||
public async Task ReplaceProjectPeople_UserCountMismatch_DoesNotSucceed(AccessClientType accessClient,
|
||||
SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider, ProjectPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal, Guid userId)
|
||||
{
|
||||
var requirement = ProjectPeopleAccessPoliciesOperations.Replace;
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||
var orgUsers = resource.UserAccessPolicies.Select(userPolicy =>
|
||||
new OrganizationUser
|
||||
{
|
||||
OrganizationId = resource.OrganizationId,
|
||||
Id = userPolicy.OrganizationUserId!.Value
|
||||
}).ToList();
|
||||
orgUsers.RemoveAt(0);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(default)
|
||||
.ReturnsForAnyArgs(orgUsers);
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
@ -179,35 +139,8 @@ public class ProjectPeopleAccessPoliciesAuthorizationHandlerTests
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||
SetupOrganizationUsers(sutProvider, resource);
|
||||
|
||||
var groups = resource.GroupAccessPolicies.Select(groupPolicy =>
|
||||
new Group { OrganizationId = Guid.NewGuid(), Id = groupPolicy.GroupId!.Value }).ToList();
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(default)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.User)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck)]
|
||||
public async Task ReplaceProjectPeople_GroupCountMismatch_DoesNotSucceed(AccessClientType accessClient,
|
||||
SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider, ProjectPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal, Guid userId)
|
||||
{
|
||||
var requirement = ProjectPeopleAccessPoliciesOperations.Replace;
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||
SetupOrganizationUsers(sutProvider, resource);
|
||||
|
||||
var groups = resource.GroupAccessPolicies.Select(groupPolicy =>
|
||||
new Group { OrganizationId = resource.OrganizationId, Id = groupPolicy.GroupId!.Value }).ToList();
|
||||
groups.RemoveAt(0);
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(default)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.GroupsInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId).Returns(false);
|
||||
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
@ -0,0 +1,186 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Commercial.Core.Test.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class ServiceAccountPeopleAccessPoliciesAuthorizationHandlerTests
|
||||
{
|
||||
private static void SetupUserPermission(
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
AccessClientType accessClientType, ServiceAccountPeopleAccessPolicies resource, Guid userId = new(),
|
||||
bool read = true,
|
||||
bool write = true)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(resource.OrganizationId)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IAccessClientQuery>().GetAccessClientAsync(default, resource.OrganizationId)
|
||||
.ReturnsForAnyArgs(
|
||||
(accessClientType, userId));
|
||||
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||
.AccessToServiceAccountAsync(resource.Id, userId, accessClientType)
|
||||
.Returns((read, write));
|
||||
}
|
||||
|
||||
private static void SetupOrganizationUsers(
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource) =>
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.OrgUsersInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
private static void SetupGroups(SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource) =>
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.GroupsInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||
.Returns(true);
|
||||
|
||||
[Fact]
|
||||
public void ServiceAccountPeopleAccessPoliciesOperations_OnlyPublicStatic()
|
||||
{
|
||||
var publicStaticFields =
|
||||
typeof(ServiceAccountPeopleAccessPoliciesOperations).GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||
var allFields = typeof(ServiceAccountPeopleAccessPoliciesOperations).GetFields();
|
||||
Assert.Equal(publicStaticFields.Length, allFields.Length);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Handler_UnsupportedServiceAccountPeopleAccessPoliciesOperationRequirement_Throws(
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
var requirement = new ServiceAccountPeopleAccessPoliciesOperationRequirement();
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(resource.OrganizationId)
|
||||
.Returns(true);
|
||||
sutProvider.GetDependency<IAccessClientQuery>().GetAccessClientAsync(default, resource.OrganizationId)
|
||||
.ReturnsForAnyArgs(
|
||||
(AccessClientType.NoAccessCheck, new Guid()));
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await Assert.ThrowsAsync<ArgumentException>(() => sutProvider.Sut.HandleAsync(authzContext));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Handler_AccessSecretsManagerFalse_DoesNotSucceed(
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
var requirement = new ServiceAccountPeopleAccessPoliciesOperationRequirement();
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(resource.OrganizationId)
|
||||
.Returns(false);
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.ServiceAccount)]
|
||||
[BitAutoData(AccessClientType.Organization)]
|
||||
public async Task Handler_UnsupportedClientTypes_DoesNotSucceed(AccessClientType clientType,
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
var requirement = new ServiceAccountPeopleAccessPoliciesOperationRequirement();
|
||||
SetupUserPermission(sutProvider, clientType, resource);
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.User)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck)]
|
||||
public async Task ReplaceServiceAccountPeople_UserNotInOrg_DoesNotSucceed(AccessClientType accessClient,
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal, Guid userId)
|
||||
{
|
||||
var requirement = ServiceAccountPeopleAccessPoliciesOperations.Replace;
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.OrgUsersInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||
.Returns(false);
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.User)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck)]
|
||||
public async Task ReplaceServiceAccountPeople_GroupNotInOrg_DoesNotSucceed(AccessClientType accessClient,
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal, Guid userId)
|
||||
{
|
||||
var requirement = ServiceAccountPeopleAccessPoliciesOperations.Replace;
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||
SetupOrganizationUsers(sutProvider, resource);
|
||||
|
||||
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||
.GroupsInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId).Returns(false);
|
||||
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.User, false, false, false)]
|
||||
[BitAutoData(AccessClientType.User, false, true, true)]
|
||||
[BitAutoData(AccessClientType.User, true, false, false)]
|
||||
[BitAutoData(AccessClientType.User, true, true, true)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck, false, false, false)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck, false, true, true)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck, true, false, false)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck, true, true, true)]
|
||||
public async Task ReplaceServiceAccountPeople_AccessCheck(AccessClientType accessClient, bool read, bool write,
|
||||
bool expected,
|
||||
SutProvider<ServiceAccountPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ServiceAccountPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal, Guid userId)
|
||||
{
|
||||
var requirement = ServiceAccountPeopleAccessPoliciesOperations.Replace;
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId, read, write);
|
||||
SetupOrganizationUsers(sutProvider, resource);
|
||||
SetupGroups(sutProvider, resource);
|
||||
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.Equal(expected, authzContext.HasSucceeded);
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
using Bit.Commercial.Core.SecretsManager.Queries.AccessPolicies;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Commercial.Core.Test.SecretsManager.Queries.AccessPolicies;
|
||||
|
||||
[SutProviderCustomize]
|
||||
public class SameOrganizationQueryTests
|
||||
{
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task OrgUsersInTheSameOrg_NoOrgUsers_ReturnsFalse(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<OrganizationUser> orgUsers, Guid organizationId)
|
||||
{
|
||||
var orgUserIds = orgUsers.Select(ou => ou.Id).ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(orgUserIds)
|
||||
.ReturnsForAnyArgs(new List<OrganizationUser>());
|
||||
|
||||
var result = await sutProvider.Sut.OrgUsersInTheSameOrgAsync(orgUserIds, organizationId);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task OrgUsersInTheSameOrg_OrgMismatch_ReturnsFalse(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<OrganizationUser> orgUsers, Guid organizationId)
|
||||
{
|
||||
var orgUserIds = orgUsers.Select(ou => ou.Id).ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(orgUserIds)
|
||||
.ReturnsForAnyArgs(orgUsers);
|
||||
|
||||
var result = await sutProvider.Sut.OrgUsersInTheSameOrgAsync(orgUserIds, organizationId);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task OrgUsersInTheSameOrg_CountMismatch_ReturnsFalse(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<OrganizationUser> orgUsers, Guid organizationId)
|
||||
{
|
||||
var orgUserIds = orgUsers.Select(ou => ou.Id).ToList();
|
||||
foreach (var organizationUser in orgUsers)
|
||||
{
|
||||
organizationUser.OrganizationId = organizationId;
|
||||
}
|
||||
|
||||
orgUsers.RemoveAt(0);
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(orgUserIds)
|
||||
.ReturnsForAnyArgs(orgUsers);
|
||||
|
||||
var result = await sutProvider.Sut.OrgUsersInTheSameOrgAsync(orgUserIds, organizationId);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task OrgUsersInTheSameOrg_Success_ReturnsTrue(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<OrganizationUser> orgUsers, Guid organizationId)
|
||||
{
|
||||
var orgUserIds = orgUsers.Select(ou => ou.Id).ToList();
|
||||
foreach (var organizationUser in orgUsers)
|
||||
{
|
||||
organizationUser.OrganizationId = organizationId;
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(orgUserIds)
|
||||
.ReturnsForAnyArgs(orgUsers);
|
||||
|
||||
var result = await sutProvider.Sut.OrgUsersInTheSameOrgAsync(orgUserIds, organizationId);
|
||||
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GroupsInTheSameOrg_NoGroups_ReturnsFalse(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<Group> groups, Guid organizationId)
|
||||
{
|
||||
var groupIds = groups.Select(ou => ou.Id).ToList();
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(groupIds)
|
||||
.ReturnsForAnyArgs(new List<Group>());
|
||||
|
||||
var result = await sutProvider.Sut.GroupsInTheSameOrgAsync(groupIds, organizationId);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GroupsInTheSameOrg_OrgMismatch_ReturnsFalse(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<Group> groups, Guid organizationId)
|
||||
{
|
||||
var groupIds = groups.Select(ou => ou.Id).ToList();
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(groupIds)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
|
||||
var result = await sutProvider.Sut.GroupsInTheSameOrgAsync(groupIds, organizationId);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GroupsInTheSameOrg_CountMismatch_ReturnsFalse(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<Group> groups, Guid organizationId)
|
||||
{
|
||||
var groupIds = groups.Select(ou => ou.Id).ToList();
|
||||
foreach (var group in groups)
|
||||
{
|
||||
group.OrganizationId = organizationId;
|
||||
}
|
||||
|
||||
groups.RemoveAt(0);
|
||||
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(groupIds)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
|
||||
var result = await sutProvider.Sut.GroupsInTheSameOrgAsync(groupIds, organizationId);
|
||||
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task GroupsInTheSameOrg_Success_ReturnsTrue(SutProvider<SameOrganizationQuery> sutProvider,
|
||||
List<Group> groups, Guid organizationId)
|
||||
{
|
||||
var groupIds = groups.Select(ou => ou.Id).ToList();
|
||||
foreach (var group in groups)
|
||||
{
|
||||
group.OrganizationId = organizationId;
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(groupIds)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
|
||||
|
||||
var result = await sutProvider.Sut.GroupsInTheSameOrgAsync(groupIds, organizationId);
|
||||
|
||||
Assert.True(result);
|
||||
}
|
||||
}
|
@ -89,46 +89,6 @@ public class AccessPoliciesController : Controller
|
||||
return new ProjectAccessPoliciesResponseModel(results);
|
||||
}
|
||||
|
||||
[HttpPost("/service-accounts/{id}/access-policies")]
|
||||
public async Task<ServiceAccountAccessPoliciesResponseModel> CreateServiceAccountAccessPoliciesAsync(
|
||||
[FromRoute] Guid id,
|
||||
[FromBody] AccessPoliciesCreateRequest request)
|
||||
{
|
||||
if (request.Count() > _maxBulkCreation)
|
||||
{
|
||||
throw new BadRequestException($"Can process no more than {_maxBulkCreation} creation requests at once.");
|
||||
}
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||
if (serviceAccount == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var policies = request.ToBaseAccessPoliciesForServiceAccount(id, serviceAccount.OrganizationId);
|
||||
foreach (var policy in policies)
|
||||
{
|
||||
var authorizationResult = await _authorizationService.AuthorizeAsync(User, policy, AccessPolicyOperations.Create);
|
||||
if (!authorizationResult.Succeeded)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
var results = await _createAccessPoliciesCommand.CreateManyAsync(policies);
|
||||
return new ServiceAccountAccessPoliciesResponseModel(results);
|
||||
}
|
||||
|
||||
[HttpGet("/service-accounts/{id}/access-policies")]
|
||||
public async Task<ServiceAccountAccessPoliciesResponseModel> GetServiceAccountAccessPoliciesAsync(
|
||||
[FromRoute] Guid id)
|
||||
{
|
||||
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||
var (_, userId) = await CheckUserHasWriteAccessToServiceAccountAsync(serviceAccount);
|
||||
var results = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(id, userId);
|
||||
return new ServiceAccountAccessPoliciesResponseModel(results);
|
||||
}
|
||||
|
||||
[HttpPost("/service-accounts/{id}/granted-policies")]
|
||||
public async Task<ListResponseModel<ServiceAccountProjectAccessPolicyResponseModel>>
|
||||
CreateServiceAccountGrantedPoliciesAsync([FromRoute] Guid id,
|
||||
@ -308,6 +268,40 @@ public class AccessPoliciesController : Controller
|
||||
return new ProjectPeopleAccessPoliciesResponseModel(results, userId);
|
||||
}
|
||||
|
||||
[HttpGet("/service-accounts/{id}/access-policies/people")]
|
||||
public async Task<ServiceAccountPeopleAccessPoliciesResponseModel> GetServiceAccountPeopleAccessPoliciesAsync(
|
||||
[FromRoute] Guid id)
|
||||
{
|
||||
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||
var (_, userId) = await CheckUserHasWriteAccessToServiceAccountAsync(serviceAccount);
|
||||
var results = await _accessPolicyRepository.GetPeoplePoliciesByGrantedServiceAccountIdAsync(id, userId);
|
||||
return new ServiceAccountPeopleAccessPoliciesResponseModel(results, userId);
|
||||
}
|
||||
|
||||
[HttpPut("/service-accounts/{id}/access-policies/people")]
|
||||
public async Task<ServiceAccountPeopleAccessPoliciesResponseModel> PutServiceAccountPeopleAccessPoliciesAsync(
|
||||
[FromRoute] Guid id,
|
||||
[FromBody] PeopleAccessPoliciesRequestModel request)
|
||||
{
|
||||
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
|
||||
if (serviceAccount == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var peopleAccessPolicies = request.ToServiceAccountPeopleAccessPolicies(id, serviceAccount.OrganizationId);
|
||||
|
||||
var authorizationResult = await _authorizationService.AuthorizeAsync(User, peopleAccessPolicies,
|
||||
ServiceAccountPeopleAccessPoliciesOperations.Replace);
|
||||
if (!authorizationResult.Succeeded)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User)!.Value;
|
||||
var results = await _accessPolicyRepository.ReplaceServiceAccountPeopleAsync(peopleAccessPolicies, userId);
|
||||
return new ServiceAccountPeopleAccessPoliciesResponseModel(results, userId);
|
||||
}
|
||||
|
||||
private async Task<(AccessClientType AccessClientType, Guid UserId)> CheckUserHasWriteAccessToProjectAsync(Project project)
|
||||
{
|
||||
@ -345,6 +339,7 @@ public class AccessPoliciesController : Controller
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return (accessClient, userId);
|
||||
}
|
||||
|
||||
|
@ -61,4 +61,39 @@ public class PeopleAccessPoliciesRequestModel
|
||||
GroupAccessPolicies = groupAccessPolicies
|
||||
};
|
||||
}
|
||||
|
||||
public ServiceAccountPeopleAccessPolicies ToServiceAccountPeopleAccessPolicies(Guid grantedServiceAccountId, Guid organizationId)
|
||||
{
|
||||
var userAccessPolicies = UserAccessPolicyRequests?
|
||||
.Select(x => x.ToUserServiceAccountAccessPolicy(grantedServiceAccountId, organizationId)).ToList();
|
||||
|
||||
var groupAccessPolicies = GroupAccessPolicyRequests?
|
||||
.Select(x => x.ToGroupServiceAccountAccessPolicy(grantedServiceAccountId, organizationId)).ToList();
|
||||
|
||||
var policies = new List<BaseAccessPolicy>();
|
||||
if (userAccessPolicies != null)
|
||||
{
|
||||
policies.AddRange(userAccessPolicies);
|
||||
}
|
||||
|
||||
if (groupAccessPolicies != null)
|
||||
{
|
||||
policies.AddRange(groupAccessPolicies);
|
||||
}
|
||||
|
||||
CheckForDistinctAccessPolicies(policies);
|
||||
|
||||
if (!policies.All(ap => ap.Read && ap.Write))
|
||||
{
|
||||
throw new BadRequestException("Service account access must be Can read, write");
|
||||
}
|
||||
|
||||
return new ServiceAccountPeopleAccessPolicies
|
||||
{
|
||||
Id = grantedServiceAccountId,
|
||||
OrganizationId = organizationId,
|
||||
UserAccessPolicies = userAccessPolicies,
|
||||
GroupAccessPolicies = groupAccessPolicies
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -69,10 +69,14 @@ public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyRespo
|
||||
public UserServiceAccountAccessPolicyResponseModel(UserServiceAccountAccessPolicy accessPolicy)
|
||||
: base(accessPolicy, _objectName)
|
||||
{
|
||||
OrganizationUserId = accessPolicy.OrganizationUserId;
|
||||
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
||||
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
||||
UserId = accessPolicy.User?.Id;
|
||||
SetProperties(accessPolicy);
|
||||
}
|
||||
|
||||
public UserServiceAccountAccessPolicyResponseModel(UserServiceAccountAccessPolicy accessPolicy, Guid userId)
|
||||
: base(accessPolicy, _objectName)
|
||||
{
|
||||
SetProperties(accessPolicy);
|
||||
CurrentUser = accessPolicy.User?.Id == userId;
|
||||
}
|
||||
|
||||
public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName)
|
||||
@ -83,6 +87,15 @@ public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyRespo
|
||||
public string? OrganizationUserName { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? GrantedServiceAccountId { get; set; }
|
||||
public bool CurrentUser { get; set; }
|
||||
|
||||
private void SetProperties(UserServiceAccountAccessPolicy accessPolicy)
|
||||
{
|
||||
OrganizationUserId = accessPolicy.OrganizationUserId;
|
||||
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
||||
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
||||
UserId = accessPolicy.User?.Id;
|
||||
}
|
||||
}
|
||||
|
||||
public class GroupProjectAccessPolicyResponseModel : BaseAccessPolicyResponseModel
|
||||
|
@ -3,11 +3,11 @@ using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Models.Response;
|
||||
|
||||
public class ServiceAccountAccessPoliciesResponseModel : ResponseModel
|
||||
public class ServiceAccountPeopleAccessPoliciesResponseModel : ResponseModel
|
||||
{
|
||||
private const string _objectName = "serviceAccountAccessPolicies";
|
||||
|
||||
public ServiceAccountAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies)
|
||||
public ServiceAccountPeopleAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies, Guid userId)
|
||||
: base(_objectName)
|
||||
{
|
||||
if (baseAccessPolicies == null)
|
||||
@ -20,7 +20,7 @@ public class ServiceAccountAccessPoliciesResponseModel : ResponseModel
|
||||
switch (baseAccessPolicy)
|
||||
{
|
||||
case UserServiceAccountAccessPolicy accessPolicy:
|
||||
UserAccessPolicies.Add(new UserServiceAccountAccessPolicyResponseModel(accessPolicy));
|
||||
UserAccessPolicies.Add(new UserServiceAccountAccessPolicyResponseModel(accessPolicy, userId));
|
||||
break;
|
||||
case GroupServiceAccountAccessPolicy accessPolicy:
|
||||
GroupAccessPolicies.Add(new GroupServiceAccountAccessPolicyResponseModel(accessPolicy));
|
||||
@ -29,7 +29,7 @@ public class ServiceAccountAccessPoliciesResponseModel : ResponseModel
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceAccountAccessPoliciesResponseModel() : base(_objectName)
|
||||
public ServiceAccountPeopleAccessPoliciesResponseModel() : base(_objectName)
|
||||
{
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Authorization.Infrastructure;
|
||||
|
||||
namespace Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
|
||||
public class ServiceAccountPeopleAccessPoliciesOperationRequirement : OperationAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
|
||||
public static class ServiceAccountPeopleAccessPoliciesOperations
|
||||
{
|
||||
public static readonly ServiceAccountPeopleAccessPoliciesOperationRequirement Replace = new() { Name = nameof(Replace) };
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
namespace Bit.Core.SecretsManager.Models.Data;
|
||||
|
||||
public class ServiceAccountPeopleAccessPolicies
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationId { get; set; }
|
||||
public IEnumerable<UserServiceAccountAccessPolicy> UserAccessPolicies { get; set; }
|
||||
public IEnumerable<GroupServiceAccountAccessPolicy> GroupAccessPolicies { get; set; }
|
||||
|
||||
public IEnumerable<BaseAccessPolicy> ToBaseAccessPolicies()
|
||||
{
|
||||
var policies = new List<BaseAccessPolicy>();
|
||||
if (UserAccessPolicies != null && UserAccessPolicies.Any())
|
||||
{
|
||||
policies.AddRange(UserAccessPolicies);
|
||||
}
|
||||
|
||||
if (GroupAccessPolicies != null && GroupAccessPolicies.Any())
|
||||
{
|
||||
policies.AddRange(GroupAccessPolicies);
|
||||
}
|
||||
|
||||
return policies;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||
|
||||
public interface ISameOrganizationQuery
|
||||
{
|
||||
Task<bool> OrgUsersInTheSameOrgAsync(List<Guid> organizationUserIds, Guid organizationId);
|
||||
Task<bool> GroupsInTheSameOrgAsync(List<Guid> groupIds, Guid organizationId);
|
||||
}
|
@ -11,7 +11,6 @@ public interface IAccessPolicyRepository
|
||||
Task<bool> AccessPolicyExists(BaseAccessPolicy baseAccessPolicy);
|
||||
Task<BaseAccessPolicy?> GetByIdAsync(Guid id);
|
||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id, Guid userId);
|
||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id, Guid userId);
|
||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByServiceAccountIdAsync(Guid id, Guid userId,
|
||||
AccessClientType accessType);
|
||||
Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy);
|
||||
@ -19,4 +18,6 @@ public interface IAccessPolicyRepository
|
||||
Task<IEnumerable<BaseAccessPolicy>> GetPeoplePoliciesByGrantedProjectIdAsync(Guid id, Guid userId);
|
||||
Task<IEnumerable<BaseAccessPolicy>> ReplaceProjectPeopleAsync(ProjectPeopleAccessPolicies peopleAccessPolicies, Guid userId);
|
||||
Task<PeopleGrantees> GetPeopleGranteesAsync(Guid organizationId, Guid currentUserId);
|
||||
Task<IEnumerable<BaseAccessPolicy>> GetPeoplePoliciesByGrantedServiceAccountIdAsync(Guid id, Guid userId);
|
||||
Task<IEnumerable<BaseAccessPolicy>> ReplaceServiceAccountPeopleAsync(ServiceAccountPeopleAccessPolicies peopleAccessPolicies, Guid userId);
|
||||
}
|
||||
|
@ -627,228 +627,6 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
Assert.Equal(project.Id, result.Data.First(x => x.Id == project.Id).Id);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, false)]
|
||||
[InlineData(false, false, true)]
|
||||
[InlineData(false, true, false)]
|
||||
[InlineData(false, true, true)]
|
||||
[InlineData(true, false, false)]
|
||||
[InlineData(true, false, true)]
|
||||
[InlineData(true, true, false)]
|
||||
public async Task CreateServiceAccountAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled)
|
||||
{
|
||||
var (org, orgUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
var request = new AccessPoliciesCreateRequest
|
||||
{
|
||||
UserAccessPolicyRequests = new List<AccessPolicyRequest>
|
||||
{
|
||||
new() { GranteeId = orgUser.Id, Read = true, Write = true },
|
||||
},
|
||||
};
|
||||
|
||||
var response =
|
||||
await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies", request);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task CreateServiceAccountAccessPolicies_MismatchOrgId_NotFound(PermissionType permissionType)
|
||||
{
|
||||
var (_, orgUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
var anotherOrg = await _organizationHelper.CreateSmOrganizationAsync();
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = anotherOrg.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
var request =
|
||||
await SetupUserServiceAccountAccessPolicyRequestAsync(permissionType, orgUser.Id, serviceAccount.Id);
|
||||
|
||||
var response =
|
||||
await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies", request);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task CreateServiceAccountAccessPolicies_Success(PermissionType permissionType)
|
||||
{
|
||||
var (org, orgUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
var ownerOrgUserId = orgUser.Id;
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
var request =
|
||||
await SetupUserServiceAccountAccessPolicyRequestAsync(permissionType, orgUser.Id, serviceAccount.Id);
|
||||
|
||||
var response =
|
||||
await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies", request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<ServiceAccountAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(ownerOrgUserId,
|
||||
result!.UserAccessPolicies.First(ap => ap.OrganizationUserId == ownerOrgUserId).OrganizationUserId);
|
||||
Assert.True(result.UserAccessPolicies.First().Read);
|
||||
Assert.True(result.UserAccessPolicies.First().Write);
|
||||
|
||||
var createdAccessPolicy =
|
||||
await _accessPolicyRepository.GetByIdAsync(result.UserAccessPolicies.First().Id);
|
||||
Assert.NotNull(createdAccessPolicy);
|
||||
Assert.Equal(result.UserAccessPolicies.First().Read, createdAccessPolicy!.Read);
|
||||
Assert.Equal(result.UserAccessPolicies.First().Write, createdAccessPolicy.Write);
|
||||
Assert.Equal(result.UserAccessPolicies.First().Id, createdAccessPolicy.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateServiceAccountAccessPolicies_NoPermission()
|
||||
{
|
||||
// Create a new account as a user
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await LoginAsync(email);
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
var request = new AccessPoliciesCreateRequest
|
||||
{
|
||||
UserAccessPolicyRequests = new List<AccessPolicyRequest>
|
||||
{
|
||||
new() { GranteeId = orgUser.Id, Read = true, Write = true },
|
||||
},
|
||||
};
|
||||
|
||||
var response =
|
||||
await _client.PostAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies", request);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, false)]
|
||||
[InlineData(false, false, true)]
|
||||
[InlineData(false, true, false)]
|
||||
[InlineData(false, true, true)]
|
||||
[InlineData(true, false, false)]
|
||||
[InlineData(true, false, true)]
|
||||
[InlineData(true, true, false)]
|
||||
public async Task GetServiceAccountAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled)
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
||||
await LoginAsync(_email);
|
||||
var initData = await SetupAccessPolicyRequest(org.Id);
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/access-policies");
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetServiceAccountAccessPolicies_ReturnsEmpty()
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<ServiceAccountAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result!.UserAccessPolicies);
|
||||
Assert.Empty(result.GroupAccessPolicies);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetServiceAccountAccessPolicies_NoPermission()
|
||||
{
|
||||
// Create a new account as a user
|
||||
await _organizationHelper.Initialize(true, true, true);
|
||||
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await LoginAsync(email);
|
||||
|
||||
var initData = await SetupAccessPolicyRequest(orgUser.OrganizationId);
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/access-policies");
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task GetServiceAccountAccessPolicies(PermissionType permissionType)
|
||||
{
|
||||
var (org, ownerOrgUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
var initData = await SetupAccessPolicyRequest(org.Id);
|
||||
|
||||
if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||
{
|
||||
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await LoginAsync(email);
|
||||
var accessPolicies = new List<BaseAccessPolicy>
|
||||
{
|
||||
new UserServiceAccountAccessPolicy
|
||||
{
|
||||
GrantedServiceAccountId = initData.ServiceAccountId,
|
||||
OrganizationUserId = orgUser.Id,
|
||||
Read = true,
|
||||
Write = true,
|
||||
},
|
||||
};
|
||||
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
||||
}
|
||||
|
||||
var policies = new List<BaseAccessPolicy>
|
||||
{
|
||||
new UserServiceAccountAccessPolicy
|
||||
{
|
||||
GrantedServiceAccountId = initData.ServiceAccountId,
|
||||
OrganizationUserId = ownerOrgUser.Id,
|
||||
Read = true,
|
||||
Write = true,
|
||||
},
|
||||
};
|
||||
await _accessPolicyRepository.CreateManyAsync(policies);
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/access-policies");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<ServiceAccountAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result?.UserAccessPolicies);
|
||||
Assert.NotEmpty(result!.UserAccessPolicies);
|
||||
Assert.Equal(ownerOrgUser.Id,
|
||||
result.UserAccessPolicies.First(x => x.OrganizationUserId == ownerOrgUser.Id).OrganizationUserId);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, false)]
|
||||
[InlineData(false, false, true)]
|
||||
@ -1066,9 +844,13 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var initData = await SetupAccessPolicyRequest(org.Id);
|
||||
var project = await _projectRepository.CreateAsync(new Project
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString
|
||||
});
|
||||
|
||||
var response = await _client.GetAsync($"/projects/{initData.ProjectId}/access-policies/people");
|
||||
var response = await _client.GetAsync($"/projects/{project.Id}/access-policies/people");
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
@ -1230,6 +1012,183 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
Assert.Equal(result.UserAccessPolicies.First().Id, createdAccessPolicy.Id);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false, false)]
|
||||
[InlineData(false, false, true)]
|
||||
[InlineData(false, true, false)]
|
||||
[InlineData(false, true, true)]
|
||||
[InlineData(true, false, false)]
|
||||
[InlineData(true, false, true)]
|
||||
[InlineData(true, true, false)]
|
||||
public async Task GetServiceAccountPeopleAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled)
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
||||
await LoginAsync(_email);
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people");
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetServiceAccountPeopleAccessPolicies_ReturnsEmpty()
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<ServiceAccountPeopleAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result!.UserAccessPolicies);
|
||||
Assert.Empty(result.GroupAccessPolicies);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetServiceAccountPeopleAccessPolicies_NoPermission()
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await LoginAsync(email);
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people");
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task GetServiceAccountPeopleAccessPolicies_Success(PermissionType permissionType)
|
||||
{
|
||||
var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var (serviceAccount, _) = await SetupServiceAccountPeoplePermissionAsync(permissionType, organizationUser);
|
||||
|
||||
var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<ServiceAccountPeopleAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result?.UserAccessPolicies);
|
||||
Assert.Single(result!.UserAccessPolicies);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false)]
|
||||
[InlineData(true, false)]
|
||||
[InlineData(false, true)]
|
||||
public async Task PutServiceAccountPeopleAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets)
|
||||
{
|
||||
var (_, organizationUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets, true);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var (serviceAccount, request) = await SetupServiceAccountPeopleRequestAsync(PermissionType.RunAsAdmin, organizationUser);
|
||||
|
||||
var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request);
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PutServiceAccountPeopleAccessPolicies_NoPermission()
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
var (email, organizationUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await LoginAsync(email);
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
|
||||
var request = new PeopleAccessPoliciesRequestModel
|
||||
{
|
||||
UserAccessPolicyRequests = new List<AccessPolicyRequest>
|
||||
{
|
||||
new() { GranteeId = organizationUser.Id, Read = true, Write = true }
|
||||
}
|
||||
};
|
||||
|
||||
var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task PutServiceAccountPeopleAccessPolicies_MismatchedOrgIds_NotFound(PermissionType permissionType)
|
||||
{
|
||||
var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var (serviceAccount, request) = await SetupServiceAccountPeopleRequestAsync(permissionType, organizationUser);
|
||||
var newOrg = await _organizationHelper.CreateSmOrganizationAsync();
|
||||
var group = await _groupRepository.CreateAsync(new Group
|
||||
{
|
||||
OrganizationId = newOrg.Id,
|
||||
Name = _mockEncryptedString
|
||||
});
|
||||
request.GroupAccessPolicyRequests = new List<AccessPolicyRequest>
|
||||
{
|
||||
new() { GranteeId = group.Id, Read = true, Write = true }
|
||||
};
|
||||
|
||||
var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task PutServiceAccountPeopleAccessPolicies_Success(PermissionType permissionType)
|
||||
{
|
||||
var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await LoginAsync(_email);
|
||||
|
||||
var (serviceAccount, request) = await SetupServiceAccountPeopleRequestAsync(permissionType, organizationUser);
|
||||
|
||||
var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<ServiceAccountPeopleAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(request.UserAccessPolicyRequests.First().GranteeId,
|
||||
result!.UserAccessPolicies.First().OrganizationUserId);
|
||||
Assert.True(result.UserAccessPolicies.First().Read);
|
||||
Assert.True(result.UserAccessPolicies.First().Write);
|
||||
|
||||
var createdAccessPolicy =
|
||||
await _accessPolicyRepository.GetByIdAsync(result.UserAccessPolicies.First().Id);
|
||||
Assert.NotNull(createdAccessPolicy);
|
||||
Assert.Equal(result.UserAccessPolicies.First().Read, createdAccessPolicy!.Read);
|
||||
Assert.Equal(result.UserAccessPolicies.First().Write, createdAccessPolicy.Write);
|
||||
Assert.Equal(result.UserAccessPolicies.First().Id, createdAccessPolicy.Id);
|
||||
}
|
||||
|
||||
private async Task<RequestSetupData> SetupAccessPolicyRequest(Guid organizationId)
|
||||
{
|
||||
var project = await _projectRepository.CreateAsync(new Project
|
||||
@ -1293,6 +1252,38 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
return (project, organizationUser);
|
||||
}
|
||||
|
||||
private async Task<(ServiceAccount serviceAccount, OrganizationUser currentUser)> SetupServiceAccountPeoplePermissionAsync(
|
||||
PermissionType permissionType,
|
||||
OrganizationUser organizationUser)
|
||||
{
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
OrganizationId = organizationUser.OrganizationId,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||
{
|
||||
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await LoginAsync(email);
|
||||
organizationUser = orgUser;
|
||||
}
|
||||
|
||||
var accessPolicies = new List<BaseAccessPolicy>
|
||||
{
|
||||
new UserServiceAccountAccessPolicy
|
||||
{
|
||||
GrantedServiceAccountId = serviceAccount.Id,
|
||||
OrganizationUserId = organizationUser.Id,
|
||||
Read = true,
|
||||
Write = true
|
||||
}
|
||||
};
|
||||
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
||||
|
||||
return (serviceAccount, organizationUser);
|
||||
}
|
||||
|
||||
private async Task<(Project project, PeopleAccessPoliciesRequestModel request)> SetupProjectPeopleRequestAsync(
|
||||
PermissionType permissionType, OrganizationUser organizationUser)
|
||||
{
|
||||
@ -1307,6 +1298,20 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
return (project, request);
|
||||
}
|
||||
|
||||
private async Task<(ServiceAccount serviceAccount, PeopleAccessPoliciesRequestModel request)> SetupServiceAccountPeopleRequestAsync(
|
||||
PermissionType permissionType, OrganizationUser organizationUser)
|
||||
{
|
||||
var (serviceAccount, currentUser) = await SetupServiceAccountPeoplePermissionAsync(permissionType, organizationUser);
|
||||
var request = new PeopleAccessPoliciesRequestModel
|
||||
{
|
||||
UserAccessPolicyRequests = new List<AccessPolicyRequest>
|
||||
{
|
||||
new() { GranteeId = currentUser.Id, Read = true, Write = true }
|
||||
}
|
||||
};
|
||||
return (serviceAccount, request);
|
||||
}
|
||||
|
||||
private async Task<(Guid ProjectId, Guid ServiceAccountId)> CreateProjectAndServiceAccountAsync(Guid organizationId,
|
||||
bool misMatchOrganization = false)
|
||||
{
|
||||
|
@ -248,7 +248,7 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
|
||||
AssertHelper.AssertRecent(createdServiceAccount.CreationDate);
|
||||
|
||||
// Check permissions have been bootstrapped.
|
||||
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(createdServiceAccount.Id, currentUserId);
|
||||
var accessPolicies = await _accessPolicyRepository.GetPeoplePoliciesByGrantedServiceAccountIdAsync(createdServiceAccount.Id, currentUserId);
|
||||
Assert.NotNull(accessPolicies);
|
||||
var ap = (UserServiceAccountAccessPolicy)accessPolicies.First();
|
||||
Assert.Equal(createdServiceAccount.Id, ap.GrantedServiceAccountId);
|
||||
|
@ -51,6 +51,23 @@ public class AccessPoliciesControllerTests
|
||||
return request;
|
||||
}
|
||||
|
||||
private static PeopleAccessPoliciesRequestModel SetRequestToCanReadWrite(PeopleAccessPoliciesRequestModel request)
|
||||
{
|
||||
foreach (var ap in request.UserAccessPolicyRequests)
|
||||
{
|
||||
ap.Read = true;
|
||||
ap.Write = true;
|
||||
}
|
||||
|
||||
foreach (var ap in request.GroupAccessPolicyRequests)
|
||||
{
|
||||
ap.Read = true;
|
||||
ap.Write = true;
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private static void SetupAdmin(SutProvider<AccessPoliciesController> sutProvider, Guid organizationId)
|
||||
{
|
||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(default).ReturnsForAnyArgs(true);
|
||||
@ -103,12 +120,14 @@ public class AccessPoliciesControllerTests
|
||||
{
|
||||
case PermissionType.RunAsAdmin:
|
||||
SetupAdmin(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), AccessClientType.NoAccessCheck)
|
||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(),
|
||||
AccessClientType.NoAccessCheck)
|
||||
.Returns((true, true));
|
||||
break;
|
||||
case PermissionType.RunAsUserWithPermission:
|
||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), AccessClientType.User)
|
||||
sutProvider.GetDependency<IProjectRepository>()
|
||||
.AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), AccessClientType.User)
|
||||
.Returns((true, true));
|
||||
break;
|
||||
}
|
||||
@ -156,12 +175,14 @@ public class AccessPoliciesControllerTests
|
||||
{
|
||||
case PermissionType.RunAsAdmin:
|
||||
SetupAdmin(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), AccessClientType.NoAccessCheck)
|
||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(),
|
||||
AccessClientType.NoAccessCheck)
|
||||
.Returns((true, true));
|
||||
break;
|
||||
case PermissionType.RunAsUserWithPermission:
|
||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), AccessClientType.User)
|
||||
sutProvider.GetDependency<IProjectRepository>()
|
||||
.AccessToProjectAsync(Arg.Any<Guid>(), Arg.Any<Guid>(), AccessClientType.User)
|
||||
.Returns((true, true));
|
||||
break;
|
||||
}
|
||||
@ -201,114 +222,6 @@ public class AccessPoliciesControllerTests
|
||||
.GetManyByGrantedProjectIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||
public async void GetServiceAccountAccessPolicies_ReturnsEmptyList(
|
||||
PermissionType permissionType,
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id, ServiceAccount data)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
|
||||
|
||||
switch (permissionType)
|
||||
{
|
||||
case PermissionType.RunAsAdmin:
|
||||
SetupAdmin(sutProvider, data.OrganizationId);
|
||||
break;
|
||||
case PermissionType.RunAsUserWithPermission:
|
||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||
.UserHasWriteAccessToServiceAccount(default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
break;
|
||||
}
|
||||
|
||||
var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id);
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any<Guid>());
|
||||
|
||||
Assert.Empty(result.UserAccessPolicies);
|
||||
Assert.Empty(result.GroupAccessPolicies);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void GetServiceAccountAccessPolicies_UserWithoutPermission_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
ServiceAccount data)
|
||||
{
|
||||
SetupUserWithoutPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(default, default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||
public async void GetServiceAccountAccessPolicies_Success(
|
||||
PermissionType permissionType,
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
ServiceAccount data,
|
||||
UserServiceAccountAccessPolicy resultAccessPolicy)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
|
||||
switch (permissionType)
|
||||
{
|
||||
case PermissionType.RunAsAdmin:
|
||||
SetupAdmin(sutProvider, data.OrganizationId);
|
||||
break;
|
||||
case PermissionType.RunAsUserWithPermission:
|
||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||
.UserHasWriteAccessToServiceAccount(default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
break;
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedServiceAccountIdAsync(default, default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
||||
|
||||
var result = await sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id);
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(id)), Arg.Any<Guid>());
|
||||
|
||||
Assert.Empty(result.GroupAccessPolicies);
|
||||
Assert.NotEmpty(result.UserAccessPolicies);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void GetServiceAccountAccessPolicies_ServiceAccountExists_UserWithoutPermission_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
ServiceAccount data,
|
||||
UserServiceAccountAccessPolicy resultAccessPolicy)
|
||||
{
|
||||
SetupUserWithoutPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(default, default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetManyByGrantedServiceAccountIdAsync(default, default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.GetServiceAccountAccessPoliciesAsync(id));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.GetManyByGrantedServiceAccountIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||
@ -419,7 +332,7 @@ public class AccessPoliciesControllerTests
|
||||
UserProjectAccessPolicy data,
|
||||
AccessPoliciesCreateRequest request)
|
||||
{
|
||||
var dup = new AccessPolicyRequest() { GranteeId = Guid.NewGuid(), Read = true, Write = true };
|
||||
var dup = new AccessPolicyRequest { GranteeId = Guid.NewGuid(), Read = true, Write = true };
|
||||
request.UserAccessPolicyRequests = new[] { dup, dup };
|
||||
mockProject.Id = id;
|
||||
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(default).ReturnsForAnyArgs(mockProject);
|
||||
@ -451,6 +364,7 @@ public class AccessPoliciesControllerTests
|
||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>().CreateManyAsync(default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||
|
||||
@ -479,6 +393,7 @@ public class AccessPoliciesControllerTests
|
||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>().CreateManyAsync(default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||
|
||||
@ -488,124 +403,6 @@ public class AccessPoliciesControllerTests
|
||||
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void CreateServiceAccountAccessPolicies_RequestMoreThanMax_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
ServiceAccount serviceAccount,
|
||||
UserServiceAccountAccessPolicy data,
|
||||
AccessPoliciesCreateRequest request)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount);
|
||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>()
|
||||
.CreateManyAsync(default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||
|
||||
request = AddRequestsOverMax(request);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.CreateServiceAccountAccessPoliciesAsync(id, request));
|
||||
|
||||
await sutProvider.GetDependency<ICreateAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
|
||||
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void CreateServiceAccountAccessPolicies_ServiceAccountDoesNotExist_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
AccessPoliciesCreateRequest request)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.CreateServiceAccountAccessPoliciesAsync(id, request));
|
||||
|
||||
await sutProvider.GetDependency<ICreateAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
|
||||
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void CreateServiceAccountAccessPolicies_DuplicatePolicy_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
ServiceAccount serviceAccount,
|
||||
UserServiceAccountAccessPolicy data,
|
||||
AccessPoliciesCreateRequest request)
|
||||
{
|
||||
var dup = new AccessPolicyRequest() { GranteeId = Guid.NewGuid(), Read = true, Write = true };
|
||||
request.UserAccessPolicyRequests = new[] { dup, dup };
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount);
|
||||
|
||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>()
|
||||
.CreateManyAsync(default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.CreateServiceAccountAccessPoliciesAsync(id, request));
|
||||
|
||||
await sutProvider.GetDependency<ICreateAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
|
||||
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void CreateServiceAccountAccessPolicies_NoAccess_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
ServiceAccount serviceAccount,
|
||||
UserServiceAccountAccessPolicy data,
|
||||
AccessPoliciesCreateRequest request)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount);
|
||||
var policies = request.ToBaseAccessPoliciesForServiceAccount(id, serviceAccount.OrganizationId);
|
||||
foreach (var policy in policies)
|
||||
{
|
||||
sutProvider.GetDependency<IAuthorizationService>()
|
||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>()
|
||||
.CreateManyAsync(default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.CreateServiceAccountAccessPoliciesAsync(id, request));
|
||||
|
||||
await sutProvider.GetDependency<ICreateAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
|
||||
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void CreateServiceAccountAccessPolicies_Success(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
Guid id,
|
||||
ServiceAccount serviceAccount,
|
||||
UserServiceAccountAccessPolicy data,
|
||||
AccessPoliciesCreateRequest request)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount);
|
||||
var policies = request.ToBaseAccessPoliciesForServiceAccount(id, serviceAccount.OrganizationId);
|
||||
foreach (var policy in policies)
|
||||
{
|
||||
sutProvider.GetDependency<IAuthorizationService>()
|
||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>()
|
||||
.CreateManyAsync(default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||
|
||||
await sutProvider.Sut.CreateServiceAccountAccessPoliciesAsync(id, request);
|
||||
|
||||
await sutProvider.GetDependency<ICreateAccessPoliciesCommand>().Received(1)
|
||||
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void CreateServiceAccountGrantedPolicies_RequestMoreThanMax_Throws(
|
||||
@ -652,7 +449,7 @@ public class AccessPoliciesControllerTests
|
||||
ServiceAccountProjectAccessPolicy data,
|
||||
List<GrantedAccessPolicyRequest> request)
|
||||
{
|
||||
var dup = new GrantedAccessPolicyRequest() { GrantedId = Guid.NewGuid(), Read = true, Write = true };
|
||||
var dup = new GrantedAccessPolicyRequest { GrantedId = Guid.NewGuid(), Read = true, Write = true };
|
||||
request.Add(dup);
|
||||
request.Add(dup);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount);
|
||||
@ -1173,4 +970,199 @@ public class AccessPoliciesControllerTests
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||
.ReplaceProjectPeopleAsync(Arg.Any<ProjectPeopleAccessPolicies>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void GetServiceAccountPeopleAccessPoliciesAsync_ServiceAccountDoesntExist_ThrowsNotFound(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).ReturnsNull();
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.GetServiceAccountPeopleAccessPoliciesAsync(data.Id));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.GetPeoplePoliciesByGrantedServiceAccountIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||
public async void GetServiceAccountPeopleAccessPoliciesAsync_ReturnsEmptyList(
|
||||
PermissionType permissionType,
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
|
||||
|
||||
switch (permissionType)
|
||||
{
|
||||
case PermissionType.RunAsAdmin:
|
||||
SetupAdmin(sutProvider, data.OrganizationId);
|
||||
break;
|
||||
case PermissionType.RunAsUserWithPermission:
|
||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||
.UserHasWriteAccessToServiceAccount(default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
break;
|
||||
}
|
||||
|
||||
var result = await sutProvider.Sut.GetServiceAccountPeopleAccessPoliciesAsync(data.Id);
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||
.GetPeoplePoliciesByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.Id)), Arg.Any<Guid>());
|
||||
|
||||
Assert.Empty(result.UserAccessPolicies);
|
||||
Assert.Empty(result.GroupAccessPolicies);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void GetServiceAccountPeopleAccessPoliciesAsync_UserWithoutPermission_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data)
|
||||
{
|
||||
SetupUserWithoutPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().UserHasWriteAccessToServiceAccount(default, default)
|
||||
.ReturnsForAnyArgs(false);
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.GetServiceAccountPeopleAccessPoliciesAsync(data.Id));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.GetPeoplePoliciesByGrantedServiceAccountIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||
public async void GetServiceAccountPeopleAccessPoliciesAsync_Success(
|
||||
PermissionType permissionType,
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data,
|
||||
UserServiceAccountAccessPolicy resultAccessPolicy)
|
||||
{
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(data);
|
||||
switch (permissionType)
|
||||
{
|
||||
case PermissionType.RunAsAdmin:
|
||||
SetupAdmin(sutProvider, data.OrganizationId);
|
||||
break;
|
||||
case PermissionType.RunAsUserWithPermission:
|
||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>()
|
||||
.UserHasWriteAccessToServiceAccount(default, default)
|
||||
.ReturnsForAnyArgs(true);
|
||||
break;
|
||||
}
|
||||
|
||||
sutProvider.GetDependency<IAccessPolicyRepository>().GetPeoplePoliciesByGrantedServiceAccountIdAsync(default, default)
|
||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { resultAccessPolicy });
|
||||
|
||||
var result = await sutProvider.Sut.GetServiceAccountPeopleAccessPoliciesAsync(data.Id);
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||
.GetPeoplePoliciesByGrantedServiceAccountIdAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data.Id)), Arg.Any<Guid>());
|
||||
|
||||
Assert.Empty(result.GroupAccessPolicies);
|
||||
Assert.NotEmpty(result.UserAccessPolicies);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void PutServiceAccountPeopleAccessPolicies_ServiceAccountDoesNotExist_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data,
|
||||
PeopleAccessPoliciesRequestModel request)
|
||||
{
|
||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.PutServiceAccountPeopleAccessPoliciesAsync(data.Id, request));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.ReplaceServiceAccountPeopleAsync(Arg.Any<ServiceAccountPeopleAccessPolicies>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void PutServiceAccountPeopleAccessPolicies_DuplicatePolicy_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data,
|
||||
PeopleAccessPoliciesRequestModel request)
|
||||
{
|
||||
var dup = new AccessPolicyRequest { GranteeId = Guid.NewGuid(), Read = true, Write = true };
|
||||
request.UserAccessPolicyRequests = new[] { dup, dup };
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.PutServiceAccountPeopleAccessPoliciesAsync(data.Id, request));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.ReplaceServiceAccountPeopleAsync(Arg.Any<ServiceAccountPeopleAccessPolicies>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void PutServiceAccountPeopleAccessPolicies_NotCanReadWrite_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data,
|
||||
PeopleAccessPoliciesRequestModel request)
|
||||
{
|
||||
request.UserAccessPolicyRequests.First().Read = false;
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
|
||||
|
||||
await Assert.ThrowsAsync<BadRequestException>(() =>
|
||||
sutProvider.Sut.PutServiceAccountPeopleAccessPoliciesAsync(data.Id, request));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.ReplaceServiceAccountPeopleAsync(Arg.Any<ServiceAccountPeopleAccessPolicies>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void PutServiceAccountPeopleAccessPolicies_NoAccess_Throws(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data,
|
||||
PeopleAccessPoliciesRequestModel request)
|
||||
{
|
||||
request = SetRequestToCanReadWrite(request);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
|
||||
var peoplePolicies = request.ToServiceAccountPeopleAccessPolicies(data.Id, data.OrganizationId);
|
||||
sutProvider.GetDependency<IAuthorizationService>()
|
||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), peoplePolicies,
|
||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(() =>
|
||||
sutProvider.Sut.PutServiceAccountPeopleAccessPoliciesAsync(data.Id, request));
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().DidNotReceiveWithAnyArgs()
|
||||
.ReplaceServiceAccountPeopleAsync(Arg.Any<ServiceAccountPeopleAccessPolicies>(), Arg.Any<Guid>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async void PutServiceAccountPeopleAccessPolicies_Success(
|
||||
SutProvider<AccessPoliciesController> sutProvider,
|
||||
ServiceAccount data,
|
||||
Guid userId,
|
||||
PeopleAccessPoliciesRequestModel request)
|
||||
{
|
||||
request = SetRequestToCanReadWrite(request);
|
||||
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
|
||||
var peoplePolicies = request.ToServiceAccountPeopleAccessPolicies(data.Id, data.OrganizationId);
|
||||
sutProvider.GetDependency<IAuthorizationService>()
|
||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), peoplePolicies,
|
||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
|
||||
|
||||
sutProvider.GetDependency<IAccessPolicyRepository>().ReplaceServiceAccountPeopleAsync(peoplePolicies, Arg.Any<Guid>())
|
||||
.Returns(peoplePolicies.ToBaseAccessPolicies());
|
||||
|
||||
await sutProvider.Sut.PutServiceAccountPeopleAccessPoliciesAsync(data.Id, request);
|
||||
|
||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||
.ReplaceServiceAccountPeopleAsync(Arg.Any<ServiceAccountPeopleAccessPolicies>(), Arg.Any<Guid>());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user