mirror of
https://github.com/bitwarden/server.git
synced 2025-07-04 01:22:50 -05:00
[SM-1150] Add secret sync endpoint (#3906)
* Add SecretsSyncQuery * Add SecretsSync to controller * Add unit tests * Add integration tests * update repo layer
This commit is contained in:
@ -9,6 +9,9 @@ using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.AuthorizationRequirements;
|
||||
using Bit.Core.SecretsManager.Commands.Secrets.Interfaces;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Models.Data;
|
||||
using Bit.Core.SecretsManager.Queries.Interfaces;
|
||||
using Bit.Core.SecretsManager.Queries.Secrets.Interfaces;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Enums;
|
||||
@ -29,6 +32,8 @@ public class SecretsController : Controller
|
||||
private readonly ICreateSecretCommand _createSecretCommand;
|
||||
private readonly IUpdateSecretCommand _updateSecretCommand;
|
||||
private readonly IDeleteSecretCommand _deleteSecretCommand;
|
||||
private readonly IAccessClientQuery _accessClientQuery;
|
||||
private readonly ISecretsSyncQuery _secretsSyncQuery;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEventService _eventService;
|
||||
private readonly IReferenceEventService _referenceEventService;
|
||||
@ -42,6 +47,8 @@ public class SecretsController : Controller
|
||||
ICreateSecretCommand createSecretCommand,
|
||||
IUpdateSecretCommand updateSecretCommand,
|
||||
IDeleteSecretCommand deleteSecretCommand,
|
||||
IAccessClientQuery accessClientQuery,
|
||||
ISecretsSyncQuery secretsSyncQuery,
|
||||
IUserService userService,
|
||||
IEventService eventService,
|
||||
IReferenceEventService referenceEventService,
|
||||
@ -54,6 +61,8 @@ public class SecretsController : Controller
|
||||
_createSecretCommand = createSecretCommand;
|
||||
_updateSecretCommand = updateSecretCommand;
|
||||
_deleteSecretCommand = deleteSecretCommand;
|
||||
_accessClientQuery = accessClientQuery;
|
||||
_secretsSyncQuery = secretsSyncQuery;
|
||||
_userService = userService;
|
||||
_eventService = eventService;
|
||||
_referenceEventService = referenceEventService;
|
||||
@ -73,7 +82,7 @@ public class SecretsController : Controller
|
||||
var orgAdmin = await _currentContext.OrganizationAdmin(organizationId);
|
||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||
|
||||
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(organizationId, userId, accessClient);
|
||||
var secrets = await _secretRepository.GetManyDetailsByOrganizationIdAsync(organizationId, userId, accessClient);
|
||||
|
||||
return new SecretWithProjectsListResponseModel(secrets);
|
||||
}
|
||||
@ -139,7 +148,7 @@ public class SecretsController : Controller
|
||||
var orgAdmin = await _currentContext.OrganizationAdmin(project.OrganizationId);
|
||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.ClientType, orgAdmin);
|
||||
|
||||
var secrets = await _secretRepository.GetManyByProjectIdAsync(projectId, userId, accessClient);
|
||||
var secrets = await _secretRepository.GetManyDetailsByProjectIdAsync(projectId, userId, accessClient);
|
||||
|
||||
return new SecretWithProjectsListResponseModel(secrets);
|
||||
}
|
||||
@ -246,4 +255,35 @@ public class SecretsController : Controller
|
||||
var responses = secrets.Select(s => new BaseSecretResponseModel(s));
|
||||
return new ListResponseModel<BaseSecretResponseModel>(responses);
|
||||
}
|
||||
|
||||
[HttpGet("/organizations/{organizationId}/secrets/sync")]
|
||||
public async Task<SecretsSyncResponseModel> GetSecretsSyncAsync([FromRoute] Guid organizationId,
|
||||
[FromQuery] DateTime? lastSyncedDate = null)
|
||||
{
|
||||
if (lastSyncedDate.HasValue && lastSyncedDate.Value > DateTime.UtcNow)
|
||||
{
|
||||
throw new BadRequestException("Last synced date must be in the past.");
|
||||
}
|
||||
|
||||
if (!_currentContext.AccessSecretsManager(organizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var (accessClient, serviceAccountId) = await _accessClientQuery.GetAccessClientAsync(User, organizationId);
|
||||
if (accessClient != AccessClientType.ServiceAccount)
|
||||
{
|
||||
throw new BadRequestException("Only service accounts can sync secrets.");
|
||||
}
|
||||
|
||||
var syncRequest = new SecretsSyncRequest
|
||||
{
|
||||
AccessClientType = accessClient,
|
||||
OrganizationId = organizationId,
|
||||
ServiceAccountId = serviceAccountId,
|
||||
LastSyncedDate = lastSyncedDate
|
||||
};
|
||||
var (hasChanges, secrets) = await _secretsSyncQuery.GetAsync(syncRequest);
|
||||
return new SecretsSyncResponseModel(hasChanges, secrets);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public class SecretsManagerPortingController : Controller
|
||||
|
||||
var userId = _userService.GetProperUserId(User).Value;
|
||||
var projects = await _projectRepository.GetManyByOrganizationIdAsync(organizationId, userId, AccessClientType.NoAccessCheck);
|
||||
var secrets = await _secretRepository.GetManyByOrganizationIdAsync(organizationId, userId, AccessClientType.NoAccessCheck);
|
||||
var secrets = await _secretRepository.GetManyDetailsByOrganizationIdAsync(organizationId, userId, AccessClientType.NoAccessCheck);
|
||||
|
||||
if (projects == null && secrets == null)
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ public class TrashController : Controller
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
var secrets = await _secretRepository.GetManyByOrganizationIdInTrashAsync(organizationId);
|
||||
var secrets = await _secretRepository.GetManyDetailsByOrganizationIdInTrashAsync(organizationId);
|
||||
return new SecretWithProjectsListResponseModel(secrets);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
#nullable enable
|
||||
using Bit.Api.Models.Response;
|
||||
using Bit.Core.Models.Api;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
|
||||
namespace Bit.Api.SecretsManager.Models.Response;
|
||||
|
||||
public class SecretsSyncResponseModel : ResponseModel
|
||||
{
|
||||
private const string _objectName = "secretsSync";
|
||||
|
||||
public bool HasChanges { get; set; }
|
||||
public ListResponseModel<BaseSecretResponseModel>? Secrets { get; set; }
|
||||
|
||||
public SecretsSyncResponseModel(bool hasChanges, IEnumerable<Secret>? secrets, string obj = _objectName)
|
||||
: base(obj)
|
||||
{
|
||||
Secrets = secrets != null
|
||||
? new ListResponseModel<BaseSecretResponseModel>(secrets.Select(s => new BaseSecretResponseModel(s)))
|
||||
: null;
|
||||
HasChanges = hasChanges;
|
||||
}
|
||||
|
||||
public SecretsSyncResponseModel() : base(_objectName)
|
||||
{
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user