1
0
mirror of https://github.com/bitwarden/server synced 2025-12-11 05:43:35 +00:00

[SM-1273] Adding new logging for secrets (#5991)

* Adding new logging for secrets

* fixing secrest controller tests

* fixing the tests
This commit is contained in:
cd-bitwarden
2025-07-02 22:28:48 -04:00
committed by GitHub
parent b7df8525af
commit 669a5cb372
6 changed files with 73 additions and 21 deletions

View File

@@ -109,7 +109,7 @@ public class SecretsController : Controller
} }
var result = await _createSecretCommand.CreateAsync(secret, accessPoliciesUpdates); var result = await _createSecretCommand.CreateAsync(secret, accessPoliciesUpdates);
await LogSecretEventAsync(secret, EventType.Secret_Created);
// Creating a secret means you have read & write permission. // Creating a secret means you have read & write permission.
return new SecretResponseModel(result, true, true); return new SecretResponseModel(result, true, true);
} }
@@ -135,10 +135,7 @@ public class SecretsController : Controller
throw new NotFoundException(); throw new NotFoundException();
} }
if (_currentContext.IdentityClientType == IdentityClientType.ServiceAccount) await LogSecretEventAsync(secret, EventType.Secret_Retrieved);
{
await _eventService.LogServiceAccountSecretEventAsync(userId, secret, EventType.Secret_Retrieved);
}
return new SecretResponseModel(secret, access.Read, access.Write); return new SecretResponseModel(secret, access.Read, access.Write);
} }
@@ -188,10 +185,10 @@ public class SecretsController : Controller
{ {
throw new NotFoundException(); throw new NotFoundException();
} }
} }
var result = await _updateSecretCommand.UpdateAsync(updatedSecret, accessPoliciesUpdates); var result = await _updateSecretCommand.UpdateAsync(updatedSecret, accessPoliciesUpdates);
await LogSecretEventAsync(secret, EventType.Secret_Edited);
// Updating a secret means you have read & write permission. // Updating a secret means you have read & write permission.
return new SecretResponseModel(result, true, true); return new SecretResponseModel(result, true, true);
@@ -234,6 +231,7 @@ public class SecretsController : Controller
await _deleteSecretCommand.DeleteSecrets(secretsToDelete); await _deleteSecretCommand.DeleteSecrets(secretsToDelete);
var responses = results.Select(r => new BulkDeleteResponseModel(r.Secret.Id, r.Error)); var responses = results.Select(r => new BulkDeleteResponseModel(r.Secret.Id, r.Error));
await LogSecretsEventAsync(secretsToDelete, EventType.Secret_Deleted);
return new ListResponseModel<BulkDeleteResponseModel>(responses); return new ListResponseModel<BulkDeleteResponseModel>(responses);
} }
@@ -253,7 +251,7 @@ public class SecretsController : Controller
throw new NotFoundException(); throw new NotFoundException();
} }
await LogSecretsRetrievalAsync(secrets); await LogSecretsEventAsync(secrets, EventType.Secret_Retrieved);
var responses = secrets.Select(s => new BaseSecretResponseModel(s)); var responses = secrets.Select(s => new BaseSecretResponseModel(s));
return new ListResponseModel<BaseSecretResponseModel>(responses); return new ListResponseModel<BaseSecretResponseModel>(responses);
@@ -290,18 +288,28 @@ public class SecretsController : Controller
if (syncResult.HasChanges) if (syncResult.HasChanges)
{ {
await LogSecretsRetrievalAsync(syncResult.Secrets); await LogSecretsEventAsync(syncResult.Secrets, EventType.Secret_Retrieved);
} }
return new SecretsSyncResponseModel(syncResult.HasChanges, syncResult.Secrets); return new SecretsSyncResponseModel(syncResult.HasChanges, syncResult.Secrets);
} }
private async Task LogSecretsRetrievalAsync(IEnumerable<Secret> secrets) private async Task LogSecretsEventAsync(IEnumerable<Secret> secrets, EventType eventType)
{ {
if (_currentContext.IdentityClientType == IdentityClientType.ServiceAccount) var userId = _userService.GetProperUserId(User)!.Value;
switch (_currentContext.IdentityClientType)
{ {
var userId = _userService.GetProperUserId(User)!.Value; case IdentityClientType.ServiceAccount:
await _eventService.LogServiceAccountSecretsEventAsync(userId, secrets, EventType.Secret_Retrieved); 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);
} }

View File

@@ -90,4 +90,7 @@ public enum EventType : int
OrganizationDomain_NotVerified = 2003, OrganizationDomain_NotVerified = 2003,
Secret_Retrieved = 2100, Secret_Retrieved = 2100,
Secret_Created = 2101,
Secret_Edited = 2102,
Secret_Deleted = 2103,
} }

