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

[SM-706] Extract Authorization From Create/Update Secret Commands (#2896)

* Extract authorization from commands

* Swap to request model validation.

* Swap to pattern detection
This commit is contained in:
Thomas Avery
2023-06-08 16:40:35 -05:00
committed by GitHub
parent 6a9e7a1d0a
commit 05f11a8ee1
15 changed files with 674 additions and 231 deletions

View File

@ -6,6 +6,7 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Identity;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.AuthorizationRequirements;
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services;
@ -32,6 +33,7 @@ public class SecretsController : Controller
private readonly IUserService _userService;
private readonly IEventService _eventService;
private readonly IReferenceEventService _referenceEventService;
private readonly IAuthorizationService _authorizationService;
public SecretsController(
ICurrentContext currentContext,
@ -43,7 +45,8 @@ public class SecretsController : Controller
IDeleteSecretCommand deleteSecretCommand,
IUserService userService,
IEventService eventService,
IReferenceEventService referenceEventService)
IReferenceEventService referenceEventService,
IAuthorizationService authorizationService)
{
_currentContext = currentContext;
_projectRepository = projectRepository;
@ -55,6 +58,7 @@ public class SecretsController : Controller
_userService = userService;
_eventService = eventService;
_referenceEventService = referenceEventService;
_authorizationService = authorizationService;
}
@ -78,18 +82,14 @@ public class SecretsController : Controller
[HttpPost("organizations/{organizationId}/secrets")]
public async Task<SecretResponseModel> CreateAsync([FromRoute] Guid organizationId, [FromBody] SecretCreateRequestModel createRequest)
{
if (!_currentContext.AccessSecretsManager(organizationId))
var secret = createRequest.ToSecret(organizationId);
var authorizationResult = await _authorizationService.AuthorizeAsync(User, secret, SecretOperations.Create);
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
if (createRequest.ProjectIds != null && createRequest.ProjectIds.Length > 1)
{
throw new BadRequestException();
}
var userId = _userService.GetProperUserId(User).Value;
var result = await _createSecretCommand.CreateAsync(createRequest.ToSecret(organizationId), userId);
var result = await _createSecretCommand.CreateAsync(secret);
// Creating a secret means you have read & write permission.
return new SecretResponseModel(result, true, true);
@ -148,14 +148,20 @@ public class SecretsController : Controller
[HttpPut("secrets/{id}")]
public async Task<SecretResponseModel> UpdateSecretAsync([FromRoute] Guid id, [FromBody] SecretUpdateRequestModel updateRequest)
{
if (updateRequest.ProjectIds != null && updateRequest.ProjectIds.Length > 1)
var secret = await _secretRepository.GetByIdAsync(id);
if (secret == null)
{
throw new BadRequestException();
throw new NotFoundException();
}
var userId = _userService.GetProperUserId(User).Value;
var secret = updateRequest.ToSecret(id);
var result = await _updateSecretCommand.UpdateAsync(secret, userId);
var updatedSecret = updateRequest.ToSecret(id, secret.OrganizationId);
var authorizationResult = await _authorizationService.AuthorizeAsync(User, updatedSecret, SecretOperations.Update);
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
var result = await _updateSecretCommand.UpdateAsync(updatedSecret);
// Updating a secret means you have read & write permission.
return new SecretResponseModel(result, true, true);

View File

@ -4,7 +4,7 @@ using Bit.Core.Utilities;
namespace Bit.Api.SecretsManager.Models.Request;
public class SecretCreateRequestModel
public class SecretCreateRequestModel : IValidatableObject
{
[Required]
[EncryptedString]
@ -32,4 +32,14 @@ public class SecretCreateRequestModel
Projects = ProjectIds != null && ProjectIds.Any() ? ProjectIds.Select(x => new Project() { Id = x }).ToList() : null,
};
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ProjectIds is { Length: > 1 })
{
yield return new ValidationResult(
$"Only one project assignment is supported.",
new[] { nameof(ProjectIds) });
}
}
}

View File

@ -4,7 +4,7 @@ using Bit.Core.Utilities;
namespace Bit.Api.SecretsManager.Models.Request;
public class SecretUpdateRequestModel
public class SecretUpdateRequestModel : IValidatableObject
{
[Required]
[EncryptedString]
@ -20,11 +20,12 @@ public class SecretUpdateRequestModel
public Guid[] ProjectIds { get; set; }
public Secret ToSecret(Guid id)
public Secret ToSecret(Guid id, Guid organizationId)
{
return new Secret()
{
Id = id,
OrganizationId = organizationId,
Key = Key,
Value = Value,
Note = Note,
@ -32,4 +33,14 @@ public class SecretUpdateRequestModel
Projects = ProjectIds != null && ProjectIds.Any() ? ProjectIds.Select(x => new Project() { Id = x }).ToList() : null,
};
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ProjectIds is { Length: > 1 })
{
yield return new ValidationResult(
$"Only one project assignment is supported.",
new[] { nameof(ProjectIds) });
}
}
}