mirror of
https://github.com/bitwarden/server
synced 2025-12-14 23:33:41 +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:
@@ -5,9 +5,12 @@ using Bit.Api.Models.Response;
|
||||
using Bit.Api.Utilities;
|
||||
using Bit.Core.AdminConsole.Repositories;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.SecretsManager.Entities;
|
||||
using Bit.Core.SecretsManager.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
@@ -25,6 +28,8 @@ public class EventsController : Controller
|
||||
private readonly IProviderUserRepository _providerUserRepository;
|
||||
private readonly IEventRepository _eventRepository;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly ISecretRepository _secretRepository;
|
||||
private readonly IProjectRepository _projectRepository;
|
||||
|
||||
public EventsController(
|
||||
IUserService userService,
|
||||
@@ -32,7 +37,9 @@ public class EventsController : Controller
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IProviderUserRepository providerUserRepository,
|
||||
IEventRepository eventRepository,
|
||||
ICurrentContext currentContext)
|
||||
ICurrentContext currentContext,
|
||||
ISecretRepository secretRepository,
|
||||
IProjectRepository projectRepository)
|
||||
{
|
||||
_userService = userService;
|
||||
_cipherRepository = cipherRepository;
|
||||
@@ -40,6 +47,8 @@ public class EventsController : Controller
|
||||
_providerUserRepository = providerUserRepository;
|
||||
_eventRepository = eventRepository;
|
||||
_currentContext = currentContext;
|
||||
_secretRepository = secretRepository;
|
||||
_projectRepository = projectRepository;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
@@ -104,6 +113,77 @@ public class EventsController : Controller
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/organization/{orgId}/secrets/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetSecrets(
|
||||
Guid id, Guid orgId,
|
||||
[FromQuery] DateTime? start = null,
|
||||
[FromQuery] DateTime? end = null,
|
||||
[FromQuery] string continuationToken = null)
|
||||
{
|
||||
if (id == Guid.Empty || orgId == Guid.Empty)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var secret = await _secretRepository.GetByIdAsync(id);
|
||||
var orgIdForVerification = secret?.OrganizationId ?? orgId;
|
||||
var secretOrg = _currentContext.GetOrganization(orgIdForVerification);
|
||||
|
||||
if (secretOrg == null || !await _currentContext.AccessEventLogs(secretOrg.Id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
bool canViewLogs = false;
|
||||
|
||||
if (secret == null)
|
||||
{
|
||||
secret = new Core.SecretsManager.Entities.Secret { Id = id, OrganizationId = orgId };
|
||||
canViewLogs = secretOrg.Type is Core.Enums.OrganizationUserType.Admin or Core.Enums.OrganizationUserType.Owner;
|
||||
}
|
||||
else
|
||||
{
|
||||
canViewLogs = await CanViewSecretsLogs(secret);
|
||||
}
|
||||
|
||||
if (!canViewLogs)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var (fromDate, toDate) = ApiHelpers.GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyBySecretAsync(secret, fromDate, toDate, new PageOptions { ContinuationToken = continuationToken });
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/organization/{orgId}/projects/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetProjects(
|
||||
Guid id,
|
||||
Guid orgId,
|
||||
[FromQuery] DateTime? start = null,
|
||||
[FromQuery] DateTime? end = null,
|
||||
[FromQuery] string continuationToken = null)
|
||||
{
|
||||
if (id == Guid.Empty || orgId == Guid.Empty)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var project = await GetProject(id, orgId);
|
||||
await ValidateOrganization(project);
|
||||
|
||||
var (fromDate, toDate) = ApiHelpers.GetDateRange(start, end);
|
||||
var result = await _eventRepository.GetManyByProjectAsync(
|
||||
project,
|
||||
fromDate,
|
||||
toDate,
|
||||
new PageOptions { ContinuationToken = continuationToken });
|
||||
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[HttpGet("~/organizations/{orgId}/users/{id}/events")]
|
||||
public async Task<ListResponseModel<EventResponseModel>> GetOrganizationUser(string orgId, string id,
|
||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||
@@ -157,4 +237,48 @@ public class EventsController : Controller
|
||||
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||
}
|
||||
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
private async Task ValidateOrganization(Project project)
|
||||
{
|
||||
var org = _currentContext.GetOrganization(project.OrganizationId);
|
||||
|
||||
if (org == null || !await _currentContext.AccessEventLogs(org.Id))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
private async Task<Project> GetProject(Guid projectGuid, Guid orgGuid)
|
||||
{
|
||||
var project = await _projectRepository.GetByIdAsync(projectGuid);
|
||||
if (project != null)
|
||||
{
|
||||
return project;
|
||||
}
|
||||
|
||||
var fallbackProject = new Project
|
||||
{
|
||||
Id = projectGuid,
|
||||
OrganizationId = orgGuid
|
||||
};
|
||||
|
||||
return fallbackProject;
|
||||
}
|
||||
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
private async Task<bool> CanViewSecretsLogs(Secret secret)
|
||||
{
|
||||
if (!_currentContext.AccessSecretsManager(secret.OrganizationId))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var userId = _userService.GetProperUserId(User)!.Value;
|
||||
var isAdmin = await _currentContext.OrganizationAdmin(secret.OrganizationId);
|
||||
var accessClient = AccessClientHelper.ToAccessClient(_currentContext.IdentityClientType, isAdmin);
|
||||
var access = await _secretRepository.AccessToSecretAsync(secret.Id, userId, accessClient);
|
||||
return access.Read;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user