mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 21:18:13 -05: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.Enums;
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||||
using Bit.Core.SecretsManager.Models.Data;
|
using Bit.Core.SecretsManager.Models.Data;
|
||||||
|
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@ -11,25 +10,23 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||||
|
|
||||||
public class
|
public class
|
||||||
ProjectPeopleAccessPoliciesAuthorizationHandler : AuthorizationHandler<ProjectPeopleAccessPoliciesOperationRequirement,
|
ProjectPeopleAccessPoliciesAuthorizationHandler : AuthorizationHandler<
|
||||||
|
ProjectPeopleAccessPoliciesOperationRequirement,
|
||||||
ProjectPeopleAccessPolicies>
|
ProjectPeopleAccessPolicies>
|
||||||
{
|
{
|
||||||
private readonly IAccessClientQuery _accessClientQuery;
|
private readonly IAccessClientQuery _accessClientQuery;
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly IGroupRepository _groupRepository;
|
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
||||||
private readonly IProjectRepository _projectRepository;
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly ISameOrganizationQuery _sameOrganizationQuery;
|
||||||
|
|
||||||
public ProjectPeopleAccessPoliciesAuthorizationHandler(ICurrentContext currentContext,
|
public ProjectPeopleAccessPoliciesAuthorizationHandler(ICurrentContext currentContext,
|
||||||
IAccessClientQuery accessClientQuery,
|
IAccessClientQuery accessClientQuery,
|
||||||
IGroupRepository groupRepository,
|
ISameOrganizationQuery sameOrganizationQuery,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
|
||||||
IProjectRepository projectRepository)
|
IProjectRepository projectRepository)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_accessClientQuery = accessClientQuery;
|
_accessClientQuery = accessClientQuery;
|
||||||
_groupRepository = groupRepository;
|
_sameOrganizationQuery = sameOrganizationQuery;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
|
||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,9 +68,7 @@ public class
|
|||||||
if (resource.UserAccessPolicies != null && resource.UserAccessPolicies.Any())
|
if (resource.UserAccessPolicies != null && resource.UserAccessPolicies.Any())
|
||||||
{
|
{
|
||||||
var orgUserIds = resource.UserAccessPolicies.Select(ap => ap.OrganizationUserId!.Value).ToList();
|
var orgUserIds = resource.UserAccessPolicies.Select(ap => ap.OrganizationUserId!.Value).ToList();
|
||||||
var users = await _organizationUserRepository.GetManyAsync(orgUserIds);
|
if (!await _sameOrganizationQuery.OrgUsersInTheSameOrgAsync(orgUserIds, resource.OrganizationId))
|
||||||
if (users.Any(user => user.OrganizationId != resource.OrganizationId) ||
|
|
||||||
users.Count != orgUserIds.Count)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -82,9 +77,7 @@ public class
|
|||||||
if (resource.GroupAccessPolicies != null && resource.GroupAccessPolicies.Any())
|
if (resource.GroupAccessPolicies != null && resource.GroupAccessPolicies.Any())
|
||||||
{
|
{
|
||||||
var groupIds = resource.GroupAccessPolicies.Select(ap => ap.GroupId!.Value).ToList();
|
var groupIds = resource.GroupAccessPolicies.Select(ap => ap.GroupId!.Value).ToList();
|
||||||
var groups = await _groupRepository.GetManyByManyIds(groupIds);
|
if (!await _sameOrganizationQuery.GroupsInTheSameOrgAsync(groupIds, resource.OrganizationId))
|
||||||
if (groups.Any(group => group.OrganizationId != resource.OrganizationId) ||
|
|
||||||
groups.Count != groupIds.Count)
|
|
||||||
{
|
{
|
||||||
return;
|
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.ServiceAccounts;
|
||||||
using Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
using Bit.Commercial.Core.SecretsManager.Commands.Trash;
|
||||||
using Bit.Commercial.Core.SecretsManager.Queries;
|
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.Projects;
|
||||||
using Bit.Commercial.Core.SecretsManager.Queries.ServiceAccounts;
|
using Bit.Commercial.Core.SecretsManager.Queries.ServiceAccounts;
|
||||||
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
|
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.Secrets.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Commands.Trash.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.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces;
|
using Bit.Core.SecretsManager.Queries.ServiceAccounts.Interfaces;
|
||||||
@ -36,8 +38,10 @@ public static class SecretsManagerCollectionExtensions
|
|||||||
services.AddScoped<IAuthorizationHandler, ServiceAccountAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, ServiceAccountAuthorizationHandler>();
|
||||||
services.AddScoped<IAuthorizationHandler, AccessPolicyAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, AccessPolicyAuthorizationHandler>();
|
||||||
services.AddScoped<IAuthorizationHandler, ProjectPeopleAccessPoliciesAuthorizationHandler>();
|
services.AddScoped<IAuthorizationHandler, ProjectPeopleAccessPoliciesAuthorizationHandler>();
|
||||||
|
services.AddScoped<IAuthorizationHandler, ServiceAccountPeopleAccessPoliciesAuthorizationHandler>();
|
||||||
services.AddScoped<IAccessClientQuery, AccessClientQuery>();
|
services.AddScoped<IAccessClientQuery, AccessClientQuery>();
|
||||||
services.AddScoped<IMaxProjectsQuery, MaxProjectsQuery>();
|
services.AddScoped<IMaxProjectsQuery, MaxProjectsQuery>();
|
||||||
|
services.AddScoped<ISameOrganizationQuery, SameOrganizationQuery>();
|
||||||
services.AddScoped<IServiceAccountSecretsDetailsQuery, ServiceAccountSecretsDetailsQuery>();
|
services.AddScoped<IServiceAccountSecretsDetailsQuery, ServiceAccountSecretsDetailsQuery>();
|
||||||
services.AddScoped<ICreateSecretCommand, CreateSecretCommand>();
|
services.AddScoped<ICreateSecretCommand, CreateSecretCommand>();
|
||||||
services.AddScoped<IUpdateSecretCommand, UpdateSecretCommand>();
|
services.AddScoped<IUpdateSecretCommand, UpdateSecretCommand>();
|
||||||
|
@ -183,28 +183,6 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
return entities.Select(e => MapToCore(e.ap, e.CurrentUserInGroup));
|
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)
|
public async Task DeleteAsync(Guid id)
|
||||||
{
|
{
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
@ -352,6 +330,81 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
|||||||
return await GetPeoplePoliciesByGrantedProjectIdAsync(peopleAccessPolicies.Id, userId);
|
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,
|
private static async Task UpsertPeoplePoliciesAsync(DatabaseContext dbContext,
|
||||||
List<BaseAccessPolicy> policies, IReadOnlyCollection<AccessPolicy> userPolicyEntities,
|
List<BaseAccessPolicy> policies, IReadOnlyCollection<AccessPolicy> userPolicyEntities,
|
||||||
IReadOnlyCollection<AccessPolicy> groupPolicyEntities)
|
IReadOnlyCollection<AccessPolicy> groupPolicyEntities)
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||||
using Bit.Core.SecretsManager.Models.Data;
|
using Bit.Core.SecretsManager.Models.Data;
|
||||||
|
using Bit.Core.SecretsManager.Queries.AccessPolicies.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||||
@ -38,26 +35,16 @@ public class ProjectPeopleAccessPoliciesAuthorizationHandlerTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void SetupOrganizationUsers(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
private static void SetupOrganizationUsers(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||||
ProjectPeopleAccessPolicies resource)
|
ProjectPeopleAccessPolicies resource) =>
|
||||||
{
|
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||||
var orgUsers = resource.UserAccessPolicies.Select(userPolicy =>
|
.OrgUsersInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||||
new OrganizationUser
|
.Returns(true);
|
||||||
{
|
|
||||||
OrganizationId = resource.OrganizationId,
|
|
||||||
Id = userPolicy.OrganizationUserId!.Value
|
|
||||||
}).ToList();
|
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(default)
|
|
||||||
.ReturnsForAnyArgs(orgUsers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetupGroups(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
private static void SetupGroups(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||||
ProjectPeopleAccessPolicies resource)
|
ProjectPeopleAccessPolicies resource) =>
|
||||||
{
|
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||||
var groups = resource.GroupAccessPolicies.Select(groupPolicy =>
|
.GroupsInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||||
new Group { OrganizationId = resource.OrganizationId, Id = groupPolicy.GroupId!.Value }).ToList();
|
.Returns(true);
|
||||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(default)
|
|
||||||
.ReturnsForAnyArgs(groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PeopleAccessPoliciesOperations_OnlyPublicStatic()
|
public void PeopleAccessPoliciesOperations_OnlyPublicStatic()
|
||||||
@ -129,37 +116,10 @@ public class ProjectPeopleAccessPoliciesAuthorizationHandlerTests
|
|||||||
{
|
{
|
||||||
var requirement = ProjectPeopleAccessPoliciesOperations.Replace;
|
var requirement = ProjectPeopleAccessPoliciesOperations.Replace;
|
||||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||||
var orgUsers = resource.UserAccessPolicies.Select(userPolicy =>
|
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||||
new OrganizationUser { OrganizationId = Guid.NewGuid(), Id = userPolicy.OrganizationUserId!.Value })
|
.OrgUsersInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId)
|
||||||
.ToList();
|
.Returns(false);
|
||||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(default)
|
|
||||||
.ReturnsForAnyArgs(orgUsers);
|
|
||||||
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_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 },
|
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||||
claimsPrincipal, resource);
|
claimsPrincipal, resource);
|
||||||
|
|
||||||
@ -179,35 +139,8 @@ public class ProjectPeopleAccessPoliciesAuthorizationHandlerTests
|
|||||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||||
SetupOrganizationUsers(sutProvider, resource);
|
SetupOrganizationUsers(sutProvider, resource);
|
||||||
|
|
||||||
var groups = resource.GroupAccessPolicies.Select(groupPolicy =>
|
sutProvider.GetDependency<ISameOrganizationQuery>()
|
||||||
new Group { OrganizationId = Guid.NewGuid(), Id = groupPolicy.GroupId!.Value }).ToList();
|
.GroupsInTheSameOrgAsync(Arg.Any<List<Guid>>(), resource.OrganizationId).Returns(false);
|
||||||
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);
|
|
||||||
|
|
||||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||||
claimsPrincipal, resource);
|
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);
|
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")]
|
[HttpPost("/service-accounts/{id}/granted-policies")]
|
||||||
public async Task<ListResponseModel<ServiceAccountProjectAccessPolicyResponseModel>>
|
public async Task<ListResponseModel<ServiceAccountProjectAccessPolicyResponseModel>>
|
||||||
CreateServiceAccountGrantedPoliciesAsync([FromRoute] Guid id,
|
CreateServiceAccountGrantedPoliciesAsync([FromRoute] Guid id,
|
||||||
@ -308,6 +268,40 @@ public class AccessPoliciesController : Controller
|
|||||||
return new ProjectPeopleAccessPoliciesResponseModel(results, userId);
|
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)
|
private async Task<(AccessClientType AccessClientType, Guid UserId)> CheckUserHasWriteAccessToProjectAsync(Project project)
|
||||||
{
|
{
|
||||||
@ -345,6 +339,7 @@ public class AccessPoliciesController : Controller
|
|||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (accessClient, userId);
|
return (accessClient, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,4 +61,39 @@ public class PeopleAccessPoliciesRequestModel
|
|||||||
GroupAccessPolicies = groupAccessPolicies
|
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)
|
public UserServiceAccountAccessPolicyResponseModel(UserServiceAccountAccessPolicy accessPolicy)
|
||||||
: base(accessPolicy, _objectName)
|
: base(accessPolicy, _objectName)
|
||||||
{
|
{
|
||||||
OrganizationUserId = accessPolicy.OrganizationUserId;
|
SetProperties(accessPolicy);
|
||||||
GrantedServiceAccountId = accessPolicy.GrantedServiceAccountId;
|
}
|
||||||
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
|
|
||||||
UserId = accessPolicy.User?.Id;
|
public UserServiceAccountAccessPolicyResponseModel(UserServiceAccountAccessPolicy accessPolicy, Guid userId)
|
||||||
|
: base(accessPolicy, _objectName)
|
||||||
|
{
|
||||||
|
SetProperties(accessPolicy);
|
||||||
|
CurrentUser = accessPolicy.User?.Id == userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName)
|
public UserServiceAccountAccessPolicyResponseModel() : base(new UserServiceAccountAccessPolicy(), _objectName)
|
||||||
@ -83,6 +87,15 @@ public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyRespo
|
|||||||
public string? OrganizationUserName { get; set; }
|
public string? OrganizationUserName { get; set; }
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
public Guid? GrantedServiceAccountId { 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
|
public class GroupProjectAccessPolicyResponseModel : BaseAccessPolicyResponseModel
|
||||||
|
@ -3,11 +3,11 @@ using Bit.Core.SecretsManager.Entities;
|
|||||||
|
|
||||||
namespace Bit.Api.SecretsManager.Models.Response;
|
namespace Bit.Api.SecretsManager.Models.Response;
|
||||||
|
|
||||||
public class ServiceAccountAccessPoliciesResponseModel : ResponseModel
|
public class ServiceAccountPeopleAccessPoliciesResponseModel : ResponseModel
|
||||||
{
|
{
|
||||||
private const string _objectName = "serviceAccountAccessPolicies";
|
private const string _objectName = "serviceAccountAccessPolicies";
|
||||||
|
|
||||||
public ServiceAccountAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies)
|
public ServiceAccountPeopleAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies, Guid userId)
|
||||||
: base(_objectName)
|
: base(_objectName)
|
||||||
{
|
{
|
||||||
if (baseAccessPolicies == null)
|
if (baseAccessPolicies == null)
|
||||||
@ -20,7 +20,7 @@ public class ServiceAccountAccessPoliciesResponseModel : ResponseModel
|
|||||||
switch (baseAccessPolicy)
|
switch (baseAccessPolicy)
|
||||||
{
|
{
|
||||||
case UserServiceAccountAccessPolicy accessPolicy:
|
case UserServiceAccountAccessPolicy accessPolicy:
|
||||||
UserAccessPolicies.Add(new UserServiceAccountAccessPolicyResponseModel(accessPolicy));
|
UserAccessPolicies.Add(new UserServiceAccountAccessPolicyResponseModel(accessPolicy, userId));
|
||||||
break;
|
break;
|
||||||
case GroupServiceAccountAccessPolicy accessPolicy:
|
case GroupServiceAccountAccessPolicy accessPolicy:
|
||||||
GroupAccessPolicies.Add(new GroupServiceAccountAccessPolicyResponseModel(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<bool> AccessPolicyExists(BaseAccessPolicy baseAccessPolicy);
|
||||||
Task<BaseAccessPolicy?> GetByIdAsync(Guid id);
|
Task<BaseAccessPolicy?> GetByIdAsync(Guid id);
|
||||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id, Guid userId);
|
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedProjectIdAsync(Guid id, Guid userId);
|
||||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByGrantedServiceAccountIdAsync(Guid id, Guid userId);
|
|
||||||
Task<IEnumerable<BaseAccessPolicy>> GetManyByServiceAccountIdAsync(Guid id, Guid userId,
|
Task<IEnumerable<BaseAccessPolicy>> GetManyByServiceAccountIdAsync(Guid id, Guid userId,
|
||||||
AccessClientType accessType);
|
AccessClientType accessType);
|
||||||
Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy);
|
Task ReplaceAsync(BaseAccessPolicy baseAccessPolicy);
|
||||||
@ -19,4 +18,6 @@ public interface IAccessPolicyRepository
|
|||||||
Task<IEnumerable<BaseAccessPolicy>> GetPeoplePoliciesByGrantedProjectIdAsync(Guid id, Guid userId);
|
Task<IEnumerable<BaseAccessPolicy>> GetPeoplePoliciesByGrantedProjectIdAsync(Guid id, Guid userId);
|
||||||
Task<IEnumerable<BaseAccessPolicy>> ReplaceProjectPeopleAsync(ProjectPeopleAccessPolicies peopleAccessPolicies, Guid userId);
|
Task<IEnumerable<BaseAccessPolicy>> ReplaceProjectPeopleAsync(ProjectPeopleAccessPolicies peopleAccessPolicies, Guid userId);
|
||||||
Task<PeopleGrantees> GetPeopleGranteesAsync(Guid organizationId, Guid currentUserId);
|
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);
|
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]
|
[Theory]
|
||||||
[InlineData(false, false, false)]
|
[InlineData(false, false, false)]
|
||||||
[InlineData(false, false, true)]
|
[InlineData(false, false, true)]
|
||||||
@ -1066,9 +844,13 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
|||||||
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
||||||
await LoginAsync(_email);
|
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);
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1230,6 +1012,183 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
|||||||
Assert.Equal(result.UserAccessPolicies.First().Id, createdAccessPolicy.Id);
|
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)
|
private async Task<RequestSetupData> SetupAccessPolicyRequest(Guid organizationId)
|
||||||
{
|
{
|
||||||
var project = await _projectRepository.CreateAsync(new Project
|
var project = await _projectRepository.CreateAsync(new Project
|
||||||
@ -1293,6 +1252,38 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
|||||||
return (project, organizationUser);
|
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(
|
private async Task<(Project project, PeopleAccessPoliciesRequestModel request)> SetupProjectPeopleRequestAsync(
|
||||||
PermissionType permissionType, OrganizationUser organizationUser)
|
PermissionType permissionType, OrganizationUser organizationUser)
|
||||||
{
|
{
|
||||||
@ -1307,6 +1298,20 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
|||||||
return (project, request);
|
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,
|
private async Task<(Guid ProjectId, Guid ServiceAccountId)> CreateProjectAndServiceAccountAsync(Guid organizationId,
|
||||||
bool misMatchOrganization = false)
|
bool misMatchOrganization = false)
|
||||||
{
|
{
|
||||||
|
@ -248,7 +248,7 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
|
|||||||
AssertHelper.AssertRecent(createdServiceAccount.CreationDate);
|
AssertHelper.AssertRecent(createdServiceAccount.CreationDate);
|
||||||
|
|
||||||
// Check permissions have been bootstrapped.
|
// Check permissions have been bootstrapped.
|
||||||
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(createdServiceAccount.Id, currentUserId);
|
var accessPolicies = await _accessPolicyRepository.GetPeoplePoliciesByGrantedServiceAccountIdAsync(createdServiceAccount.Id, currentUserId);
|
||||||
Assert.NotNull(accessPolicies);
|
Assert.NotNull(accessPolicies);
|
||||||
var ap = (UserServiceAccountAccessPolicy)accessPolicies.First();
|
var ap = (UserServiceAccountAccessPolicy)accessPolicies.First();
|
||||||
Assert.Equal(createdServiceAccount.Id, ap.GrantedServiceAccountId);
|
Assert.Equal(createdServiceAccount.Id, ap.GrantedServiceAccountId);
|
||||||
|
@ -51,6 +51,23 @@ public class AccessPoliciesControllerTests
|
|||||||
return request;
|
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)
|
private static void SetupAdmin(SutProvider<AccessPoliciesController> sutProvider, Guid organizationId)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(default).ReturnsForAnyArgs(true);
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(default).ReturnsForAnyArgs(true);
|
||||||
@ -103,12 +120,14 @@ public class AccessPoliciesControllerTests
|
|||||||
{
|
{
|
||||||
case PermissionType.RunAsAdmin:
|
case PermissionType.RunAsAdmin:
|
||||||
SetupAdmin(sutProvider, data.OrganizationId);
|
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));
|
.Returns((true, true));
|
||||||
break;
|
break;
|
||||||
case PermissionType.RunAsUserWithPermission:
|
case PermissionType.RunAsUserWithPermission:
|
||||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
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));
|
.Returns((true, true));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -156,12 +175,14 @@ public class AccessPoliciesControllerTests
|
|||||||
{
|
{
|
||||||
case PermissionType.RunAsAdmin:
|
case PermissionType.RunAsAdmin:
|
||||||
SetupAdmin(sutProvider, data.OrganizationId);
|
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));
|
.Returns((true, true));
|
||||||
break;
|
break;
|
||||||
case PermissionType.RunAsUserWithPermission:
|
case PermissionType.RunAsUserWithPermission:
|
||||||
SetupUserWithPermission(sutProvider, data.OrganizationId);
|
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));
|
.Returns((true, true));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -201,114 +222,6 @@ public class AccessPoliciesControllerTests
|
|||||||
.GetManyByGrantedProjectIdAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
|
.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]
|
[Theory]
|
||||||
[BitAutoData(PermissionType.RunAsAdmin)]
|
[BitAutoData(PermissionType.RunAsAdmin)]
|
||||||
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
[BitAutoData(PermissionType.RunAsUserWithPermission)]
|
||||||
@ -419,7 +332,7 @@ public class AccessPoliciesControllerTests
|
|||||||
UserProjectAccessPolicy data,
|
UserProjectAccessPolicy data,
|
||||||
AccessPoliciesCreateRequest request)
|
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 };
|
request.UserAccessPolicyRequests = new[] { dup, dup };
|
||||||
mockProject.Id = id;
|
mockProject.Id = id;
|
||||||
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(default).ReturnsForAnyArgs(mockProject);
|
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(default).ReturnsForAnyArgs(mockProject);
|
||||||
@ -451,6 +364,7 @@ public class AccessPoliciesControllerTests
|
|||||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
||||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
|
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
|
||||||
}
|
}
|
||||||
|
|
||||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>().CreateManyAsync(default)
|
sutProvider.GetDependency<ICreateAccessPoliciesCommand>().CreateManyAsync(default)
|
||||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||||
|
|
||||||
@ -479,6 +393,7 @@ public class AccessPoliciesControllerTests
|
|||||||
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), policy,
|
||||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
|
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
|
||||||
}
|
}
|
||||||
|
|
||||||
sutProvider.GetDependency<ICreateAccessPoliciesCommand>().CreateManyAsync(default)
|
sutProvider.GetDependency<ICreateAccessPoliciesCommand>().CreateManyAsync(default)
|
||||||
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
.ReturnsForAnyArgs(new List<BaseAccessPolicy> { data });
|
||||||
|
|
||||||
@ -488,124 +403,6 @@ public class AccessPoliciesControllerTests
|
|||||||
.CreateManyAsync(Arg.Any<List<BaseAccessPolicy>>());
|
.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]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async void CreateServiceAccountGrantedPolicies_RequestMoreThanMax_Throws(
|
public async void CreateServiceAccountGrantedPolicies_RequestMoreThanMax_Throws(
|
||||||
@ -652,7 +449,7 @@ public class AccessPoliciesControllerTests
|
|||||||
ServiceAccountProjectAccessPolicy data,
|
ServiceAccountProjectAccessPolicy data,
|
||||||
List<GrantedAccessPolicyRequest> request)
|
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);
|
||||||
request.Add(dup);
|
request.Add(dup);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetByIdAsync(default).ReturnsForAnyArgs(serviceAccount);
|
||||||
@ -1173,4 +970,199 @@ public class AccessPoliciesControllerTests
|
|||||||
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(1)
|
||||||
.ReplaceProjectPeopleAsync(Arg.Any<ProjectPeopleAccessPolicies>(), Arg.Any<Guid>());
|
.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…
x
Reference in New Issue
Block a user