1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 15:42:48 -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

@ -4,6 +4,7 @@ using Bit.Api.SecretsManager.Models.Response;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.SecretsManager.AuthorizationRequirements;
using Bit.Core.SecretsManager.Commands.AccessTokens.Interfaces;
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
using Bit.Core.SecretsManager.Repositories;
@ -21,6 +22,7 @@ public class ServiceAccountsController : Controller
{
private readonly ICurrentContext _currentContext;
private readonly IUserService _userService;
private readonly IAuthorizationService _authorizationService;
private readonly IServiceAccountRepository _serviceAccountRepository;
private readonly IApiKeyRepository _apiKeyRepository;
private readonly ICreateAccessTokenCommand _createAccessTokenCommand;
@ -32,6 +34,7 @@ public class ServiceAccountsController : Controller
public ServiceAccountsController(
ICurrentContext currentContext,
IUserService userService,
IAuthorizationService authorizationService,
IServiceAccountRepository serviceAccountRepository,
IApiKeyRepository apiKeyRepository,
ICreateAccessTokenCommand createAccessTokenCommand,
@ -42,6 +45,7 @@ public class ServiceAccountsController : Controller
{
_currentContext = currentContext;
_userService = userService;
_authorizationService = authorizationService;
_serviceAccountRepository = serviceAccountRepository;
_apiKeyRepository = apiKeyRepository;
_createServiceAccountCommand = createServiceAccountCommand;
@ -73,32 +77,13 @@ public class ServiceAccountsController : Controller
[HttpGet("{id}")]
public async Task<ServiceAccountResponseModel> GetByServiceAccountIdAsync(
[FromRoute] Guid id)
[FromRoute] Guid id)
{
var userId = _userService.GetProperUserId(User).Value;
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
var authorizationResult =
await _authorizationService.AuthorizeAsync(User, serviceAccount, ServiceAccountOperations.Read);
if (serviceAccount == null)
{
throw new NotFoundException();
}
if (!_currentContext.AccessSecretsManager(serviceAccount.OrganizationId))
{
throw new NotFoundException();
}
var orgAdmin = await _currentContext.OrganizationAdmin(serviceAccount.OrganizationId);
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
var hasAccess = accessClient switch
{
AccessClientType.NoAccessCheck => true,
AccessClientType.User => await _serviceAccountRepository.UserHasWriteAccessToServiceAccount(id, userId),
_ => false,
};
if (!hasAccess)
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
@ -110,12 +95,18 @@ public class ServiceAccountsController : Controller
public async Task<ServiceAccountResponseModel> CreateAsync([FromRoute] Guid organizationId,
[FromBody] ServiceAccountCreateRequestModel createRequest)
{
if (!_currentContext.AccessSecretsManager(organizationId))
var serviceAccount = createRequest.ToServiceAccount(organizationId);
var authorizationResult =
await _authorizationService.AuthorizeAsync(User, serviceAccount, ServiceAccountOperations.Create);
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User).Value;
var result = await _createServiceAccountCommand.CreateAsync(createRequest.ToServiceAccount(organizationId), userId);
var result =
await _createServiceAccountCommand.CreateAsync(createRequest.ToServiceAccount(organizationId), userId);
return new ServiceAccountResponseModel(result);
}
@ -123,9 +114,16 @@ public class ServiceAccountsController : Controller
public async Task<ServiceAccountResponseModel> UpdateAsync([FromRoute] Guid id,
[FromBody] ServiceAccountUpdateRequestModel updateRequest)
{
var userId = _userService.GetProperUserId(User).Value;
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(id);
var authorizationResult =
await _authorizationService.AuthorizeAsync(User, serviceAccount, ServiceAccountOperations.Update);
var result = await _updateServiceAccountCommand.UpdateAsync(updateRequest.ToServiceAccount(id), userId);
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
var result = await _updateServiceAccountCommand.UpdateAsync(updateRequest.ToServiceAccount(id));
return new ServiceAccountResponseModel(result);
}