mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 08:32:50 -05:00
[SM-919] Add project people access policy management endpoints (#3285)
* Expose access policy discriminators * Add people policy model and auth handler * Add unit tests for authz handler * Add people policies support in repo * Add new endpoints and request/response models * Update tests
This commit is contained in:
@ -0,0 +1,96 @@
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
|
||||
public class
|
||||
ProjectPeopleAccessPoliciesAuthorizationHandler : AuthorizationHandler<ProjectPeopleAccessPoliciesOperationRequirement,
|
||||
ProjectPeopleAccessPolicies>
|
||||
{
|
||||
private readonly IAccessClientQuery _accessClientQuery;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly IGroupRepository _groupRepository;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
|
||||
public ProjectPeopleAccessPoliciesAuthorizationHandler(ICurrentContext currentContext,
|
||||
IAccessClientQuery accessClientQuery,
|
||||
IGroupRepository groupRepository,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProjectRepository projectRepository)
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_accessClientQuery = accessClientQuery;
|
||||
_groupRepository = groupRepository;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_projectRepository = projectRepository;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context,
|
||||
ProjectPeopleAccessPoliciesOperationRequirement requirement,
|
||||
ProjectPeopleAccessPolicies 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 == ProjectPeopleAccessPoliciesOperations.Replace:
|
||||
await CanReplaceProjectPeopleAsync(context, requirement, resource, accessClient, userId);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unsupported operation requirement type provided.",
|
||||
nameof(requirement));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CanReplaceProjectPeopleAsync(AuthorizationHandlerContext context,
|
||||
ProjectPeopleAccessPoliciesOperationRequirement requirement, ProjectPeopleAccessPolicies resource,
|
||||
AccessClientType accessClient, Guid userId)
|
||||
{
|
||||
var access = await _projectRepository.AccessToProjectAsync(resource.Id, userId, accessClient);
|
||||
if (access.Write)
|
||||
{
|
||||
if (resource.UserAccessPolicies != null && resource.UserAccessPolicies.Any())
|
||||
{
|
||||
var orgUserIds = resource.UserAccessPolicies.Select(ap => ap.OrganizationUserId!.Value).ToList();
|
||||
var users = await _organizationUserRepository.GetManyAsync(orgUserIds);
|
||||
if (users.Any(user => user.OrganizationId != resource.OrganizationId) ||
|
||||
users.Count != orgUserIds.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.GroupAccessPolicies != null && resource.GroupAccessPolicies.Any())
|
||||
{
|
||||
var groupIds = resource.GroupAccessPolicies.Select(ap => ap.GroupId!.Value).ToList();
|
||||
var groups = await _groupRepository.GetManyByManyIds(groupIds);
|
||||
if (groups.Any(group => group.OrganizationId != resource.OrganizationId) ||
|
||||
groups.Count != groupIds.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ public static class SecretsManagerCollectionExtensions
|
||||
services.AddScoped<IAuthorizationHandler, SecretAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, ServiceAccountAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, AccessPolicyAuthorizationHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, ProjectPeopleAccessPoliciesAuthorizationHandler>();
|
||||
services.AddScoped<IAccessClientQuery, AccessClientQuery>();
|
||||
services.AddScoped<IMaxProjectsQuery, MaxProjectsQuery>();
|
||||
services.AddScoped<IServiceAccountSecretsDetailsQuery, ServiceAccountSecretsDetailsQuery>();
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System.Linq.Expressions;
|
||||
using AutoMapper;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Bit.Infrastructure.EntityFramework.SecretsManager.Discriminators;
|
||||
using Bit.Infrastructure.EntityFramework.SecretsManager.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@ -238,6 +240,153 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
||||
return entities.Select(MapToCore);
|
||||
}
|
||||
|
||||
public async Task<PeopleGrantees> GetPeopleGranteesAsync(Guid organizationId, Guid currentUserId)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var userGrantees = await dbContext.OrganizationUsers
|
||||
.Where(ou =>
|
||||
ou.OrganizationId == organizationId &&
|
||||
ou.AccessSecretsManager &&
|
||||
ou.Status == OrganizationUserStatusType.Confirmed)
|
||||
.Include(ou => ou.User)
|
||||
.Select(ou => new
|
||||
UserGrantee
|
||||
{
|
||||
OrganizationUserId = ou.Id,
|
||||
Name = ou.User.Name,
|
||||
Email = ou.User.Email,
|
||||
CurrentUser = ou.UserId == currentUserId
|
||||
}).ToListAsync();
|
||||
|
||||
var groupGrantees = await dbContext.Groups
|
||||
.Where(g => g.OrganizationId == organizationId)
|
||||
.Include(g => g.GroupUsers)
|
||||
.Select(g => new GroupGrantee
|
||||
{
|
||||
GroupId = g.Id,
|
||||
Name = g.Name,
|
||||
CurrentUserInGroup = g.GroupUsers.Any(gu =>
|
||||
gu.OrganizationUser.User.Id == currentUserId)
|
||||
}).ToListAsync();
|
||||
|
||||
return new PeopleGrantees { UserGrantees = userGrantees, GroupGrantees = groupGrantees };
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Core.SecretsManager.Entities.BaseAccessPolicy>>
|
||||
GetPeoplePoliciesByGrantedProjectIdAsync(Guid id, Guid userId)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
|
||||
var entities = await dbContext.AccessPolicies.Where(ap =>
|
||||
ap.Discriminator != AccessPolicyDiscriminator.ServiceAccountProject &&
|
||||
(((UserProjectAccessPolicy)ap).GrantedProjectId == id ||
|
||||
((GroupProjectAccessPolicy)ap).GrantedProjectId == id))
|
||||
.Include(ap => ((UserProjectAccessPolicy)ap).OrganizationUser.User)
|
||||
.Include(ap => ((GroupProjectAccessPolicy)ap).Group)
|
||||
.Select(ap => new
|
||||
{
|
||||
ap,
|
||||
CurrentUserInGroup = ap is GroupProjectAccessPolicy &&
|
||||
((GroupProjectAccessPolicy)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>> ReplaceProjectPeopleAsync(
|
||||
ProjectPeopleAccessPolicies peopleAccessPolicies, Guid userId)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var dbContext = GetDatabaseContext(scope);
|
||||
var peoplePolicyEntities = await dbContext.AccessPolicies.Where(ap =>
|
||||
ap.Discriminator != AccessPolicyDiscriminator.ServiceAccountProject &&
|
||||
(((UserProjectAccessPolicy)ap).GrantedProjectId == peopleAccessPolicies.Id ||
|
||||
((GroupProjectAccessPolicy)ap).GrantedProjectId == peopleAccessPolicies.Id)).ToListAsync();
|
||||
|
||||
var userPolicyEntities =
|
||||
peoplePolicyEntities.Where(ap => ap.GetType() == typeof(UserProjectAccessPolicy)).ToList();
|
||||
var groupPolicyEntities =
|
||||
peoplePolicyEntities.Where(ap => ap.GetType() == typeof(GroupProjectAccessPolicy)).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.UserProjectAccessPolicy)ap).OrganizationUserId !=
|
||||
((UserProjectAccessPolicy)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.GroupProjectAccessPolicy)ap).GroupId !=
|
||||
((GroupProjectAccessPolicy)entity).GroupId)))
|
||||
{
|
||||
dbContext.Remove(groupPolicyEntity);
|
||||
}
|
||||
}
|
||||
|
||||
await UpsertPeoplePoliciesAsync(dbContext,
|
||||
peopleAccessPolicies.ToBaseAccessPolicies().Select(MapToEntity).ToList(), userPolicyEntities,
|
||||
groupPolicyEntities);
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
return await GetPeoplePoliciesByGrantedProjectIdAsync(peopleAccessPolicies.Id, userId);
|
||||
}
|
||||
|
||||
private static async Task UpsertPeoplePoliciesAsync(DatabaseContext dbContext,
|
||||
List<BaseAccessPolicy> policies, IReadOnlyCollection<AccessPolicy> userPolicyEntities,
|
||||
IReadOnlyCollection<AccessPolicy> groupPolicyEntities)
|
||||
{
|
||||
var currentDate = DateTime.UtcNow;
|
||||
foreach (var updatedEntity in policies)
|
||||
{
|
||||
var currentEntity = updatedEntity switch
|
||||
{
|
||||
UserProjectAccessPolicy ap => userPolicyEntities.FirstOrDefault(e =>
|
||||
((UserProjectAccessPolicy)e).OrganizationUserId == ap.OrganizationUserId),
|
||||
GroupProjectAccessPolicy ap => groupPolicyEntities.FirstOrDefault(e =>
|
||||
((GroupProjectAccessPolicy)e).GroupId == ap.GroupId),
|
||||
UserServiceAccountAccessPolicy ap => userPolicyEntities.FirstOrDefault(e =>
|
||||
((UserServiceAccountAccessPolicy)e).OrganizationUserId == ap.OrganizationUserId),
|
||||
GroupServiceAccountAccessPolicy ap => groupPolicyEntities.FirstOrDefault(e =>
|
||||
((GroupServiceAccountAccessPolicy)e).GroupId == ap.GroupId),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (currentEntity != null)
|
||||
{
|
||||
dbContext.AccessPolicies.Attach(currentEntity);
|
||||
currentEntity.Read = updatedEntity.Read;
|
||||
currentEntity.Write = updatedEntity.Write;
|
||||
currentEntity.RevisionDate = currentDate;
|
||||
}
|
||||
else
|
||||
{
|
||||
updatedEntity.SetNewId();
|
||||
await dbContext.AddAsync(updatedEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Core.SecretsManager.Entities.BaseAccessPolicy MapToCore(
|
||||
BaseAccessPolicy baseAccessPolicyEntity) =>
|
||||
baseAccessPolicyEntity switch
|
||||
@ -250,9 +399,27 @@ public class AccessPolicyRepository : BaseEntityFrameworkRepository, IAccessPoli
|
||||
Mapper.Map<Core.SecretsManager.Entities.UserServiceAccountAccessPolicy>(ap),
|
||||
GroupServiceAccountAccessPolicy ap => Mapper
|
||||
.Map<Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy>(ap),
|
||||
_ => throw new ArgumentException("Unsupported access policy type"),
|
||||
_ => throw new ArgumentException("Unsupported access policy type")
|
||||
};
|
||||
|
||||
private BaseAccessPolicy MapToEntity(Core.SecretsManager.Entities.BaseAccessPolicy baseAccessPolicy)
|
||||
{
|
||||
return baseAccessPolicy switch
|
||||
{
|
||||
Core.SecretsManager.Entities.UserProjectAccessPolicy accessPolicy => Mapper.Map<UserProjectAccessPolicy>(
|
||||
accessPolicy),
|
||||
Core.SecretsManager.Entities.UserServiceAccountAccessPolicy accessPolicy => Mapper
|
||||
.Map<UserServiceAccountAccessPolicy>(accessPolicy),
|
||||
Core.SecretsManager.Entities.GroupProjectAccessPolicy accessPolicy => Mapper.Map<GroupProjectAccessPolicy>(
|
||||
accessPolicy),
|
||||
Core.SecretsManager.Entities.GroupServiceAccountAccessPolicy accessPolicy => Mapper
|
||||
.Map<GroupServiceAccountAccessPolicy>(accessPolicy),
|
||||
Core.SecretsManager.Entities.ServiceAccountProjectAccessPolicy accessPolicy => Mapper
|
||||
.Map<ServiceAccountProjectAccessPolicy>(accessPolicy),
|
||||
_ => throw new ArgumentException("Unsupported access policy type")
|
||||
};
|
||||
}
|
||||
|
||||
private Core.SecretsManager.Entities.BaseAccessPolicy MapToCore(
|
||||
BaseAccessPolicy baseAccessPolicyEntity, bool currentUserInGroup)
|
||||
{
|
||||
|
@ -0,0 +1,246 @@
|
||||
using System.Reflection;
|
||||
using System.Security.Claims;
|
||||
using Bit.Commercial.Core.SecretsManager.AuthorizationHandlers.AccessPolicies;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Test.SecretsManager.AutoFixture.ProjectsFixture;
|
||||
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]
|
||||
[ProjectCustomize]
|
||||
public class ProjectPeopleAccessPoliciesAuthorizationHandlerTests
|
||||
{
|
||||
private static void SetupUserPermission(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
AccessClientType accessClientType, ProjectPeopleAccessPolicies 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<IProjectRepository>().AccessToProjectAsync(resource.Id, userId, accessClientType)
|
||||
.Returns((read, write));
|
||||
}
|
||||
|
||||
private static void SetupOrganizationUsers(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ProjectPeopleAccessPolicies resource)
|
||||
{
|
||||
var orgUsers = resource.UserAccessPolicies.Select(userPolicy =>
|
||||
new OrganizationUser
|
||||
{
|
||||
OrganizationId = resource.OrganizationId,
|
||||
Id = userPolicy.OrganizationUserId!.Value
|
||||
}).ToList();
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetManyAsync(default)
|
||||
.ReturnsForAnyArgs(orgUsers);
|
||||
}
|
||||
|
||||
private static void SetupGroups(SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider,
|
||||
ProjectPeopleAccessPolicies resource)
|
||||
{
|
||||
var groups = resource.GroupAccessPolicies.Select(groupPolicy =>
|
||||
new Group { OrganizationId = resource.OrganizationId, Id = groupPolicy.GroupId!.Value }).ToList();
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(default)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PeopleAccessPoliciesOperations_OnlyPublicStatic()
|
||||
{
|
||||
var publicStaticFields =
|
||||
typeof(ProjectPeopleAccessPoliciesOperations).GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||
var allFields = typeof(ProjectPeopleAccessPoliciesOperations).GetFields();
|
||||
Assert.Equal(publicStaticFields.Length, allFields.Length);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task Handler_UnsupportedProjectPeopleAccessPoliciesOperationRequirement_Throws(
|
||||
SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider, ProjectPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
var requirement = new ProjectPeopleAccessPoliciesOperationRequirement();
|
||||
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<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider, ProjectPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
var requirement = new ProjectPeopleAccessPoliciesOperationRequirement();
|
||||
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<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider, ProjectPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal)
|
||||
{
|
||||
var requirement = new ProjectPeopleAccessPoliciesOperationRequirement();
|
||||
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 ReplaceProjectPeople_UserNotInOrg_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 = Guid.NewGuid(), Id = userPolicy.OrganizationUserId!.Value })
|
||||
.ToList();
|
||||
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 },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.User)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck)]
|
||||
public async Task ReplaceProjectPeople_GroupNotInOrg_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 = Guid.NewGuid(), Id = groupPolicy.GroupId!.Value }).ToList();
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(default)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
|
||||
var authzContext = new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement },
|
||||
claimsPrincipal, resource);
|
||||
|
||||
await sutProvider.Sut.HandleAsync(authzContext);
|
||||
|
||||
Assert.False(authzContext.HasSucceeded);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(AccessClientType.User)]
|
||||
[BitAutoData(AccessClientType.NoAccessCheck)]
|
||||
public async Task ReplaceProjectPeople_GroupCountMismatch_DoesNotSucceed(AccessClientType accessClient,
|
||||
SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider, ProjectPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal, Guid userId)
|
||||
{
|
||||
var requirement = ProjectPeopleAccessPoliciesOperations.Replace;
|
||||
SetupUserPermission(sutProvider, accessClient, resource, userId);
|
||||
SetupOrganizationUsers(sutProvider, resource);
|
||||
|
||||
var groups = resource.GroupAccessPolicies.Select(groupPolicy =>
|
||||
new Group { OrganizationId = resource.OrganizationId, Id = groupPolicy.GroupId!.Value }).ToList();
|
||||
groups.RemoveAt(0);
|
||||
sutProvider.GetDependency<IGroupRepository>().GetManyByManyIds(default)
|
||||
.ReturnsForAnyArgs(groups);
|
||||
|
||||
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 ReplaceProjectPeople_AccessCheck(AccessClientType accessClient, bool read, bool write,
|
||||
bool expected,
|
||||
SutProvider<ProjectPeopleAccessPoliciesAuthorizationHandler> sutProvider, ProjectPeopleAccessPolicies resource,
|
||||
ClaimsPrincipal claimsPrincipal, Guid userId)
|
||||
{
|
||||
var requirement = ProjectPeopleAccessPoliciesOperations.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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user