View File

@@ -30,6 +30,6 @@ public interface IEventService
Task LogProviderOrganizationEventsAsync(IEnumerable<(ProviderOrganization, EventType, DateTime?)> events); Task LogProviderOrganizationEventsAsync(IEnumerable<(ProviderOrganization, EventType, DateTime?)> events);
Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, DateTime? date = null); Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, DateTime? date = null);
Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, EventSystemUser systemUser, DateTime? date = null); Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, EventSystemUser systemUser, DateTime? date = null);
Task LogServiceAccountSecretEventAsync(Guid serviceAccountId, Secret secret, EventType type, DateTime? date = null); Task LogUserSecretsEventAsync(Guid userId, IEnumerable<Secret> secrets, EventType type, DateTime? date = null);
Task LogServiceAccountSecretsEventAsync(Guid serviceAccountId, IEnumerable<Secret> secrets, EventType type, DateTime? date = null); Task LogServiceAccountSecretsEventAsync(Guid serviceAccountId, IEnumerable<Secret> secrets, EventType type, DateTime? date = null);
} }

View File

@@ -409,9 +409,30 @@ public class EventService : IEventService
await _eventWriteService.CreateAsync(e); await _eventWriteService.CreateAsync(e);
} }
public async Task LogServiceAccountSecretEventAsync(Guid serviceAccountId, Secret secret, EventType type, DateTime? date = null) public async Task LogUserSecretsEventAsync(Guid userId, IEnumerable<Secret> secrets, EventType type, DateTime? date = null)
{ {
await LogServiceAccountSecretsEventAsync(serviceAccountId, new[] { secret }, type, date); var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
var eventMessages = new List<IEvent>();
foreach (var secret in secrets)
{
if (!CanUseEvents(orgAbilities, secret.OrganizationId))
{
continue;
}
var e = new EventMessage(_currentContext)
{
OrganizationId = secret.OrganizationId,
Type = type,
SecretId = secret.Id,
UserId = userId,
Date = date.GetValueOrDefault(DateTime.UtcNow)
};
eventMessages.Add(e);
}
await _eventWriteService.CreateManyAsync(eventMessages);
} }
public async Task LogServiceAccountSecretsEventAsync(Guid serviceAccountId, IEnumerable<Secret> secrets, EventType type, DateTime? date = null) public async Task LogServiceAccountSecretsEventAsync(Guid serviceAccountId, IEnumerable<Secret> secrets, EventType type, DateTime? date = null)

View File

