1
0
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:
Thomas Avery
2023-11-08 11:42:40 -05:00
committed by GitHub
parent 35500b197d
commit 0ca65e3f9d
17 changed files with 1211 additions and 73 deletions

View File

@ -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);
}
}
}

View File

@ -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>();

View File

@ -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)
{

View File

@ -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);
}
}