1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 07:36:14 -05:00

[SM-704] Extract Authorization For ServiceAccounts (#2869)

* Move to access query for project commands

* Swap to hasAccess method per action

* Swap to authorization handler pattern

* Move ProjectOperationRequirement to Core

* Add default throw + tests

* Extract authorization out of commands

* Unit tests for authorization handler

* Formatting

* Swap to reflection for testing switch

* Swap to check read & reflections in test

* fix wording on exception

* Refactor GetAccessClient into its own query

* Use accessClientQuery in project handler
This commit is contained in:
Thomas Avery
2023-05-31 13:49:58 -05:00
committed by GitHub
parent c08e2a7473
commit d1155ee376
16 changed files with 694 additions and 249 deletions

View File

@ -139,11 +139,21 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetByServiceAccountId_ServiceAccountDoesNotExist_NotFound()
{
var (org, _) = await _organizationHelper.Initialize(true, true);
await LoginAsync(_email);
var response = await _client.GetAsync($"/service-accounts/{new Guid()}");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GetByServiceAccountId_UserWithoutPermission_NotFound()
{
var (org, _) = await _organizationHelper.Initialize(true, true);
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
var (email, _) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
await LoginAsync(email);
var serviceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
@ -161,30 +171,7 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
[InlineData(PermissionType.RunAsUserWithPermission)]
public async Task GetByServiceAccountId_Success(PermissionType permissionType)
{
var (org, _) = await _organizationHelper.Initialize(true, true);
await 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 LoginAsync(email);
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> {
new UserServiceAccountAccessPolicy
{
GrantedServiceAccountId = serviceAccount.Id,
OrganizationUserId = orgUser.Id,
Write = true,
Read = true,
},
});
}
var serviceAccount = await SetupServiceAccountWithAccessAsync(permissionType);
var response = await _client.GetAsync($"/service-accounts/{serviceAccount.Id}");
response.EnsureSuccessStatusCode();
@ -212,12 +199,25 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task Create_Admin_Success()
[Theory]
[InlineData(PermissionType.RunAsAdmin)]
[InlineData(PermissionType.RunAsUserWithPermission)]
public async Task Create_Success(PermissionType permissionType)
{
var (org, orgUser) = await _organizationHelper.Initialize(true, true);
var (org, adminOrgUser) = await _organizationHelper.Initialize(true, true);
await LoginAsync(_email);
var orgUserId = adminOrgUser.Id;
var currentUserId = adminOrgUser.UserId!.Value;
if (permissionType == PermissionType.RunAsUserWithPermission)
{
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
await LoginAsync(email);
orgUserId = orgUser.Id;
currentUserId = orgUser.UserId!.Value;
}
var request = new ServiceAccountCreateRequestModel { Name = _mockEncryptedString };
var response = await _client.PostAsJsonAsync($"/organizations/{org.Id}/service-accounts", request);
@ -236,9 +236,11 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
AssertHelper.AssertRecent(createdServiceAccount.CreationDate);
// Check permissions have been bootstrapped.
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(createdServiceAccount.Id, orgUser.UserId!.Value);
var accessPolicies = await _accessPolicyRepository.GetManyByGrantedServiceAccountIdAsync(createdServiceAccount.Id, currentUserId);
Assert.NotNull(accessPolicies);
var ap = accessPolicies!.First();
var ap = (UserServiceAccountAccessPolicy)accessPolicies.First();
Assert.Equal(createdServiceAccount.Id, ap.GrantedServiceAccountId);
Assert.Equal(orgUserId, ap.OrganizationUserId);
Assert.True(ap.Read);
Assert.True(ap.Write);
AssertHelper.AssertRecent(ap.CreationDate);
@ -266,73 +268,6 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task Update_Admin()
{
var (org, _) = await _organizationHelper.Initialize(true, true);
await LoginAsync(_email);
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
{
OrganizationId = org.Id,
Name = _mockEncryptedString,
});
var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName };
var response = await _client.PutAsJsonAsync($"/service-accounts/{initialServiceAccount.Id}", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<ServiceAccountResponseModel>();
Assert.NotNull(result);
Assert.Equal(request.Name, result!.Name);
Assert.NotEqual(initialServiceAccount.Name, result.Name);
AssertHelper.AssertRecent(result.RevisionDate);
Assert.NotEqual(initialServiceAccount.RevisionDate, result.RevisionDate);
var updatedServiceAccount = await _serviceAccountRepository.GetByIdAsync(initialServiceAccount.Id);
Assert.NotNull(result);
Assert.Equal(request.Name, updatedServiceAccount.Name);
AssertHelper.AssertRecent(updatedServiceAccount.RevisionDate);
AssertHelper.AssertRecent(updatedServiceAccount.CreationDate);
Assert.NotEqual(initialServiceAccount.Name, updatedServiceAccount.Name);
Assert.NotEqual(initialServiceAccount.RevisionDate, updatedServiceAccount.RevisionDate);
}
[Fact]
public async Task Update_User_WithPermission()
{
var (org, _) = await _organizationHelper.Initialize(true, true);
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
await LoginAsync(email);
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
{
OrganizationId = org.Id,
Name = _mockEncryptedString,
});
await CreateUserPolicyAsync(orgUser.Id, initialServiceAccount.Id, true, true);
var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName };
var response = await _client.PutAsJsonAsync($"/service-accounts/{initialServiceAccount.Id}", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<ServiceAccountResponseModel>();
Assert.NotNull(result);
Assert.Equal(request.Name, result!.Name);
Assert.NotEqual(initialServiceAccount.Name, result.Name);
AssertHelper.AssertRecent(result.RevisionDate);
Assert.NotEqual(initialServiceAccount.RevisionDate, result.RevisionDate);
var updatedServiceAccount = await _serviceAccountRepository.GetByIdAsync(initialServiceAccount.Id);
Assert.NotNull(result);
Assert.Equal(request.Name, updatedServiceAccount.Name);
AssertHelper.AssertRecent(updatedServiceAccount.RevisionDate);
AssertHelper.AssertRecent(updatedServiceAccount.CreationDate);
Assert.NotEqual(initialServiceAccount.Name, updatedServiceAccount.Name);
Assert.NotEqual(initialServiceAccount.RevisionDate, updatedServiceAccount.RevisionDate);
}
[Fact]
public async Task Update_User_NoPermissions()
{
@ -352,6 +287,45 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task Update_NonExistingServiceAccount_NotFound()
{
await _organizationHelper.Initialize(true, true);
await LoginAsync(_email);
var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName };
var response = await _client.PutAsJsonAsync("/service-accounts/c53de509-4581-402c-8cbd-f26d2c516fba", request);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory]
[InlineData(PermissionType.RunAsAdmin)]
[InlineData(PermissionType.RunAsUserWithPermission)]
public async Task Update_Success(PermissionType permissionType)
{
var initialServiceAccount = await SetupServiceAccountWithAccessAsync(permissionType);
var request = new ServiceAccountUpdateRequestModel { Name = _mockNewName };
var response = await _client.PutAsJsonAsync($"/service-accounts/{initialServiceAccount.Id}", request);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<ServiceAccountResponseModel>();
Assert.NotNull(result);
Assert.Equal(request.Name, result!.Name);
Assert.NotEqual(initialServiceAccount.Name, result.Name);
AssertHelper.AssertRecent(result.RevisionDate);
Assert.NotEqual(initialServiceAccount.RevisionDate, result.RevisionDate);
var updatedServiceAccount = await _serviceAccountRepository.GetByIdAsync(initialServiceAccount.Id);
Assert.NotNull(result);
Assert.Equal(request.Name, updatedServiceAccount.Name);
AssertHelper.AssertRecent(updatedServiceAccount.RevisionDate);
AssertHelper.AssertRecent(updatedServiceAccount.CreationDate);
Assert.NotEqual(initialServiceAccount.Name, updatedServiceAccount.Name);
Assert.NotEqual(initialServiceAccount.RevisionDate, updatedServiceAccount.RevisionDate);
}
[Theory]
[InlineData(false, false)]
[InlineData(true, false)]
@ -837,4 +811,35 @@ public class ServiceAccountsControllerTests : IClassFixture<ApiApplicationFactor
return serviceAccountIds;
}
private async Task<ServiceAccount> SetupServiceAccountWithAccessAsync(PermissionType permissionType)
{
var (org, _) = await _organizationHelper.Initialize(true, true);
await LoginAsync(_email);
var initialServiceAccount = await _serviceAccountRepository.CreateAsync(new ServiceAccount
{
OrganizationId = org.Id,
Name = _mockEncryptedString,
});
if (permissionType == PermissionType.RunAsAdmin)
{
return initialServiceAccount;
}
var (email, orgUser) = await _organizationHelper.CreateNewUser(OrganizationUserType.User, true);
await LoginAsync(email);
var accessPolicies = new List<BaseAccessPolicy>
{
new UserServiceAccountAccessPolicy
{
GrantedServiceAccountId = initialServiceAccount.Id, OrganizationUserId = orgUser.Id, Read = true, Write = true,
},
};
await _accessPolicyRepository.CreateManyAsync(accessPolicies);
return initialServiceAccount;
}
}