Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/Api/SecretsManager/Controllers/SecretVersionsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.SecretsManager.Repositories;
using Bit.Core.Services;
using Microsoft.AspNetCore.Authorization;
Expand All @@ -21,19 +22,22 @@ public class SecretVersionsController : Controller
private readonly ISecretRepository _secretRepository;
private readonly IUserService _userService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IEventService _eventService;

public SecretVersionsController(
ICurrentContext currentContext,
ISecretVersionRepository secretVersionRepository,
ISecretRepository secretRepository,
IUserService userService,
IOrganizationUserRepository organizationUserRepository)
IOrganizationUserRepository organizationUserRepository,
IEventService eventService)
{
_currentContext = currentContext;
_secretVersionRepository = secretVersionRepository;
_secretRepository = secretRepository;
_userService = userService;
_organizationUserRepository = organizationUserRepository;
_eventService = eventService;
}

[HttpGet("secrets/{secretId}/versions")]
Expand All @@ -51,7 +55,7 @@ public async Task<ListResponseModel<SecretVersionResponseModel>> GetVersionsBySe
{
// Already verified Secrets Manager access above
var versionList = await _secretVersionRepository.GetManyBySecretIdAsync(secretId);
var responseList = versionList.Select(v => new SecretVersionResponseModel(v));
var responseList = versionList.Select(v => new SecretVersionResponseModel(v)).ToList();
return new ListResponseModel<SecretVersionResponseModel>(responseList);
}

Expand All @@ -71,7 +75,7 @@ public async Task<ListResponseModel<SecretVersionResponseModel>> GetVersionsBySe
}

var versions = await _secretVersionRepository.GetManyBySecretIdAsync(secretId);
var responses = versions.Select(v => new SecretVersionResponseModel(v));
var responses = versions.Select(v => new SecretVersionResponseModel(v)).ToList();

return new ListResponseModel<SecretVersionResponseModel>(responses);
}
Expand Down Expand Up @@ -201,6 +205,7 @@ public async Task<SecretResponseModel> RestoreVersionAsync([FromRoute] Guid secr

// Store the current value before restoration
var currentValue = secret.Value;
var currentValueRevisionDate = secret.RevisionDate;

// For service accounts and organization API, skip user-level access checks
if (_currentContext.IdentityClientType == IdentityClientType.ServiceAccount)
Expand All @@ -215,7 +220,7 @@ public async Task<SecretResponseModel> RestoreVersionAsync([FromRoute] Guid secr
{
SecretId = secretId,
Value = currentValue!,
VersionDate = DateTime.UtcNow,
VersionDate = currentValueRevisionDate,
EditorServiceAccountId = editorUserId.Value
};

Expand All @@ -227,6 +232,7 @@ public async Task<SecretResponseModel> RestoreVersionAsync([FromRoute] Guid secr
secret.Value = version.Value;
secret.RevisionDate = DateTime.UtcNow;
var updatedSec = await _secretRepository.UpdateAsync(secret);
await LogSecretEventAsync(updatedSec, EventType.Secret_Edited);
return new SecretResponseModel(updatedSec, true, true);
}

Expand Down Expand Up @@ -258,7 +264,7 @@ public async Task<SecretResponseModel> RestoreVersionAsync([FromRoute] Guid secr
{
SecretId = secretId,
Value = currentValue!,
VersionDate = DateTime.UtcNow,
VersionDate = currentValueRevisionDate,
EditorOrganizationUserId = orgUser.Id
};

Expand All @@ -270,6 +276,7 @@ public async Task<SecretResponseModel> RestoreVersionAsync([FromRoute] Guid secr
secret.RevisionDate = DateTime.UtcNow;

var updatedSecret = await _secretRepository.UpdateAsync(secret);
await LogSecretEventAsync(updatedSecret, EventType.Secret_Edited);

return new SecretResponseModel(updatedSecret, true, true);
}
Expand Down Expand Up @@ -334,4 +341,22 @@ public async Task<IActionResult> BulkDeleteAsync([FromBody] List<Guid> ids)

return Ok();
}

private async Task LogSecretsEventAsync(IEnumerable<Secret> secrets, EventType eventType)
{
var userId = _userService.GetProperUserId(User)!.Value;

switch (_currentContext.IdentityClientType)
{
case IdentityClientType.ServiceAccount:
await _eventService.LogServiceAccountSecretsEventAsync(userId, secrets, eventType);
break;
case IdentityClientType.User:
await _eventService.LogUserSecretsEventAsync(userId, secrets, eventType);
break;
}
}

private Task LogSecretEventAsync(Secret secret, EventType eventType) =>
LogSecretsEventAsync(new[] { secret }, eventType);
}
18 changes: 11 additions & 7 deletions src/Api/SecretsManager/Controllers/SecretsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ public async Task<SecretResponseModel> UpdateSecretAsync([FromRoute] Guid id, [F
throw new NotFoundException();
}

var originalValue = secret.Value;
var valueRevisionDate = secret.RevisionDate;

var updatedSecret = updateRequest.ToSecret(secret);
var authorizationResult = await _authorizationService.AuthorizeAsync(User, updatedSecret, SecretOperations.Update);
if (!authorizationResult.Succeeded)
Expand All @@ -200,8 +203,6 @@ public async Task<SecretResponseModel> UpdateSecretAsync([FromRoute] Guid id, [F
// Create a version record if the value changed
if (updateRequest.ValueChanged)
{
// Store the old value before updating
var oldValue = secret.Value;
var userId = _userService.GetProperUserId(User)!.Value;
Guid? editorServiceAccountId = null;
Guid? editorOrganizationUserId = null;
Expand All @@ -217,21 +218,24 @@ public async Task<SecretResponseModel> UpdateSecretAsync([FromRoute] Guid id, [F
{
editorOrganizationUserId = orgUser.Id;
}
else
}
else if (_currentContext.IdentityClientType == IdentityClientType.Organization)
{
var orgUser = await _organizationUserRepository.GetByOrganizationAsync(secret.OrganizationId, userId);
if (orgUser != null)
{
throw new NotFoundException();
editorOrganizationUserId = orgUser.Id;
}
}

var secretVersion = new SecretVersion
{
SecretId = id,
Value = oldValue,
VersionDate = DateTime.UtcNow,
Value = originalValue,
VersionDate = valueRevisionDate,
EditorServiceAccountId = editorServiceAccountId,
EditorOrganizationUserId = editorOrganizationUserId
};

await _secretVersionRepository.CreateAsync(secretVersion);
}

Expand Down
Loading