using System.Net; using Bit.Api.IntegrationTest.Factories; using Bit.Api.IntegrationTest.SecretsManager.Enums; using Bit.Api.IntegrationTest.SecretsManager.Helpers; using Bit.Api.Models.Response; using Bit.Api.SecretsManager.Models.Request; using Bit.Api.SecretsManager.Models.Response; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.SecretsManager.Entities; using Bit.Core.SecretsManager.Repositories; using Xunit; namespace Bit.Api.IntegrationTest.SecretsManager.Controllers; public class AccessPoliciesControllerTests : IClassFixture, IAsyncLifetime { private const string _mockEncryptedString = "2.3Uk+WNBIoU5xzmVFNcoWzz==|1MsPIYuRfdOHfu/0uY6H2Q==|/98sp4wb6pHP1VTZ9JcNCYgQjEUMFPlqJgCwRk1YXKg="; private readonly IAccessPolicyRepository _accessPolicyRepository; private readonly HttpClient _client; private readonly ApiApplicationFactory _factory; private readonly IGroupRepository _groupRepository; private readonly LoginHelper _loginHelper; private readonly IProjectRepository _projectRepository; private readonly ISecretRepository _secretRepository; private readonly IServiceAccountRepository _serviceAccountRepository; private string _email = null!; private SecretsManagerOrganizationHelper _organizationHelper = null!; public AccessPoliciesControllerTests(ApiApplicationFactory factory) { _factory = factory; _client = _factory.CreateClient(); _accessPolicyRepository = _factory.GetService(); _serviceAccountRepository = _factory.GetService(); _secretRepository = _factory.GetService(); _projectRepository = _factory.GetService(); _groupRepository = _factory.GetService(); _loginHelper = new LoginHelper(_factory, _client); } public async Task InitializeAsync() { _email = $"integration-test{Guid.NewGuid()}@bitwarden.com"; await _factory.LoginWithNewAccount(_email); _organizationHelper = new SecretsManagerOrganizationHelper(_factory, _email); } public Task DisposeAsync() { _client.Dispose(); return Task.CompletedTask; } [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 GetPeoplePotentialGrantees_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/people/potential-grantees"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetPeoplePotentialGrantees_Success(PermissionType permissionType) { var (org, _) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); } var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/people/potential-grantees"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync>(); Assert.NotNull(result?.Data); Assert.NotEmpty(result.Data); } [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 GetServiceAccountPotentialGrantees_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/service-accounts/potential-grantees"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task GetServiceAccountPotentialGrantees_OnlyReturnsServiceAccountsWithWriteAccess() { // Create a new account as a user var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = org.Id, Name = _mockEncryptedString }); var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/service-accounts/potential-grantees"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync>(); Assert.NotNull(result?.Data); Assert.Empty(result.Data); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetServiceAccountsPotentialGrantees_Success(PermissionType permissionType) { var (org, _) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = org.Id, Name = _mockEncryptedString }); if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); await _accessPolicyRepository.CreateManyAsync( [ new UserServiceAccountAccessPolicy { GrantedServiceAccountId = serviceAccount.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true } ]); } var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/service-accounts/potential-grantees"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync>(); Assert.NotNull(result?.Data); Assert.NotEmpty(result.Data); Assert.Equal(serviceAccount.Id, result.Data.First(x => x.Id == serviceAccount.Id).Id); } [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 GetProjectPotentialGrantees_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/projects/potential-grantees"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task GetProjectPotentialGrantees_OnlyReturnsProjectsWithWriteAccess() { // Create a new account as a user var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, Name = _mockEncryptedString }); var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/projects/potential-grantees"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync>(); Assert.NotNull(result?.Data); Assert.Empty(result.Data); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetProjectPotentialGrantees_Success(PermissionType permissionType) { var (org, _) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var project = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, Name = _mockEncryptedString }); if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); await _accessPolicyRepository.CreateManyAsync( [ new UserProjectAccessPolicy { GrantedProjectId = project.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true } ]); } var response = await _client.GetAsync( $"/organizations/{org.Id}/access-policies/projects/potential-grantees"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync>(); Assert.NotNull(result?.Data); Assert.NotEmpty(result.Data); Assert.Equal(project.Id, result.Data.First(x => x.Id == project.Id).Id); } [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 GetProjectPeopleAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); 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/people"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task GetProjectPeopleAccessPolicies_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/people"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result); Assert.Empty(result.UserAccessPolicies); Assert.Empty(result.GroupAccessPolicies); } [Fact] public async Task GetProjectPeopleAccessPolicies_NoPermission_NotFound() { await _organizationHelper.Initialize(true, true, true); var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); var project = await _projectRepository.CreateAsync(new Project { OrganizationId = orgUser.OrganizationId, Name = _mockEncryptedString }); var response = await _client.GetAsync($"/projects/{project.Id}/access-policies/people"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetProjectPeopleAccessPolicies_Success(PermissionType permissionType) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (project, _) = await SetupProjectPeoplePermissionAsync(permissionType, organizationUser); var response = await _client.GetAsync($"/projects/{project.Id}/access-policies/people"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result?.UserAccessPolicies); Assert.Single(result.UserAccessPolicies); } [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 PutProjectPeopleAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (_, organizationUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var (project, request) = await SetupProjectPeopleRequestAsync(PermissionType.RunAsAdmin, organizationUser); var response = await _client.PutAsJsonAsync($"/projects/{project.Id}/access-policies/people", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PutProjectPeopleAccessPolicies_NoPermission() { var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, organizationUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); var project = await _projectRepository.CreateAsync(new Project { OrganizationId = org.Id, Name = _mockEncryptedString }); var request = new PeopleAccessPoliciesRequestModel { UserAccessPolicyRequests = new List { new() { GranteeId = organizationUser.Id, Read = true, Write = true } } }; var response = await _client.PutAsJsonAsync($"/projects/{project.Id}/access-policies/people", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task PutProjectPeopleAccessPolicies_MismatchedOrgIds_NotFound(PermissionType permissionType) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (project, request) = await SetupProjectPeopleRequestAsync(permissionType, organizationUser); var newOrg = await _organizationHelper.CreateSmOrganizationAsync(); var group = await _groupRepository.CreateAsync(new Group { OrganizationId = newOrg.Id, Name = _mockEncryptedString }); request.GroupAccessPolicyRequests = new List { new() { GranteeId = group.Id, Read = true, Write = true } }; var response = await _client.PutAsJsonAsync($"/projects/{project.Id}/access-policies/people", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task PutProjectPeopleAccessPolicies_Success(PermissionType permissionType) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (project, request) = await SetupProjectPeopleRequestAsync(permissionType, organizationUser); var response = await _client.PutAsJsonAsync($"/projects/{project.Id}/access-policies/people", request); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result); Assert.Equal(request.UserAccessPolicyRequests.First().GranteeId, result.UserAccessPolicies.First().OrganizationUserId); Assert.True(result.UserAccessPolicies.First().Read); Assert.True(result.UserAccessPolicies.First().Write); } [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 GetServiceAccountPeopleAccessPolicies_SmAccessDenied_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = org.Id, Name = _mockEncryptedString }); var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task GetServiceAccountPeopleAccessPolicies_ReturnsEmpty() { var (org, _) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = org.Id, Name = _mockEncryptedString }); var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result); Assert.Empty(result.UserAccessPolicies); Assert.Empty(result.GroupAccessPolicies); } [Fact] public async Task GetServiceAccountPeopleAccessPolicies_NoPermission() { var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = org.Id, Name = _mockEncryptedString }); var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetServiceAccountPeopleAccessPolicies_Success(PermissionType permissionType) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (serviceAccount, _) = await SetupServiceAccountPeoplePermissionAsync(permissionType, organizationUser); var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people"); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result?.UserAccessPolicies); Assert.Single(result.UserAccessPolicies); } [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 PutServiceAccountPeopleAccessPolicies_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (_, organizationUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var (serviceAccount, request) = await SetupServiceAccountPeopleRequestAsync(PermissionType.RunAsAdmin, organizationUser); var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PutServiceAccountPeopleAccessPolicies_NoPermission() { var (org, _) = await _organizationHelper.Initialize(true, true, true); var (email, organizationUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = org.Id, Name = _mockEncryptedString }); var request = new PeopleAccessPoliciesRequestModel { UserAccessPolicyRequests = new List { new() { GranteeId = organizationUser.Id, Read = true, Write = true } } }; var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task PutServiceAccountPeopleAccessPolicies_MismatchedOrgIds_NotFound(PermissionType permissionType) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (serviceAccount, request) = await SetupServiceAccountPeopleRequestAsync(permissionType, organizationUser); var newOrg = await _organizationHelper.CreateSmOrganizationAsync(); var group = await _groupRepository.CreateAsync(new Group { OrganizationId = newOrg.Id, Name = _mockEncryptedString }); request.GroupAccessPolicyRequests = new List { new() { GranteeId = group.Id, Read = true, Write = true } }; var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task PutServiceAccountPeopleAccessPolicies_Success(PermissionType permissionType) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (serviceAccount, request) = await SetupServiceAccountPeopleRequestAsync(permissionType, organizationUser); var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/access-policies/people", request); response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); Assert.NotNull(result); Assert.Equal(request.UserAccessPolicyRequests.First().GranteeId, result.UserAccessPolicies.First().OrganizationUserId); Assert.True(result.UserAccessPolicies.First().Read); Assert.True(result.UserAccessPolicies.First().Write); } [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 GetServiceAccountGrantedPoliciesAsync_SmAccessDenied_ReturnsNotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var initData = await CreateServiceAccountProjectAccessPolicyAsync(org.Id); var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/granted-policies"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task GetServiceAccountGrantedPoliciesAsync_NoAccessPolicies_ReturnsEmpty() { var (org, _) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = org.Id, Name = _mockEncryptedString }); var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}/granted-policies"); response.EnsureSuccessStatusCode(); var result = await response.Content .ReadFromJsonAsync(); Assert.NotNull(result); Assert.Empty(result.GrantedProjectPolicies); } [Fact] public async Task GetServiceAccountGrantedPoliciesAsync_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 CreateServiceAccountProjectAccessPolicyAsync(orgUser.OrganizationId); var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/granted-policies"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task GetServiceAccountGrantedPoliciesAsync_Success(PermissionType permissionType) { var (org, _) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var initData = await CreateServiceAccountProjectAccessPolicyAsync(org.Id); if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); var accessPolicies = new List { new UserServiceAccountAccessPolicy { GrantedServiceAccountId = initData.ServiceAccountId, OrganizationUserId = orgUser.Id, Read = true, Write = true } }; await _accessPolicyRepository.CreateManyAsync(accessPolicies); } var response = await _client.GetAsync($"/service-accounts/{initData.ServiceAccountId}/granted-policies"); response.EnsureSuccessStatusCode(); var result = await response.Content .ReadFromJsonAsync(); Assert.NotNull(result); Assert.NotEmpty(result.GrantedProjectPolicies); Assert.NotNull(result.GrantedProjectPolicies.First().AccessPolicy.GrantedProjectName); Assert.NotNull(result.GrantedProjectPolicies.First().AccessPolicy.GrantedProjectId); } [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 PutServiceAccountGrantedPoliciesAsync_SmNotEnabled_NotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (_, organizationUser) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var (serviceAccount, request) = await SetupServiceAccountGrantedPoliciesRequestAsync(PermissionType.RunAsAdmin, organizationUser, false); var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/granted-policies", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task PutServiceAccountGrantedPoliciesAsync_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 ServiceAccountGrantedPoliciesRequestModel { ProjectGrantedPolicyRequests = new List { new() { GrantedId = projectId, Read = true, Write = true } } }; var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccountId}/granted-policies", request); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] [InlineData(PermissionType.RunAsUserWithPermission)] public async Task PutServiceAccountGrantedPoliciesAsync_MismatchedOrgIds_ReturnsNotFound( PermissionType permissionType) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (serviceAccount, request) = await SetupServiceAccountGrantedPoliciesRequestAsync(permissionType, organizationUser, false); var newOrg = await _organizationHelper.CreateSmOrganizationAsync(); var project = await _projectRepository.CreateAsync(new Project { Name = _mockEncryptedString, OrganizationId = newOrg.Id }); request.ProjectGrantedPolicyRequests = new List { new() { GrantedId = project.Id, Read = true, Write = true } }; var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/granted-policies", 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 PutServiceAccountGrantedPoliciesAsync_Success(PermissionType permissionType, bool createPreviousAccessPolicy) { var (_, organizationUser) = await _organizationHelper.Initialize(true, true, true); await _loginHelper.LoginAsync(_email); var (serviceAccount, request) = await SetupServiceAccountGrantedPoliciesRequestAsync(permissionType, organizationUser, createPreviousAccessPolicy); var response = await _client.PutAsJsonAsync($"/service-accounts/{serviceAccount.Id}/granted-policies", request); response.EnsureSuccessStatusCode(); var result = await response.Content .ReadFromJsonAsync(); Assert.NotNull(result); Assert.Equal(request.ProjectGrantedPolicyRequests.First().GrantedId, result.GrantedProjectPolicies.First().AccessPolicy.GrantedProjectId); Assert.True(result.GrantedProjectPolicies.First().AccessPolicy.Read); Assert.True(result.GrantedProjectPolicies.First().AccessPolicy.Write); Assert.True(result.GrantedProjectPolicies.First().HasPermission); 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 CreateServiceAccountProjectAccessPolicyAsync(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(); 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 CreateServiceAccountProjectAccessPolicyAsync(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 CreateServiceAccountProjectAccessPolicyAsync(org.Id); if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); var accessPolicies = new List { 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(); Assert.NotNull(result); Assert.NotEmpty(result.ServiceAccountAccessPolicies); Assert.Equal(initData.ServiceAccountId, result.ServiceAccountAccessPolicies.First().ServiceAccountId); Assert.NotNull(result.ServiceAccountAccessPolicies.First().ServiceAccountName); } [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(); 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); } [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 GetSecretAccessPoliciesAsync_SmAccessDenied_ReturnsNotFound(bool useSecrets, bool accessSecrets, bool organizationEnabled) { var (org, _) = await _organizationHelper.Initialize(useSecrets, accessSecrets, organizationEnabled); await _loginHelper.LoginAsync(_email); var secret = await _secretRepository.CreateAsync(new Secret { OrganizationId = org.Id, Key = _mockEncryptedString, Value = _mockEncryptedString, Note = _mockEncryptedString }); var response = await _client.GetAsync($"/secrets/{secret.Id}/access-policies"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Fact] public async Task GetSecretAccessPoliciesAsync_NoAccessPolicies_ReturnsEmpty() { var (secretId, _) = await SetupSecretAccessPoliciesTest(PermissionType.RunAsAdmin); var response = await _client.GetAsync($"/secrets/{secretId}/access-policies"); response.EnsureSuccessStatusCode(); var result = await response.Content .ReadFromJsonAsync(); Assert.NotNull(result); Assert.Empty(result.UserAccessPolicies); Assert.Empty(result.GroupAccessPolicies); Assert.Empty(result.ServiceAccountAccessPolicies); } [Fact] public async Task GetSecretAccessPoliciesAsync_UserDoesntHavePermission_ReturnsNotFound() { var (secretId, _) = await SetupSecretAccessPoliciesTest(PermissionType.RunAsUserWithPermission); var response = await _client.GetAsync($"/secrets/{secretId}/access-policies"); Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } [Theory] [InlineData(PermissionType.RunAsAdmin)] public async Task GetSecretAccessPoliciesAsync_Success(PermissionType permissionType) { var (secretId, currentOrgUser) = await SetupSecretAccessPoliciesTest(permissionType); var accessPolicies = new List { new UserSecretAccessPolicy { GrantedSecretId = secretId, OrganizationUserId = currentOrgUser.Id, Read = true, Write = true } }; await _accessPolicyRepository.CreateManyAsync(accessPolicies); var response = await _client.GetAsync($"/secrets/{secretId}/access-policies"); response.EnsureSuccessStatusCode(); var result = await response.Content .ReadFromJsonAsync(); Assert.NotNull(result); Assert.NotEmpty(result.UserAccessPolicies); Assert.Empty(result.GroupAccessPolicies); Assert.Empty(result.ServiceAccountAccessPolicies); Assert.NotNull(result.UserAccessPolicies.First().OrganizationUserName); Assert.NotNull(result.UserAccessPolicies.First().OrganizationUserId); Assert.NotNull(result.UserAccessPolicies.First().CurrentUser); Assert.Equal(currentOrgUser.Id, result.UserAccessPolicies.First().OrganizationUserId); } private async Task<(Guid ProjectId, Guid ServiceAccountId)> CreateServiceAccountProjectAccessPolicyAsync( Guid organizationId) { var (projectId, serviceAccountId) = await CreateProjectAndServiceAccountAsync(organizationId); await _accessPolicyRepository.CreateManyAsync( [ new ServiceAccountProjectAccessPolicy { Read = true, Write = true, ServiceAccountId = serviceAccountId, GrantedProjectId = projectId } ]); return (projectId, serviceAccountId); } private async Task<(Project project, OrganizationUser currentUser)> SetupProjectPeoplePermissionAsync( PermissionType permissionType, OrganizationUser organizationUser) { var project = await _projectRepository.CreateAsync(new Project { OrganizationId = organizationUser.OrganizationId, Name = _mockEncryptedString }); if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); organizationUser = orgUser; } var accessPolicies = new List { new UserProjectAccessPolicy { GrantedProjectId = project.Id, OrganizationUserId = organizationUser.Id, Read = true, Write = true } }; await _accessPolicyRepository.CreateManyAsync(accessPolicies); return (project, organizationUser); } private async Task<(ServiceAccount serviceAccount, OrganizationUser currentUser)> SetupServiceAccountPeoplePermissionAsync( PermissionType permissionType, OrganizationUser organizationUser) { var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = organizationUser.OrganizationId, Name = _mockEncryptedString }); if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); organizationUser = orgUser; } var accessPolicies = new List { new UserServiceAccountAccessPolicy { GrantedServiceAccountId = serviceAccount.Id, OrganizationUserId = organizationUser.Id, Read = true, Write = true } }; await _accessPolicyRepository.CreateManyAsync(accessPolicies); return (serviceAccount, organizationUser); } private async Task<(Project project, PeopleAccessPoliciesRequestModel request)> SetupProjectPeopleRequestAsync( PermissionType permissionType, OrganizationUser organizationUser) { var (project, currentUser) = await SetupProjectPeoplePermissionAsync(permissionType, organizationUser); var request = new PeopleAccessPoliciesRequestModel { UserAccessPolicyRequests = new List { new() { GranteeId = currentUser.Id, Read = true, Write = true } } }; return (project, request); } private async Task<(ServiceAccount serviceAccount, PeopleAccessPoliciesRequestModel request)> SetupServiceAccountPeopleRequestAsync( PermissionType permissionType, OrganizationUser organizationUser) { var (serviceAccount, currentUser) = await SetupServiceAccountPeoplePermissionAsync(permissionType, organizationUser); var request = new PeopleAccessPoliciesRequestModel { UserAccessPolicyRequests = new List { new() { GranteeId = currentUser.Id, Read = true, Write = true } } }; return (serviceAccount, request); } private async Task<(Guid ProjectId, Guid ServiceAccountId)> CreateProjectAndServiceAccountAsync(Guid organizationId, bool misMatchOrganization = false) { var newOrg = new Organization(); if (misMatchOrganization) { newOrg = await _organizationHelper.CreateSmOrganizationAsync(); } var project = await _projectRepository.CreateAsync(new Project { OrganizationId = misMatchOrganization ? newOrg.Id : organizationId, Name = _mockEncryptedString }); var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount { OrganizationId = organizationId, Name = _mockEncryptedString }); return (project.Id, serviceAccount.Id); } private async Task<(ServiceAccount serviceAccount, ServiceAccountGrantedPoliciesRequestModel request)> SetupServiceAccountGrantedPoliciesRequestAsync( PermissionType permissionType, OrganizationUser organizationUser, bool createPreviousAccessPolicy) { var (serviceAccount, currentUser) = await SetupServiceAccountPeoplePermissionAsync(permissionType, organizationUser); var project = await _projectRepository.CreateAsync(new Project { Name = _mockEncryptedString, OrganizationId = organizationUser.OrganizationId }); var accessPolicies = new List { new UserProjectAccessPolicy { GrantedProjectId = project.Id, OrganizationUserId = currentUser.Id, Read = true, Write = true } }; if (createPreviousAccessPolicy) { var anotherProject = await _projectRepository.CreateAsync(new Project { Name = _mockEncryptedString, OrganizationId = organizationUser.OrganizationId }); accessPolicies.Add(new UserProjectAccessPolicy { GrantedProjectId = anotherProject.Id, OrganizationUserId = currentUser.Id, Read = true, Write = true }); accessPolicies.Add(new ServiceAccountProjectAccessPolicy { GrantedProjectId = anotherProject.Id, ServiceAccountId = serviceAccount.Id, Read = true, Write = true }); } await _accessPolicyRepository.CreateManyAsync(accessPolicies); var request = new ServiceAccountGrantedPoliciesRequestModel { ProjectGrantedPolicyRequests = new List { new() { GrantedId = project.Id, Read = true, Write = true } } }; 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 { new UserServiceAccountAccessPolicy { GrantedServiceAccountId = serviceAccount.Id, OrganizationUserId = currentUser.Id, Read = true, Write = true } }; var request = new ProjectServiceAccountsAccessPoliciesRequestModel { ServiceAccountAccessPolicyRequests = [ new AccessPolicyRequest { 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 async Task<(Guid SecretId, OrganizationUser currentOrgUser)> SetupSecretAccessPoliciesTest( PermissionType permissionType) { var (org, orgAdmin) = await _organizationHelper.Initialize(true, true, true); var currentOrgUser = orgAdmin; if (permissionType == PermissionType.RunAsUserWithPermission) { var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true); await _loginHelper.LoginAsync(email); currentOrgUser = orgUser; } else { await _loginHelper.LoginAsync(_email); } var secret = await _secretRepository.CreateAsync(new Secret { OrganizationId = org.Id, Key = _mockEncryptedString, Value = _mockEncryptedString, Note = _mockEncryptedString }); return (secret.Id, currentOrgUser); } }