mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 23:52:50 -05:00
[SM-863] Add endpoint for fetching multiple secrets by IDs (#3134)
* Add support CanReadSecret authorization * Extract base response model for secret * Add support for SA bulk fetching event logging * secret repository bug fix * Add endpoint and request for bulk fetching secrets * Swap to original reference event * Add unit tests * Add integration tests * Add unit tests for authz handler * update authz handler tests ---------
This commit is contained in:
@ -207,4 +207,45 @@ public class SecretsController : Controller
|
||||
var responses = results.Select(r => new BulkDeleteResponseModel(r.Secret.Id, r.Error));
|
||||
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpPost("secrets/get-by-ids")]
|
||||
public async Task<ListResponseModel<BaseSecretResponseModel>> GetSecretsByIdsAsync(
|
||||
[FromBody] GetSecretsRequestModel request)
|
||||
{
|
||||
var secrets = (await _secretRepository.GetManyByIds(request.Ids)).ToList();
|
||||
if (!secrets.Any() || secrets.Count != request.Ids.Count())
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
// Ensure all secrets belong to the same organization.
|
||||
var organizationId = secrets.First().OrganizationId;
|
||||
if (secrets.Any(secret => secret.OrganizationId != organizationId) ||
|
||||
!_currentContext.AccessSecretsManager(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
|
||||
foreach (var secret in secrets)
|
||||
{
|
||||
var authorizationResult = await _authorizationService.AuthorizeAsync(User, secret, SecretOperations.Read);
|
||||
if (!authorizationResult.Succeeded)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentContext.ClientType == ClientType.ServiceAccount)
|
||||
{
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var org = await _organizationRepository.GetByIdAsync(organizationId);
|
||||
await _eventService.LogServiceAccountSecretsEventAsync(userId, secrets, EventType.Secret_Retrieved);
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.SmServiceAccountAccessedSecret, org, _currentContext));
|
||||
}
|
||||
|
||||
var responses = secrets.Select(s => new BaseSecretResponseModel(s));
|
||||
return new ListResponseModel<BaseSecretResponseModel>(responses);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Models.Request;
|
||||
|
||||
public class GetSecretsRequestModel
|
||||
{
|
||||
[Required]
|
||||
public IEnumerable<Guid> Ids { get; set; }
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Models.Response;
|
||||
|
||||
public class BaseSecretResponseModel : ResponseModel
|
||||
{
|
||||
private const string _objectName = "baseSecret";
|
||||
|
||||
public BaseSecretResponseModel(Secret secret, string objectName = _objectName) : base(objectName)
|
||||
{
|
||||
if (secret == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(secret));
|
||||
}
|
||||
|
||||
Id = secret.Id;
|
||||
OrganizationId = secret.OrganizationId;
|
||||
Key = secret.Key;
|
||||
Value = secret.Value;
|
||||
Note = secret.Note;
|
||||
CreationDate = secret.CreationDate;
|
||||
RevisionDate = secret.RevisionDate;
|
||||
Projects = secret.Projects?.Select(p => new SecretResponseInnerProject(p));
|
||||
}
|
||||
|
||||
public BaseSecretResponseModel(string objectName = _objectName) : base(objectName)
|
||||
{
|
||||
}
|
||||
|
||||
public BaseSecretResponseModel() : base(_objectName)
|
||||
{
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid OrganizationId { get; set; }
|
||||
|
||||
public string Key { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public string Note { get; set; }
|
||||
|
||||
public DateTime CreationDate { get; set; }
|
||||
|
||||
public DateTime RevisionDate { get; set; }
|
||||
|
||||
public IEnumerable<SecretResponseInnerProject> Projects { get; set; }
|
||||
|
||||
public class SecretResponseInnerProject
|
||||
{
|
||||
public SecretResponseInnerProject(Project project)
|
||||
{
|
||||
Id = project.Id;
|
||||
Name = project.Name;
|
||||
}
|
||||
|
||||
public SecretResponseInnerProject()
|
||||
{
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
@ -1,28 +1,13 @@
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Models.Response;
|
||||
|
||||
public class SecretResponseModel : ResponseModel
|
||||
public class SecretResponseModel : BaseSecretResponseModel
|
||||
{
|
||||
private const string _objectName = "secret";
|
||||
|
||||
public SecretResponseModel(Secret secret, bool read, bool write) : base(_objectName)
|
||||
public SecretResponseModel(Secret secret, bool read, bool write) : base(secret, _objectName)
|
||||
{
|
||||
if (secret == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(secret));
|
||||
}
|
||||
|
||||
Id = secret.Id;
|
||||
OrganizationId = secret.OrganizationId;
|
||||
Key = secret.Key;
|
||||
Value = secret.Value;
|
||||
Note = secret.Note;
|
||||
CreationDate = secret.CreationDate;
|
||||
RevisionDate = secret.RevisionDate;
|
||||
Projects = secret.Projects?.Select(p => new SecretResponseInnerProject(p));
|
||||
|
||||
Read = read;
|
||||
Write = write;
|
||||
}
|
||||
@ -31,39 +16,7 @@ public class SecretResponseModel : ResponseModel
|
||||
{
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public Guid OrganizationId { get; set; }
|
||||
|
||||
public string Key { get; set; }
|
||||
|
||||
public string Value { get; set; }
|
||||
|
||||
public string Note { get; set; }
|
||||
|
||||
public DateTime CreationDate { get; set; }
|
||||
|
||||
public DateTime RevisionDate { get; set; }
|
||||
|
||||
public IEnumerable<SecretResponseInnerProject> Projects { get; set; }
|
||||
|
||||
public bool Read { get; set; }
|
||||
|
||||
public bool Write { get; set; }
|
||||
|
||||
public class SecretResponseInnerProject
|
||||
{
|
||||
public SecretResponseInnerProject(Project project)
|
||||
{
|
||||
Id = project.Id;
|
||||
Name = project.Name;
|
||||
}
|
||||
|
||||
public SecretResponseInnerProject()
|
||||
{
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user