1
0
mirror of https://github.com/bitwarden/server synced 2025-12-31 23:53:17 +00:00

[SM-1274] Adding Project Events (#6022)

* Adding new logging for secrets

* fixing secrest controller tests

* fixing the tests

* Server side changes for adding ProjectId to Event table, adding Project event logging to projectsController

* Rough draft with TODO's need to work on EventRepository.cs, and ProjectRepository.cs

* Undoing changes to make projects soft delete, we want those to be fully deleted still. Adding GetManyTrashedSecretsByIds to secret repo so we can get soft deleted secrets, getSecrets in eventsController takes in orgdId, so that we can check the permission even if the secret was permanently deleted and doesn' thave the org Id set. Adding Secret Perm Deleted, and Restored to event logs

* db changes

* fixing the way we log events

* Trying to undo some manual changes that should have been migrations

* adding migration files

* fixing test

* setting up userid for project controller tests

* adding sql

* sql

* Rename file

* Trying to get it to for sure add the column before we try and update sprocs

* Adding code to refresh the view to include ProjectId I hope

* code improvements

* Suggested changes

* suggested changes

* trying to fix sql issues

* fixing swagger issue

* Update src/Core/SecretsManager/Repositories/Noop/NoopSecretRepository.cs

Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>

* Suggested changes

---------

Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
This commit is contained in:
cd-bitwarden
2025-08-20 10:24:17 -04:00
committed by GitHub
parent 7a6fa5a457
commit 3cad054af1
39 changed files with 10698 additions and 15 deletions

View File

@@ -32,6 +32,7 @@ public class Event : ITableObject<Guid>, IEvent
SystemUser = e.SystemUser;
DomainName = e.DomainName;
SecretId = e.SecretId;
ProjectId = e.ProjectId;
ServiceAccountId = e.ServiceAccountId;
}
@@ -56,6 +57,7 @@ public class Event : ITableObject<Guid>, IEvent
public EventSystemUser? SystemUser { get; set; }
public string? DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ProjectId { get; set; }
public Guid? ServiceAccountId { get; set; }
public void SetNewId()

View File

@@ -93,4 +93,11 @@ public enum EventType : int
Secret_Created = 2101,
Secret_Edited = 2102,
Secret_Deleted = 2103,
Secret_Permanently_Deleted = 2104,
Secret_Restored = 2105,
Project_Retrieved = 2200,
Project_Created = 2201,
Project_Edited = 2202,
Project_Deleted = 2203,
}

View File

@@ -37,5 +37,6 @@ public class EventMessage : IEvent
public EventSystemUser? SystemUser { get; set; }
public string DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ProjectId { get; set; }
public Guid? ServiceAccountId { get; set; }
}

View File

@@ -35,6 +35,7 @@ public class AzureEvent : ITableEntity
public int? SystemUser { get; set; }
public string DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ProjectId { get; set; }
public Guid? ServiceAccountId { get; set; }
public EventTableEntity ToEventTableEntity()
@@ -65,7 +66,8 @@ public class AzureEvent : ITableEntity
SystemUser = SystemUser.HasValue ? (EventSystemUser)SystemUser.Value : null,
DomainName = DomainName,
SecretId = SecretId,
ServiceAccountId = ServiceAccountId
ServiceAccountId = ServiceAccountId,
ProjectId = ProjectId,
};
}
}
@@ -95,6 +97,7 @@ public class EventTableEntity : IEvent
SystemUser = e.SystemUser;
DomainName = e.DomainName;
SecretId = e.SecretId;
ProjectId = e.ProjectId;
ServiceAccountId = e.ServiceAccountId;
}
@@ -122,6 +125,7 @@ public class EventTableEntity : IEvent
public EventSystemUser? SystemUser { get; set; }
public string DomainName { get; set; }
public Guid? SecretId { get; set; }
public Guid? ProjectId { get; set; }
public Guid? ServiceAccountId { get; set; }
public AzureEvent ToAzureEvent()
@@ -152,6 +156,7 @@ public class EventTableEntity : IEvent
SystemUser = SystemUser.HasValue ? (int)SystemUser.Value : null,
DomainName = DomainName,
SecretId = SecretId,
ProjectId = ProjectId,
ServiceAccountId = ServiceAccountId
};
}
@@ -218,6 +223,15 @@ public class EventTableEntity : IEvent
});
}
if (e.ProjectId.HasValue)
{
entities.Add(new EventTableEntity(e)
{
PartitionKey = pKey,
RowKey = $"ProjectId={e.ProjectId}__Date={dateKey}__Uniquifier={uniquifier}"
});
}
return entities;
}

