1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -05:00

[SM-923] Add project service accounts access policies management endpoints (#3993)

* Add new models

* Update repositories

* Add new authz handler

* Add new query

* Add new command

* Add authz, command, and query to DI

* Add new endpoint to controller

* Add query unit tests

* Add api unit tests

* Add api integration tests
This commit is contained in:
Thomas Avery
2024-05-02 11:06:20 -05:00
committed by GitHub
parent e302ee1520
commit 7f8cea58d0
23 changed files with 1559 additions and 29 deletions

View File

@ -6,9 +6,11 @@ using Bit.Api.Test.SecretsManager.Enums;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Identity;
using Bit.Core.SecretsManager.Commands.AccessPolicies.Interfaces;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Models.Data;
using Bit.Core.SecretsManager.Models.Data.AccessPolicyUpdates;
using Bit.Core.SecretsManager.Queries.Interfaces;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services;
@ -1082,6 +1084,195 @@ public class AccessPoliciesControllerTests
.UpdateAsync(Arg.Any<ServiceAccountGrantedPoliciesUpdates>());
}
[Theory]
[BitAutoData]
public async Task GetProjectServiceAccountsAccessPoliciesAsync_ProjectDoesntExist_ThrowsNotFound(
SutProvider<AccessPoliciesController> sutProvider,
ServiceAccount data)
{
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).ReturnsNull();
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.GetProjectServiceAccountsAccessPoliciesAsync(data.Id));
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(0)
.GetProjectServiceAccountsAccessPoliciesAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetProjectServiceAccountsAccessPoliciesAsync_NoAccess_ThrowsNotFound(
SutProvider<AccessPoliciesController> sutProvider,
Project data)
{
SetupUserWithoutPermission(sutProvider, data.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).Returns(data);
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(default, default, default)
.ReturnsForAnyArgs((false, false));
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.GetProjectServiceAccountsAccessPoliciesAsync(data.Id));
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(0)
.GetProjectServiceAccountsAccessPoliciesAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetProjectServiceAccountsAccessPoliciesAsync_ClientIsServiceAccount_ThrowsNotFound(
SutProvider<AccessPoliciesController> sutProvider,
Project data)
{
SetupUserWithoutPermission(sutProvider, data.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).Returns(data);
sutProvider.GetDependency<ICurrentContext>().ClientType = ClientType.ServiceAccount;
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(default, default, default)
.ReturnsForAnyArgs((true, true));
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.GetProjectServiceAccountsAccessPoliciesAsync(data.Id));
await sutProvider.GetDependency<IAccessPolicyRepository>().Received(0)
.GetProjectServiceAccountsAccessPoliciesAsync(Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetProjectServiceAccountsAccessPoliciesAsync_HasAccessNoPolicies_ReturnsEmptyList(
SutProvider<AccessPoliciesController> sutProvider,
Project data)
{
SetupUserWithoutPermission(sutProvider, data.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).Returns(data);
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(default, default, default)
.ReturnsForAnyArgs((true, true));
sutProvider.GetDependency<IAccessPolicyRepository>()
.GetProjectServiceAccountsAccessPoliciesAsync(Arg.Any<Guid>())
.ReturnsNullForAnyArgs();
var result = await sutProvider.Sut.GetProjectServiceAccountsAccessPoliciesAsync(data.Id);
Assert.Empty(result.ServiceAccountAccessPolicies);
}
[Theory]
[BitAutoData]
public async Task GetProjectServiceAccountsAccessPoliciesAsync_HasAccess_Success(
SutProvider<AccessPoliciesController> sutProvider,
ProjectServiceAccountsAccessPolicies policies,
Project data)
{
SetupUserWithoutPermission(sutProvider, data.OrganizationId);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).Returns(data);
sutProvider.GetDependency<IProjectRepository>().AccessToProjectAsync(default, default, default)
.ReturnsForAnyArgs((true, true));
sutProvider.GetDependency<IAccessPolicyRepository>()
.GetProjectServiceAccountsAccessPoliciesAsync(Arg.Any<Guid>())
.ReturnsForAnyArgs(policies);
var result = await sutProvider.Sut.GetProjectServiceAccountsAccessPoliciesAsync(data.Id);
Assert.NotEmpty(result.ServiceAccountAccessPolicies);
Assert.Equal(policies.ServiceAccountAccessPolicies.Count(), result.ServiceAccountAccessPolicies.Count);
}
[Theory]
[BitAutoData]
public async Task PutProjectServiceAccountsAccessPoliciesAsync_ProjectDoesNotExist_Throws(
SutProvider<AccessPoliciesController> sutProvider,
Project data,
ProjectServiceAccountsAccessPoliciesRequestModel request)
{
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.PutProjectServiceAccountsAccessPoliciesAsync(data.Id, request));
await sutProvider.GetDependency<IUpdateProjectServiceAccountsAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
.UpdateAsync(Arg.Any<ProjectServiceAccountsAccessPoliciesUpdates>());
}
[Theory]
[BitAutoData]
public async Task PutProjectServiceAccountsAccessPoliciesAsync_DuplicatePolicyRequest_ThrowsBadRequestException(
SutProvider<AccessPoliciesController> sutProvider,
Project data,
ProjectServiceAccountsAccessPoliciesRequestModel request)
{
var dup = new AccessPolicyRequest { GranteeId = Guid.NewGuid(), Read = true, Write = true };
request.ServiceAccountAccessPolicyRequests = [dup, dup];
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.PutProjectServiceAccountsAccessPoliciesAsync(data.Id, request));
await sutProvider.GetDependency<IUpdateProjectServiceAccountsAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
.UpdateAsync(Arg.Any<ProjectServiceAccountsAccessPoliciesUpdates>());
}
[Theory]
[BitAutoData]
public async Task PutProjectServiceAccountsAccessPoliciesAsync_InvalidPolicyRequest_ThrowsBadRequestException(
SutProvider<AccessPoliciesController> sutProvider,
Project data,
ProjectServiceAccountsAccessPoliciesRequestModel request)
{
var policyRequest = new AccessPolicyRequest { GranteeId = Guid.NewGuid(), Read = false, Write = true };
request.ServiceAccountAccessPolicyRequests = [policyRequest];
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
await Assert.ThrowsAsync<BadRequestException>(() =>
sutProvider.Sut.PutProjectServiceAccountsAccessPoliciesAsync(data.Id, request));
await sutProvider.GetDependency<IUpdateProjectServiceAccountsAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
.UpdateAsync(Arg.Any<ProjectServiceAccountsAccessPoliciesUpdates>());
}
[Theory]
[BitAutoData]
public async Task PutProjectServiceAccountsAccessPoliciesAsync_UserHasNoAccess_ThrowsNotFoundException(
SutProvider<AccessPoliciesController> sutProvider,
Project data,
ProjectServiceAccountsAccessPoliciesRequestModel request)
{
request = SetupValidRequest(request);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<ProjectServiceAccountsAccessPoliciesUpdates>(),
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Failed());
await Assert.ThrowsAsync<NotFoundException>(() =>
sutProvider.Sut.PutProjectServiceAccountsAccessPoliciesAsync(data.Id, request));
await sutProvider.GetDependency<IUpdateProjectServiceAccountsAccessPoliciesCommand>().DidNotReceiveWithAnyArgs()
.UpdateAsync(Arg.Any<ProjectServiceAccountsAccessPoliciesUpdates>());
;
}
[Theory]
[BitAutoData]
public async Task PutProjectServiceAccountsAccessPoliciesAsync_Success(
SutProvider<AccessPoliciesController> sutProvider,
Project data,
ProjectServiceAccountsAccessPoliciesRequestModel request)
{
request = SetupValidRequest(request);
sutProvider.GetDependency<IProjectRepository>().GetByIdAsync(data.Id).ReturnsForAnyArgs(data);
sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<ProjectServiceAccountsAccessPoliciesUpdates>(),
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Success());
await sutProvider.Sut.PutProjectServiceAccountsAccessPoliciesAsync(data.Id, request);
await sutProvider.GetDependency<IUpdateProjectServiceAccountsAccessPoliciesCommand>().Received(1)
.UpdateAsync(Arg.Any<ProjectServiceAccountsAccessPoliciesUpdates>());
}
private static AccessPoliciesCreateRequest AddRequestsOverMax(AccessPoliciesCreateRequest request)
{
var newRequests = new List<AccessPolicyRequest>();
@ -1168,4 +1359,14 @@ public class AccessPoliciesControllerTests
return request;
}
private static ProjectServiceAccountsAccessPoliciesRequestModel SetupValidRequest(ProjectServiceAccountsAccessPoliciesRequestModel request)
{
foreach (var policyRequest in request.ServiceAccountAccessPolicyRequests)
{
policyRequest.Read = true;
}
return request;
}
}