@@ -116,7 +116,7 @@ public class NoopEventService : IEventService
return Task.FromResult(0); return Task.FromResult(0);
} }
public Task LogServiceAccountSecretEventAsync(Guid serviceAccountId, Secret secret, EventType type, public Task LogUserSecretsEventAsync(Guid userId, IEnumerable<Secret> secrets, EventType type,
DateTime? date = null) DateTime? date = null)
{ {
return Task.FromResult(0); return Task.FromResult(0);

View File

@@ -19,6 +19,7 @@ using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
using Bit.Test.Common.Helpers; using Bit.Test.Common.Helpers;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using NSubstitute; using NSubstitute;
using Xunit; using Xunit;
@@ -152,6 +153,7 @@ public class SecretsControllerTests
SecretCreateRequestModel data, Guid organizationId) SecretCreateRequestModel data, Guid organizationId)
{ {
data = SetupSecretCreateRequest(sutProvider, data, organizationId); data = SetupSecretCreateRequest(sutProvider, data, organizationId);
SetControllerUser(sutProvider, new Guid());
await sutProvider.Sut.CreateAsync(organizationId, data); await sutProvider.Sut.CreateAsync(organizationId, data);
@@ -186,6 +188,7 @@ public class SecretsControllerTests
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<SecretAccessPoliciesUpdates>(), .AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<SecretAccessPoliciesUpdates>(),
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Success()); Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Success());
SetControllerUser(sutProvider, new Guid());
await sutProvider.Sut.CreateAsync(organizationId, data); await sutProvider.Sut.CreateAsync(organizationId, data);
@@ -199,6 +202,7 @@ public class SecretsControllerTests
SecretUpdateRequestModel data, Secret currentSecret) SecretUpdateRequestModel data, Secret currentSecret)
{ {
data = SetupSecretUpdateRequest(data); data = SetupSecretUpdateRequest(data);
sutProvider.GetDependency<IAuthorizationService>() sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<Secret>(), .AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<Secret>(),
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed()); Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Failed());
@@ -239,7 +243,7 @@ public class SecretsControllerTests
SecretUpdateRequestModel data, Secret currentSecret) SecretUpdateRequestModel data, Secret currentSecret)
{ {
data = SetupSecretUpdateRequest(data); data = SetupSecretUpdateRequest(data);
SetControllerUser(sutProvider, new Guid());
sutProvider.GetDependency<IAuthorizationService>() sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<Secret>(), .AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<Secret>(),
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success()); Arg.Any<IEnumerable<IAuthorizationRequirement>>()).ReturnsForAnyArgs(AuthorizationResult.Success());
@@ -260,7 +264,6 @@ public class SecretsControllerTests
SecretUpdateRequestModel data, Secret currentSecret, SecretAccessPoliciesUpdates accessPoliciesUpdates) SecretUpdateRequestModel data, Secret currentSecret, SecretAccessPoliciesUpdates accessPoliciesUpdates)
{ {
data = SetupSecretUpdateAccessPoliciesRequest(sutProvider, data, currentSecret, accessPoliciesUpdates); data = SetupSecretUpdateAccessPoliciesRequest(sutProvider, data, currentSecret, accessPoliciesUpdates);
sutProvider.GetDependency<IAuthorizationService>() sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<SecretAccessPoliciesUpdates>(), .AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), Arg.Any<SecretAccessPoliciesUpdates>(),
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Failed()); Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Failed());
@@ -339,6 +342,8 @@ public class SecretsControllerTests
{ {
var ids = data.Select(s => s.Id).ToList(); var ids = data.Select(s => s.Id).ToList();
var organizationId = data.First().OrganizationId; var organizationId = data.First().OrganizationId;
SetControllerUser(sutProvider, new Guid());
foreach (var secret in data) foreach (var secret in data)
{ {
secret.OrganizationId = organizationId; secret.OrganizationId = organizationId;
@@ -378,7 +383,7 @@ public class SecretsControllerTests
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true); sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true);
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data); sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
SetControllerUser(sutProvider, new Guid());
var results = await sutProvider.Sut.BulkDeleteAsync(ids); var results = await sutProvider.Sut.BulkDeleteAsync(ids);
await sutProvider.GetDependency<IDeleteSecretCommand>().Received(1) await sutProvider.GetDependency<IDeleteSecretCommand>().Received(1)
@@ -434,7 +439,7 @@ public class SecretsControllerTests
{ {
var (ids, request) = BuildGetSecretsRequestModel(data); var (ids, request) = BuildGetSecretsRequestModel(data);
var organizationId = SetOrganizations(ref data); var organizationId = SetOrganizations(ref data);
SetControllerUser(sutProvider, new Guid());
sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data); sutProvider.GetDependency<ISecretRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
sutProvider.GetDependency<IAuthorizationService>() sutProvider.GetDependency<IAuthorizationService>()
.AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data, .AuthorizeAsync(Arg.Any<ClaimsPrincipal>(), data,
@@ -507,7 +512,7 @@ public class SecretsControllerTests
SutProvider<SecretsController> sutProvider, Guid organizationId) SutProvider<SecretsController> sutProvider, Guid organizationId)
{ {
var lastSyncedDate = SetupSecretsSyncRequest(nullLastSyncedDate, secrets, sutProvider, organizationId); var lastSyncedDate = SetupSecretsSyncRequest(nullLastSyncedDate, secrets, sutProvider, organizationId);
SetControllerUser(sutProvider, new Guid());
var result = await sutProvider.Sut.GetSecretsSyncAsync(organizationId, lastSyncedDate); var result = await sutProvider.Sut.GetSecretsSyncAsync(organizationId, lastSyncedDate);
Assert.True(result.HasChanges); Assert.True(result.HasChanges);
Assert.NotNull(result.Secrets); Assert.NotNull(result.Secrets);
@@ -610,4 +615,19 @@ public class SecretsControllerTests
.ReturnsForAnyArgs(data.ToSecret(currentSecret)); .ReturnsForAnyArgs(data.ToSecret(currentSecret));
return data; return data;
} }
private static void SetControllerUser(SutProvider<SecretsController> sutProvider, Guid userId)
{
var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, userId.ToString()) };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
sutProvider.Sut.ControllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext
{
HttpContext = new DefaultHttpContext { User = principal }
};
sutProvider.GetDependency<IUserService>().GetProperUserId(principal).Returns(userId);
}
} }