1
0
mirror of https://github.com/bitwarden/server synced 2025-12-28 22:23:30 +00: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,64 @@
using Bit.Core.Exceptions;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Models.Data;
namespace Bit.Api.SecretsManager.Models.Request;
public class PeopleAccessPoliciesRequestModel
{
public IEnumerable<AccessPolicyRequest> UserAccessPolicyRequests { get; set; }
public IEnumerable<AccessPolicyRequest> GroupAccessPolicyRequests { get; set; }
private static void CheckForDistinctAccessPolicies(IReadOnlyCollection<BaseAccessPolicy> accessPolicies)
{
var distinctAccessPolicies = accessPolicies.DistinctBy(baseAccessPolicy =>
{
return baseAccessPolicy switch
{
UserProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId, ap.GrantedProjectId),
GroupProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedProjectId),
ServiceAccountProjectAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.ServiceAccountId,
ap.GrantedProjectId),
UserServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.OrganizationUserId,
ap.GrantedServiceAccountId),
GroupServiceAccountAccessPolicy ap => new Tuple<Guid?, Guid?>(ap.GroupId, ap.GrantedServiceAccountId),
_ => throw new ArgumentException("Unsupported access policy type provided.", nameof(baseAccessPolicy))
};
}).ToList();
if (accessPolicies.Count != distinctAccessPolicies.Count)
{
throw new BadRequestException("Resources must be unique");
}
}
public ProjectPeopleAccessPolicies ToProjectPeopleAccessPolicies(Guid grantedProjectId, Guid organizationId)
{
var userAccessPolicies = UserAccessPolicyRequests?
.Select(x => x.ToUserProjectAccessPolicy(grantedProjectId, organizationId)).ToList();
var groupAccessPolicies = GroupAccessPolicyRequests?
.Select(x => x.ToGroupProjectAccessPolicy(grantedProjectId, organizationId)).ToList();
var policies = new List<BaseAccessPolicy>();
if (userAccessPolicies != null)
{
policies.AddRange(userAccessPolicies);
}
if (groupAccessPolicies != null)
{
policies.AddRange(groupAccessPolicies);
}
CheckForDistinctAccessPolicies(policies);
return new ProjectPeopleAccessPolicies
{
Id = grantedProjectId,
OrganizationId = organizationId,
UserAccessPolicies = userAccessPolicies,
GroupAccessPolicies = groupAccessPolicies
};
}
}

View File

@@ -34,10 +34,13 @@ public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMode
public UserProjectAccessPolicyResponseModel(UserProjectAccessPolicy accessPolicy) : base(accessPolicy, _objectName)
{
OrganizationUserId = accessPolicy.OrganizationUserId;
GrantedProjectId = accessPolicy.GrantedProjectId;
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
UserId = accessPolicy.User?.Id;
SetProperties(accessPolicy);
}
public UserProjectAccessPolicyResponseModel(UserProjectAccessPolicy accessPolicy, Guid currentUserId) : base(accessPolicy, _objectName)
{
CurrentUser = currentUserId == accessPolicy.User?.Id;
SetProperties(accessPolicy);
}
public UserProjectAccessPolicyResponseModel() : base(new UserProjectAccessPolicy(), _objectName)
@@ -48,6 +51,15 @@ public class UserProjectAccessPolicyResponseModel : BaseAccessPolicyResponseMode
public string? OrganizationUserName { get; set; }
public Guid? UserId { get; set; }
public Guid? GrantedProjectId { get; set; }
public bool? CurrentUser { get; set; }
private void SetProperties(UserProjectAccessPolicy accessPolicy)
{
OrganizationUserId = accessPolicy.OrganizationUserId;
GrantedProjectId = accessPolicy.GrantedProjectId;
OrganizationUserName = GetUserDisplayName(accessPolicy.User);
UserId = accessPolicy.User?.Id;
}
}
public class UserServiceAccountAccessPolicyResponseModel : BaseAccessPolicyResponseModel

View File

@@ -1,7 +1,6 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Models.Api;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.Api;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Models.Data;
namespace Bit.Api.SecretsManager.Models.Response;
@@ -9,31 +8,33 @@ public class PotentialGranteeResponseModel : ResponseModel
{
private const string _objectName = "potentialGrantee";
public PotentialGranteeResponseModel(Group group)
public PotentialGranteeResponseModel(GroupGrantee grantee)
: base(_objectName)
{
if (group == null)
if (grantee == null)
{
throw new ArgumentNullException(nameof(group));
throw new ArgumentNullException(nameof(grantee));
}
Id = group.Id;
Name = group.Name;
Type = "group";
Id = grantee.GroupId;
Name = grantee.Name;
CurrentUserInGroup = grantee.CurrentUserInGroup;
}
public PotentialGranteeResponseModel(OrganizationUserUserDetails user)
public PotentialGranteeResponseModel(UserGrantee grantee)
: base(_objectName)
{
if (user == null)
if (grantee == null)
{
throw new ArgumentNullException(nameof(user));
throw new ArgumentNullException(nameof(grantee));
}
Id = user.Id;
Name = user.Name;
Email = user.Email;
Type = "user";
Id = grantee.OrganizationUserId;
Name = grantee.Name;
Email = grantee.Email;
CurrentUser = grantee.CurrentUser;
}
public PotentialGranteeResponseModel(ServiceAccount serviceAccount)
@@ -67,9 +68,9 @@ public class PotentialGranteeResponseModel : ResponseModel
}
public Guid Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Email { get; set; }
public bool CurrentUserInGroup { get; set; }
public bool CurrentUser { get; set; }
}

View File

@@ -0,0 +1,34 @@
using Bit.Core.Models.Api;
using Bit.Core.SecretsManager.Entities;
namespace Bit.Api.SecretsManager.Models.Response;
public class ProjectPeopleAccessPoliciesResponseModel : ResponseModel
{
private const string _objectName = "projectPeopleAccessPolicies";
public ProjectPeopleAccessPoliciesResponseModel(IEnumerable<BaseAccessPolicy> baseAccessPolicies, Guid userId)
: base(_objectName)
{
foreach (var baseAccessPolicy in baseAccessPolicies)
{
switch (baseAccessPolicy)
{
case UserProjectAccessPolicy accessPolicy:
UserAccessPolicies.Add(new UserProjectAccessPolicyResponseModel(accessPolicy, userId));
break;
case GroupProjectAccessPolicy accessPolicy:
GroupAccessPolicies.Add(new GroupProjectAccessPolicyResponseModel(accessPolicy));
break;
}
}
}
public ProjectPeopleAccessPoliciesResponseModel() : base(_objectName)
{
}
public List<UserProjectAccessPolicyResponseModel> UserAccessPolicies { get; set; } = new();
public List<GroupProjectAccessPolicyResponseModel> GroupAccessPolicies { get; set; } = new();
}