mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 07:36:14 -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:
@ -1169,6 +1169,209 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
Assert.Single(result.GrantedProjectPolicies);
|
||||
}
|
||||
|
||||
[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 GetProjectServiceAccountsAccessPoliciesAsync_SmAccessDenied_ReturnsNotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled)
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
||||
await _loginHelper.LoginAsync(_email);
|
||||
var initData = await SetupAccessPolicyRequest(org.Id);
|
||||
|
||||
var response = await _client.GetAsync($"/projects/{initData.ProjectId}/access-policies/service-accounts");
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetProjectServiceAccountsAccessPoliciesAsync_NoAccessPolicies_ReturnsEmpty()
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
await _loginHelper.LoginAsync(_email);
|
||||
|
||||
var project = await _projectRepository.CreateAsync(new Project
|
||||
{
|
||||
OrganizationId = org.Id,
|
||||
Name = _mockEncryptedString,
|
||||
});
|
||||
|
||||
var response = await _client.GetAsync($"/projects/{project.Id}/access-policies/service-accounts");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<ProjectServiceAccountsAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Empty(result.ServiceAccountAccessPolicies);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetProjectServiceAccountsAccessPoliciesAsync_UserDoesntHavePermission_ReturnsNotFound()
|
||||
{
|
||||
// Create a new account as a user
|
||||
await _organizationHelper.Initialize(true, true, true);
|
||||
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await _loginHelper.LoginAsync(email);
|
||||
|
||||
var initData = await SetupAccessPolicyRequest(orgUser.OrganizationId);
|
||||
|
||||
var response = await _client.GetAsync($"/projects/{initData.ProjectId}/access-policies/service-accounts");
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task GetProjectServiceAccountsAccessPoliciesAsync_Success(PermissionType permissionType)
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
await _loginHelper.LoginAsync(_email);
|
||||
var initData = await SetupAccessPolicyRequest(org.Id);
|
||||
|
||||
if (permissionType == PermissionType.RunAsUserWithPermission)
|
||||
{
|
||||
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await _loginHelper.LoginAsync(email);
|
||||
var accessPolicies = new List<BaseAccessPolicy>
|
||||
{
|
||||
new UserProjectAccessPolicy
|
||||
{
|
||||
GrantedProjectId = initData.ProjectId, OrganizationUserId = orgUser.Id, Read = true, Write = true,
|
||||
}
|
||||
};
|
||||
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
||||
}
|
||||
|
||||
var response = await _client.GetAsync($"/projects/{initData.ProjectId}/access-policies/service-accounts");
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content
|
||||
.ReadFromJsonAsync<ProjectServiceAccountsAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.NotEmpty(result.ServiceAccountAccessPolicies);
|
||||
Assert.Equal(initData.ServiceAccountId, result.ServiceAccountAccessPolicies.First().ServiceAccountId);
|
||||
Assert.NotNull(result.ServiceAccountAccessPolicies.First().ServiceAccountName);
|
||||
Assert.NotNull(result.ServiceAccountAccessPolicies.First().GrantedProjectName);
|
||||
}
|
||||
|
||||
[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 PutProjectServiceAccountsAccessPoliciesAsync_SmNotEnabled_NotFound(bool useSecrets,
|
||||
bool accessSecrets, bool organizationEnabled)
|
||||
{
|
||||
var (_, organizationUser) =
|
||||
await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled);
|
||||
await _loginHelper.LoginAsync(_email);
|
||||
|
||||
var (projectId, serviceAccountId) = await CreateProjectAndServiceAccountAsync(organizationUser.OrganizationId);
|
||||
|
||||
var request = new ProjectServiceAccountsAccessPoliciesRequestModel
|
||||
{
|
||||
ServiceAccountAccessPolicyRequests =
|
||||
[
|
||||
new AccessPolicyRequest { GranteeId = serviceAccountId, Read = true, Write = true }
|
||||
]
|
||||
};
|
||||
|
||||
var response = await _client.PutAsJsonAsync($"/projects/{projectId}/access-policies/service-accounts", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PutProjectServiceAccountsAccessPoliciesAsync_UserHasNoPermission_ReturnsNotFound()
|
||||
{
|
||||
var (org, _) = await _organizationHelper.Initialize(true, true, true);
|
||||
var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
|
||||
await _loginHelper.LoginAsync(email);
|
||||
|
||||
var (projectId, serviceAccountId) = await CreateProjectAndServiceAccountAsync(org.Id);
|
||||
|
||||
var request = new ProjectServiceAccountsAccessPoliciesRequestModel
|
||||
{
|
||||
ServiceAccountAccessPolicyRequests =
|
||||
[
|
||||
new AccessPolicyRequest { GranteeId = serviceAccountId, Read = true, Write = true }
|
||||
]
|
||||
};
|
||||
|
||||
var response = await _client.PutAsJsonAsync($"/projects/{projectId}/access-policies/service-accounts", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission)]
|
||||
public async Task PutProjectServiceAccountsAccessPoliciesAsync_MismatchedOrgIds_ReturnsNotFound(
|
||||
PermissionType permissionType)
|
||||
{
|
||||
var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await _loginHelper.LoginAsync(_email);
|
||||
|
||||
var (project, request) =
|
||||
await SetupProjectServiceAccountsAccessPoliciesRequestAsync(permissionType, organizationUser,
|
||||
false);
|
||||
|
||||
var newOrg = await _organizationHelper.CreateSmOrganizationAsync();
|
||||
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
Name = _mockEncryptedString,
|
||||
OrganizationId = newOrg.Id
|
||||
});
|
||||
request.ServiceAccountAccessPolicyRequests =
|
||||
[
|
||||
new AccessPolicyRequest { GranteeId = serviceAccount.Id, Read = true, Write = true }
|
||||
];
|
||||
|
||||
var response =
|
||||
await _client.PutAsJsonAsync($"/projects/{project.Id}/access-policies/service-accounts", request);
|
||||
|
||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionType.RunAsAdmin, false)]
|
||||
[InlineData(PermissionType.RunAsAdmin, true)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission, false)]
|
||||
[InlineData(PermissionType.RunAsUserWithPermission, true)]
|
||||
public async Task PutProjectServiceAccountsAccessPoliciesAsync_Success(PermissionType permissionType,
|
||||
bool createPreviousAccessPolicy)
|
||||
{
|
||||
var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true);
|
||||
await _loginHelper.LoginAsync(_email);
|
||||
|
||||
var (project, request) =
|
||||
await SetupProjectServiceAccountsAccessPoliciesRequestAsync(permissionType, organizationUser,
|
||||
createPreviousAccessPolicy);
|
||||
|
||||
var response =
|
||||
await _client.PutAsJsonAsync($"/projects/{project.Id}/access-policies/service-accounts", request);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var result = await response.Content
|
||||
.ReadFromJsonAsync<ProjectServiceAccountsAccessPoliciesResponseModel>();
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(request.ServiceAccountAccessPolicyRequests.First().GranteeId,
|
||||
result.ServiceAccountAccessPolicies.First().ServiceAccountId);
|
||||
Assert.True(result.ServiceAccountAccessPolicies.First().Read);
|
||||
Assert.True(result.ServiceAccountAccessPolicies.First().Write);
|
||||
Assert.Single(result.ServiceAccountAccessPolicies);
|
||||
}
|
||||
|
||||
private async Task<RequestSetupData> SetupAccessPolicyRequest(Guid organizationId)
|
||||
{
|
||||
var project = await _projectRepository.CreateAsync(new Project
|
||||
@ -1184,13 +1387,15 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
});
|
||||
|
||||
var accessPolicy = await _accessPolicyRepository.CreateManyAsync(
|
||||
new List<BaseAccessPolicy>
|
||||
[
|
||||
new ServiceAccountProjectAccessPolicy
|
||||
{
|
||||
new ServiceAccountProjectAccessPolicy
|
||||
{
|
||||
Read = true, Write = true, ServiceAccountId = serviceAccount.Id, GrantedProjectId = project.Id,
|
||||
},
|
||||
});
|
||||
Read = true,
|
||||
Write = true,
|
||||
ServiceAccountId = serviceAccount.Id,
|
||||
GrantedProjectId = project.Id,
|
||||
}
|
||||
]);
|
||||
|
||||
return new RequestSetupData
|
||||
{
|
||||
@ -1395,6 +1600,65 @@ public class AccessPoliciesControllerTests : IClassFixture<ApiApplicationFactory
|
||||
return (serviceAccount, request);
|
||||
}
|
||||
|
||||
private async Task<(Project project, ProjectServiceAccountsAccessPoliciesRequestModel request)>
|
||||
SetupProjectServiceAccountsAccessPoliciesRequestAsync(
|
||||
PermissionType permissionType, OrganizationUser organizationUser, bool createPreviousAccessPolicy)
|
||||
{
|
||||
var (project, currentUser) = await SetupProjectPeoplePermissionAsync(permissionType, organizationUser);
|
||||
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
Name = _mockEncryptedString,
|
||||
OrganizationId = currentUser.OrganizationId
|
||||
});
|
||||
|
||||
var accessPolicies = new List<BaseAccessPolicy>
|
||||
{
|
||||
new UserServiceAccountAccessPolicy
|
||||
{
|
||||
GrantedServiceAccountId = serviceAccount.Id,
|
||||
OrganizationUserId = currentUser.Id,
|
||||
Read = true,
|
||||
Write = true
|
||||
}
|
||||
};
|
||||
|
||||
var request = new ProjectServiceAccountsAccessPoliciesRequestModel
|
||||
{
|
||||
ServiceAccountAccessPolicyRequests =
|
||||
[
|
||||
new() { GranteeId = serviceAccount.Id, Read = true, Write = true }
|
||||
]
|
||||
};
|
||||
|
||||
if (createPreviousAccessPolicy)
|
||||
{
|
||||
var anotherServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
|
||||
{
|
||||
Name = _mockEncryptedString,
|
||||
OrganizationId = currentUser.OrganizationId
|
||||
});
|
||||
|
||||
accessPolicies.Add(new UserServiceAccountAccessPolicy
|
||||
{
|
||||
GrantedServiceAccountId = anotherServiceAccount.Id,
|
||||
OrganizationUserId = currentUser.Id,
|
||||
Read = true,
|
||||
Write = true
|
||||
});
|
||||
accessPolicies.Add(new ServiceAccountProjectAccessPolicy
|
||||
{
|
||||
GrantedProjectId = project.Id,
|
||||
ServiceAccountId = anotherServiceAccount.Id,
|
||||
Read = true,
|
||||
Write = true
|
||||
});
|
||||
}
|
||||
|
||||
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
|
||||
|
||||
return (project, request);
|
||||
}
|
||||
|
||||
private class RequestSetupData
|
||||
{
|
||||
public Guid ProjectId { get; set; }
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,79 @@
|
||||
#nullable enable
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Enums.AccessPolicies;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.SecretsManager.Models;
|
||||
|
||||
public class ProjectServiceAccountsAccessPoliciesTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetPolicyUpdates_NoChanges_ReturnsEmptyList()
|
||||
{
|
||||
var serviceAccountId1 = Guid.NewGuid();
|
||||
var serviceAccountId2 = Guid.NewGuid();
|
||||
var projectId = Guid.NewGuid();
|
||||
|
||||
var existing = new ProjectServiceAccountsAccessPolicies
|
||||
{
|
||||
ServiceAccountAccessPolicies = new List<ServiceAccountProjectAccessPolicy>
|
||||
{
|
||||
new() { ServiceAccountId = serviceAccountId1, GrantedProjectId = projectId, Read = true, Write = true },
|
||||
new() { ServiceAccountId = serviceAccountId2, GrantedProjectId = projectId, Read = false, Write = true }
|
||||
}
|
||||
};
|
||||
|
||||
var result = existing.GetPolicyUpdates(existing);
|
||||
|
||||
Assert.Empty(result.ServiceAccountAccessPolicyUpdates);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPolicyUpdates_ReturnsCorrectPolicyChanges()
|
||||
{
|
||||
var serviceAccountId1 = Guid.NewGuid();
|
||||
var serviceAccountId2 = Guid.NewGuid();
|
||||
var serviceAccountId3 = Guid.NewGuid();
|
||||
var serviceAccountId4 = Guid.NewGuid();
|
||||
var projectId = Guid.NewGuid();
|
||||
|
||||
var existing = new ProjectServiceAccountsAccessPolicies
|
||||
{
|
||||
ServiceAccountAccessPolicies = new List<ServiceAccountProjectAccessPolicy>
|
||||
{
|
||||
new() { ServiceAccountId = serviceAccountId1, GrantedProjectId = projectId, Read = true, Write = true },
|
||||
new() { ServiceAccountId = serviceAccountId3, GrantedProjectId = projectId, Read = true, Write = true },
|
||||
new() { ServiceAccountId = serviceAccountId4, GrantedProjectId = projectId, Read = true, Write = true }
|
||||
}
|
||||
};
|
||||
|
||||
var requested = new ProjectServiceAccountsAccessPolicies
|
||||
{
|
||||
ServiceAccountAccessPolicies = new List<ServiceAccountProjectAccessPolicy>
|
||||
{
|
||||
new() { ServiceAccountId = serviceAccountId1, GrantedProjectId = projectId, Read = true, Write = false },
|
||||
new() { ServiceAccountId = serviceAccountId2, GrantedProjectId = projectId, Read = false, Write = true },
|
||||
new() { ServiceAccountId = serviceAccountId3, GrantedProjectId = projectId, Read = true, Write = true }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var result = existing.GetPolicyUpdates(requested);
|
||||
|
||||
Assert.Contains(serviceAccountId2, result.ServiceAccountAccessPolicyUpdates
|
||||
.Where(pu => pu.Operation == AccessPolicyOperation.Create)
|
||||
.Select(pu => pu.AccessPolicy.ServiceAccountId!.Value));
|
||||
|
||||
Assert.Contains(serviceAccountId4, result.ServiceAccountAccessPolicyUpdates
|
||||
.Where(pu => pu.Operation == AccessPolicyOperation.Delete)
|
||||
.Select(pu => pu.AccessPolicy.ServiceAccountId!.Value));
|
||||
|
||||
Assert.Contains(serviceAccountId1, result.ServiceAccountAccessPolicyUpdates
|
||||
.Where(pu => pu.Operation == AccessPolicyOperation.Update)
|
||||
.Select(pu => pu.AccessPolicy.ServiceAccountId!.Value));
|
||||
|
||||
Assert.DoesNotContain(serviceAccountId3, result.ServiceAccountAccessPolicyUpdates
|
||||
.Select(pu => pu.AccessPolicy.ServiceAccountId!.Value));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user