diff --git a/src/Api/AdminConsole/Models/Response/EventResponseModel.cs b/src/Api/AdminConsole/Models/Response/EventResponseModel.cs
index 68695b3ab8..779dc44823 100644
--- a/src/Api/AdminConsole/Models/Response/EventResponseModel.cs
+++ b/src/Api/AdminConsole/Models/Response/EventResponseModel.cs
@@ -1,4 +1,6 @@
-using Bit.Core.Enums;
+#nullable enable
+
+using Bit.Core.Enums;
using Bit.Core.Models.Api;
using Bit.Core.Models.Data;
@@ -34,6 +36,7 @@ public class EventResponseModel : ResponseModel
DomainName = ev.DomainName;
SecretId = ev.SecretId;
ServiceAccountId = ev.ServiceAccountId;
+ SecretIds = ev.SecretIds;
}
public EventType Type { get; set; }
@@ -56,4 +59,5 @@ public class EventResponseModel : ResponseModel
public string DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ServiceAccountId { get; set; }
+ public string? SecretIds { get; set; }
}
diff --git a/src/Api/AdminConsole/Public/Models/Response/EventResponseModel.cs b/src/Api/AdminConsole/Public/Models/Response/EventResponseModel.cs
index 0609a4d782..6c10eff58f 100644
--- a/src/Api/AdminConsole/Public/Models/Response/EventResponseModel.cs
+++ b/src/Api/AdminConsole/Public/Models/Response/EventResponseModel.cs
@@ -1,4 +1,6 @@
-using System.ComponentModel.DataAnnotations;
+#nullable enable
+
+using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
@@ -29,6 +31,7 @@ public class EventResponseModel : IResponseModel
InstallationId = ev.InstallationId;
SecretId = ev.SecretId;
ServiceAccountId = ev.ServiceAccountId;
+ SecretIds = ev.SecretIds;
}
///
@@ -101,4 +104,6 @@ public class EventResponseModel : IResponseModel
///
/// e68b8629-85eb-4929-92c0-b84464976ba4
public Guid? ServiceAccountId { get; set; }
+
+ public string? SecretIds { get; set; }
}
diff --git a/src/Api/SecretsManager/Controllers/SecretsController.cs b/src/Api/SecretsManager/Controllers/SecretsController.cs
index 519bc328fa..2abcc78ebc 100644
--- a/src/Api/SecretsManager/Controllers/SecretsController.cs
+++ b/src/Api/SecretsManager/Controllers/SecretsController.cs
@@ -231,7 +231,7 @@ public class SecretsController : Controller
await _deleteSecretCommand.DeleteSecrets(secretsToDelete);
var responses = results.Select(r => new BulkDeleteResponseModel(r.Secret.Id, r.Error));
- await LogSecretsEventAsync(secretsToDelete, EventType.Secret_Deleted);
+ await LogSecretsEventAsync(secretsToDelete, EventType.Secrets_Deleted_Bulk);
return new ListResponseModel(responses);
}
@@ -251,7 +251,7 @@ public class SecretsController : Controller
throw new NotFoundException();
}
- await LogSecretsEventAsync(secrets, EventType.Secret_Retrieved);
+ await LogSecretsEventAsync(secrets, EventType.Secrets_Retrieved_Bulk);
var responses = secrets.Select(s => new BaseSecretResponseModel(s));
return new ListResponseModel(responses);
diff --git a/src/Core/AdminConsole/Entities/Event.cs b/src/Core/AdminConsole/Entities/Event.cs
index 2a6b6664c2..6d6e9a9d30 100644
--- a/src/Core/AdminConsole/Entities/Event.cs
+++ b/src/Core/AdminConsole/Entities/Event.cs
@@ -33,6 +33,7 @@ public class Event : ITableObject, IEvent
DomainName = e.DomainName;
SecretId = e.SecretId;
ServiceAccountId = e.ServiceAccountId;
+ SecretIds = e.SecretIds;
}
public Guid Id { get; set; }
@@ -57,6 +58,7 @@ public class Event : ITableObject, IEvent
public string? DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ServiceAccountId { get; set; }
+ public string? SecretIds { get; set; }
public void SetNewId()
{
diff --git a/src/Core/AdminConsole/Enums/EventType.cs b/src/Core/AdminConsole/Enums/EventType.cs
index 2359b922d8..7dc06fa666 100644
--- a/src/Core/AdminConsole/Enums/EventType.cs
+++ b/src/Core/AdminConsole/Enums/EventType.cs
@@ -93,4 +93,6 @@ public enum EventType : int
Secret_Created = 2101,
Secret_Edited = 2102,
Secret_Deleted = 2103,
+ Secrets_Retrieved_Bulk = 2104,
+ Secrets_Deleted_Bulk = 2105,
}
diff --git a/src/Core/AdminConsole/Models/Data/EventMessage.cs b/src/Core/AdminConsole/Models/Data/EventMessage.cs
index 6d2a1f2b4e..4adede2ff2 100644
--- a/src/Core/AdminConsole/Models/Data/EventMessage.cs
+++ b/src/Core/AdminConsole/Models/Data/EventMessage.cs
@@ -1,4 +1,7 @@
-using Bit.Core.Context;
+#nullable enable
+
+using System.ComponentModel.DataAnnotations;
+using Bit.Core.Context;
using Bit.Core.Enums;
namespace Bit.Core.Models.Data;
@@ -14,8 +17,10 @@ public class EventMessage : IEvent
DeviceType = currentContext.DeviceType;
}
- public DateTime Date { get; set; }
- public EventType Type { get; set; }
+ [Required]
+ public DateTime Date { get; set; } = DateTime.Now;
+ [Required]
+ public EventType Type { get; set; } = EventType.Cipher_Created;
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public Guid? InstallationId { get; set; }
@@ -29,10 +34,14 @@ public class EventMessage : IEvent
public Guid? ProviderOrganizationId { get; set; }
public Guid? ActingUserId { get; set; }
public DeviceType? DeviceType { get; set; }
- public string IpAddress { get; set; }
+ [Required]
+ public string IpAddress { get; set; } = string.Empty;
+
public Guid? IdempotencyId { get; private set; } = Guid.NewGuid();
public EventSystemUser? SystemUser { get; set; }
- public string DomainName { get; set; }
+ [Required]
+ public string DomainName { get; set; } = string.Empty;
public Guid? SecretId { get; set; }
+ public string? SecretIds { get; set; }
public Guid? ServiceAccountId { get; set; }
}
diff --git a/src/Core/AdminConsole/Models/Data/EventTableEntity.cs b/src/Core/AdminConsole/Models/Data/EventTableEntity.cs
index 7e863b128c..5afd7da737 100644
--- a/src/Core/AdminConsole/Models/Data/EventTableEntity.cs
+++ b/src/Core/AdminConsole/Models/Data/EventTableEntity.cs
@@ -33,6 +33,7 @@ public class AzureEvent : ITableEntity
public string DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ServiceAccountId { get; set; }
+ public string SecretIds { get; set; }
public EventTableEntity ToEventTableEntity()
{
@@ -62,7 +63,8 @@ public class AzureEvent : ITableEntity
SystemUser = SystemUser.HasValue ? (EventSystemUser)SystemUser.Value : null,
DomainName = DomainName,
SecretId = SecretId,
- ServiceAccountId = ServiceAccountId
+ ServiceAccountId = ServiceAccountId,
+ SecretIds = SecretIds,
};
}
}
@@ -93,6 +95,7 @@ public class EventTableEntity : IEvent
DomainName = e.DomainName;
SecretId = e.SecretId;
ServiceAccountId = e.ServiceAccountId;
+ SecretIds = e.SecretIds;
}
public string PartitionKey { get; set; }
@@ -120,6 +123,7 @@ public class EventTableEntity : IEvent
public string DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ServiceAccountId { get; set; }
+ public string SecretIds { get; set; }
public AzureEvent ToAzureEvent()
{
@@ -149,7 +153,8 @@ public class EventTableEntity : IEvent
SystemUser = SystemUser.HasValue ? (int)SystemUser.Value : null,
DomainName = DomainName,
SecretId = SecretId,
- ServiceAccountId = ServiceAccountId
+ ServiceAccountId = ServiceAccountId,
+ SecretIds = SecretIds
};
}
@@ -215,6 +220,15 @@ public class EventTableEntity : IEvent
});
}
+ if (e.SecretIds != null)
+ {
+ entities.Add(new EventTableEntity(e)
+ {
+ PartitionKey = pKey,
+ RowKey = $"SecretIds={e.SecretIds}__Date={dateKey}__Uniquifier={uniquifier}"
+ });
+ }
+
return entities;
}
diff --git a/src/Core/AdminConsole/Models/Data/IEvent.cs b/src/Core/AdminConsole/Models/Data/IEvent.cs
index 6a177e39ca..361e1e4060 100644
--- a/src/Core/AdminConsole/Models/Data/IEvent.cs
+++ b/src/Core/AdminConsole/Models/Data/IEvent.cs
@@ -24,4 +24,5 @@ public interface IEvent
string DomainName { get; set; }
Guid? SecretId { get; set; }
Guid? ServiceAccountId { get; set; }
+ string SecretIds { get; set; }
}
diff --git a/src/Core/AdminConsole/Services/Implementations/EventService.cs b/src/Core/AdminConsole/Services/Implementations/EventService.cs
index d21e6f25e8..e46246592f 100644
--- a/src/Core/AdminConsole/Services/Implementations/EventService.cs
+++ b/src/Core/AdminConsole/Services/Implementations/EventService.cs
@@ -414,48 +414,111 @@ public class EventService : IEventService
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
var eventMessages = new List();
- foreach (var secret in secrets)
+ if (IsBulkEventType(type))
{
- if (!CanUseEvents(orgAbilities, secret.OrganizationId))
+ var secretsByOrg = secrets.GroupBy(s => s.OrganizationId);
+
+ foreach (var group in secretsByOrg)
{
- continue;
+ var orgId = group.Key;
+
+ if (!CanUseEvents(orgAbilities, orgId))
+ {
+ continue;
+ }
+
+ IEnumerable secretIds = group.Select(s => s.Id);
+
+ var e = new EventMessage(_currentContext)
+ {
+ OrganizationId = orgId,
+ Type = type,
+ SecretIds = string.Join(",", secretIds.Select(id => id.ToString())),
+ UserId = userId,
+ Date = date.GetValueOrDefault(DateTime.UtcNow)
+ };
+ eventMessages.Add(e);
}
-
- var e = new EventMessage(_currentContext)
+ }
+ else
+ {
+ foreach (var secret in secrets)
{
- OrganizationId = secret.OrganizationId,
- Type = type,
- SecretId = secret.Id,
- UserId = userId,
- Date = date.GetValueOrDefault(DateTime.UtcNow)
- };
- eventMessages.Add(e);
+ 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 bool IsBulkEventType(EventType type)
+ {
+ return type == EventType.Secrets_Retrieved_Bulk || type == EventType.Secrets_Deleted_Bulk;
+ }
+
public async Task LogServiceAccountSecretsEventAsync(Guid serviceAccountId, IEnumerable secrets, EventType type, DateTime? date = null)
{
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
var eventMessages = new List();
- foreach (var secret in secrets)
+ if (IsBulkEventType(type))
{
- if (!CanUseEvents(orgAbilities, secret.OrganizationId))
+ var secretsByOrg = secrets.GroupBy(s => s.OrganizationId);
+
+ foreach (var group in secretsByOrg)
{
- continue;
+ var orgId = group.Key;
+
+ if (!CanUseEvents(orgAbilities, orgId))
+ {
+ continue;
+ }
+
+ IEnumerable secretIds = group.Select(s => s.Id);
+
+ var e = new EventMessage(_currentContext)
+ {
+ OrganizationId = orgId,
+ Type = type,
+ SecretIds = string.Join(",", secretIds.Select(id => id.ToString())),
+ UserId = serviceAccountId,
+ Date = date.GetValueOrDefault(DateTime.UtcNow)
+ };
+ eventMessages.Add(e);
}
-
- var e = new EventMessage(_currentContext)
+ }
+ else
+ {
+ foreach (var secret in secrets)
{
- OrganizationId = secret.OrganizationId,
- Type = type,
- SecretId = secret.Id,
- ServiceAccountId = serviceAccountId,
- Date = date.GetValueOrDefault(DateTime.UtcNow)
- };
- eventMessages.Add(e);
+ if (!CanUseEvents(orgAbilities, secret.OrganizationId))
+ {
+ continue;
+ }
+
+ var e = new EventMessage(_currentContext)
+ {
+ OrganizationId = secret.OrganizationId,
+ Type = type,
+ SecretId = secret.Id,
+ ServiceAccountId = serviceAccountId,
+ Date = date.GetValueOrDefault(DateTime.UtcNow)
+ };
+ eventMessages.Add(e);
+ }
}
await _eventWriteService.CreateManyAsync(eventMessages);