View File

@@ -26,5 +26,6 @@ public interface IEvent
EventSystemUser? SystemUser { get; set; }
string DomainName { get; set; }
Guid? SecretId { get; set; }
Guid? ProjectId { get; set; }
Guid? ServiceAccountId { get; set; }
}

View File

@@ -1,4 +1,5 @@
using Bit.Core.Models.Data;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.Vault.Entities;
#nullable enable
@@ -11,6 +12,13 @@ public interface IEventRepository
PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByOrganizationAsync(Guid organizationId, DateTime startDate, DateTime endDate,
PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyBySecretAsync(Secret secret, DateTime startDate, DateTime endDate,
PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByProjectAsync(Project project, DateTime startDate, DateTime endDate,
PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByOrganizationActingUserAsync(Guid organizationId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions);
Task<PagedResult<IEvent>> GetManyByProviderAsync(Guid providerId, DateTime startDate, DateTime endDate,

View File

@@ -1,5 +1,6 @@
using Azure.Data.Tables;
using Bit.Core.Models.Data;
using Bit.Core.SecretsManager.Entities;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Bit.Core.Vault.Entities;
@@ -34,6 +35,20 @@ public class EventRepository : IEventRepository
return await GetManyAsync($"OrganizationId={organizationId}", "Date={0}", startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyBySecretAsync(Secret secret,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"OrganizationId={secret.OrganizationId}",
$"SecretId={secret.Id}__Date={{0}}", startDate, endDate, pageOptions); ;
}
public async Task<PagedResult<IEvent>> GetManyByProjectAsync(Project project,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{
return await GetManyAsync($"OrganizationId={project.OrganizationId}",
$"ProjectId={project.Id}__Date={{0}}", startDate, endDate, pageOptions);
}
public async Task<PagedResult<IEvent>> GetManyByOrganizationActingUserAsync(Guid organizationId, Guid actingUserId,
DateTime startDate, DateTime endDate, PageOptions pageOptions)
{

View File

@@ -35,4 +35,6 @@ public interface IEventService
Task LogOrganizationDomainEventAsync(OrganizationDomain organizationDomain, EventType type, EventSystemUser systemUser, 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 LogUserProjectsEventAsync(Guid userId, IEnumerable<Project> projects, EventType type, DateTime? date = null);
Task LogServiceAccountProjectsEventAsync(Guid serviceAccountId, IEnumerable<Project> projects, EventType type, DateTime? date = null);
}

View File

@@ -464,6 +464,58 @@ public class EventService : IEventService
await _eventWriteService.CreateManyAsync(eventMessages);
}
public async Task LogUserProjectsEventAsync(Guid userId, IEnumerable<Project> projects, EventType type, DateTime? date = null)
{
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
var eventMessages = new List<IEvent>();
foreach (var project in projects)
{
if (!CanUseEvents(orgAbilities, project.OrganizationId))
{
continue;
}
var e = new EventMessage(_currentContext)
{
OrganizationId = project.OrganizationId,
Type = type,
ProjectId = project.Id,
UserId = userId,
Date = date.GetValueOrDefault(DateTime.UtcNow)
};
eventMessages.Add(e);
}
await _eventWriteService.CreateManyAsync(eventMessages);
}
public async Task LogServiceAccountProjectsEventAsync(Guid serviceAccountId, IEnumerable<Project> projects, EventType type, DateTime? date = null)
{
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
var eventMessages = new List<IEvent>();
foreach (var project in projects)
{
if (!CanUseEvents(orgAbilities, project.OrganizationId))
{
continue;
}
var e = new EventMessage(_currentContext)
{
OrganizationId = project.OrganizationId,
Type = type,
ProjectId = project.Id,
ServiceAccountId = serviceAccountId,
Date = date.GetValueOrDefault(DateTime.UtcNow)
};
eventMessages.Add(e);
}
await _eventWriteService.CreateManyAsync(eventMessages);
}
private async Task<Guid?> GetProviderIdAsync(Guid? orgId)
{
if (_currentContext == null || !orgId.HasValue)

View File

@@ -127,4 +127,16 @@ public class NoopEventService : IEventService
{
return Task.FromResult(0);
}
public Task LogUserProjectsEventAsync(Guid userId, IEnumerable<Project> projects, EventType type,
DateTime? date = null)
{
return Task.FromResult(0);
}
public Task LogServiceAccountProjectsEventAsync(Guid serviceAccountId, IEnumerable<Project> projects, EventType type,
DateTime? date = null)
{
return Task.FromResult(0);
}
}