mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
[SM-1489] machine account events (#6187)
* 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 * Adding event logging for machine accounts * fixing two tests * trying to fix all tests * trying to fix tests * fixing test * Migrations * fix * updating eps * adding migration * Adding missing SQL changes * updating sql * fixing sql * running migration again * fixing sql * adding query to add grantedSErviceAccountId to event table * Suggested improvements * removing more migrations * more removal * removing all migrations to them redo them * redoing migration --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
// FIXME: Update this file to be null safe and then delete the line below
|
// FIXME: Update this file to be null safe and then delete the line below
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using Bit.Core.Context;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
using Bit.Core.SecretsManager.Commands.ServiceAccounts.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
namespace Bit.Commercial.Core.SecretsManager.Commands.ServiceAccounts;
|
||||||
|
|
||||||
@@ -13,15 +16,21 @@ public class CreateServiceAccountCommand : ICreateServiceAccountCommand
|
|||||||
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
private readonly IAccessPolicyRepository _accessPolicyRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
|
private readonly IEventService _eventService;
|
||||||
|
private readonly ICurrentContext _currentContext;
|
||||||
|
|
||||||
public CreateServiceAccountCommand(
|
public CreateServiceAccountCommand(
|
||||||
IAccessPolicyRepository accessPolicyRepository,
|
IAccessPolicyRepository accessPolicyRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IServiceAccountRepository serviceAccountRepository)
|
IServiceAccountRepository serviceAccountRepository,
|
||||||
|
IEventService eventService,
|
||||||
|
ICurrentContext currentContext)
|
||||||
{
|
{
|
||||||
_accessPolicyRepository = accessPolicyRepository;
|
_accessPolicyRepository = accessPolicyRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_serviceAccountRepository = serviceAccountRepository;
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
|
_eventService = eventService;
|
||||||
|
_currentContext = currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount, Guid userId)
|
public async Task<ServiceAccount> CreateAsync(ServiceAccount serviceAccount, Guid userId)
|
||||||
@@ -38,6 +47,7 @@ public class CreateServiceAccountCommand : ICreateServiceAccountCommand
|
|||||||
Write = true,
|
Write = true,
|
||||||
};
|
};
|
||||||
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> { accessPolicy });
|
await _accessPolicyRepository.CreateManyAsync(new List<BaseAccessPolicy> { accessPolicy });
|
||||||
|
await _eventService.LogServiceAccountPeopleEventAsync(user.Id, accessPolicy, EventType.ServiceAccount_UserAdded, _currentContext.IdentityClientType);
|
||||||
return createdServiceAccount;
|
return createdServiceAccount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public class EventsController : Controller
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ISecretRepository _secretRepository;
|
private readonly ISecretRepository _secretRepository;
|
||||||
private readonly IProjectRepository _projectRepository;
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
|
|
||||||
|
|
||||||
public EventsController(
|
public EventsController(
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
@@ -39,7 +41,8 @@ public class EventsController : Controller
|
|||||||
IEventRepository eventRepository,
|
IEventRepository eventRepository,
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ISecretRepository secretRepository,
|
ISecretRepository secretRepository,
|
||||||
IProjectRepository projectRepository)
|
IProjectRepository projectRepository,
|
||||||
|
IServiceAccountRepository serviceAccountRepository)
|
||||||
{
|
{
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_cipherRepository = cipherRepository;
|
_cipherRepository = cipherRepository;
|
||||||
@@ -49,6 +52,7 @@ public class EventsController : Controller
|
|||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_secretRepository = secretRepository;
|
_secretRepository = secretRepository;
|
||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
|
_serviceAccountRepository = serviceAccountRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
@@ -184,6 +188,57 @@ public class EventsController : Controller
|
|||||||
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("~/organization/{orgId}/service-account/{id}/events")]
|
||||||
|
public async Task<ListResponseModel<EventResponseModel>> GetServiceAccounts(
|
||||||
|
Guid orgId,
|
||||||
|
Guid id,
|
||||||
|
[FromQuery] DateTime? start = null,
|
||||||
|
[FromQuery] DateTime? end = null,
|
||||||
|
[FromQuery] string continuationToken = null)
|
||||||
|
{
|
||||||
|
if (id == Guid.Empty || orgId == Guid.Empty)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceAccount = await GetServiceAccount(id, orgId);
|
||||||
|
var org = _currentContext.GetOrganization(orgId);
|
||||||
|
|
||||||
|
if (org == null || !await _currentContext.AccessEventLogs(org.Id))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var (fromDate, toDate) = ApiHelpers.GetDateRange(start, end);
|
||||||
|
var result = await _eventRepository.GetManyByOrganizationServiceAccountAsync(
|
||||||
|
serviceAccount.OrganizationId,
|
||||||
|
serviceAccount.Id,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
new PageOptions { ContinuationToken = continuationToken });
|
||||||
|
|
||||||
|
var responses = result.Data.Select(e => new EventResponseModel(e));
|
||||||
|
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
private async Task<ServiceAccount> GetServiceAccount(Guid serviceAccountId, Guid orgId)
|
||||||
|
{
|
||||||
|
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(serviceAccountId);
|
||||||
|
if (serviceAccount != null)
|
||||||
|
{
|
||||||
|
return serviceAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fallbackServiceAccount = new ServiceAccount
|
||||||
|
{
|
||||||
|
Id = serviceAccountId,
|
||||||
|
OrganizationId = orgId
|
||||||
|
};
|
||||||
|
|
||||||
|
return fallbackServiceAccount;
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet("~/organizations/{orgId}/users/{id}/events")]
|
[HttpGet("~/organizations/{orgId}/users/{id}/events")]
|
||||||
public async Task<ListResponseModel<EventResponseModel>> GetOrganizationUser(string orgId, string id,
|
public async Task<ListResponseModel<EventResponseModel>> GetOrganizationUser(string orgId, string id,
|
||||||
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null, [FromQuery] string continuationToken = null)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class EventResponseModel : ResponseModel
|
|||||||
SecretId = ev.SecretId;
|
SecretId = ev.SecretId;
|
||||||
ProjectId = ev.ProjectId;
|
ProjectId = ev.ProjectId;
|
||||||
ServiceAccountId = ev.ServiceAccountId;
|
ServiceAccountId = ev.ServiceAccountId;
|
||||||
|
GrantedServiceAccountId = ev.GrantedServiceAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventType Type { get; set; }
|
public EventType Type { get; set; }
|
||||||
@@ -58,4 +59,5 @@ public class EventResponseModel : ResponseModel
|
|||||||
public Guid? SecretId { get; set; }
|
public Guid? SecretId { get; set; }
|
||||||
public Guid? ProjectId { get; set; }
|
public Guid? ProjectId { get; set; }
|
||||||
public Guid? ServiceAccountId { get; set; }
|
public Guid? ServiceAccountId { get; set; }
|
||||||
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class AccessPoliciesController : Controller
|
|||||||
private readonly IServiceAccountRepository _serviceAccountRepository;
|
private readonly IServiceAccountRepository _serviceAccountRepository;
|
||||||
private readonly IUpdateServiceAccountGrantedPoliciesCommand _updateServiceAccountGrantedPoliciesCommand;
|
private readonly IUpdateServiceAccountGrantedPoliciesCommand _updateServiceAccountGrantedPoliciesCommand;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
|
private readonly IEventService _eventService;
|
||||||
private readonly IProjectServiceAccountsAccessPoliciesUpdatesQuery
|
private readonly IProjectServiceAccountsAccessPoliciesUpdatesQuery
|
||||||
_projectServiceAccountsAccessPoliciesUpdatesQuery;
|
_projectServiceAccountsAccessPoliciesUpdatesQuery;
|
||||||
private readonly IUpdateProjectServiceAccountsAccessPoliciesCommand
|
private readonly IUpdateProjectServiceAccountsAccessPoliciesCommand
|
||||||
@@ -47,7 +48,8 @@ public class AccessPoliciesController : Controller
|
|||||||
IServiceAccountGrantedPolicyUpdatesQuery serviceAccountGrantedPolicyUpdatesQuery,
|
IServiceAccountGrantedPolicyUpdatesQuery serviceAccountGrantedPolicyUpdatesQuery,
|
||||||
IProjectServiceAccountsAccessPoliciesUpdatesQuery projectServiceAccountsAccessPoliciesUpdatesQuery,
|
IProjectServiceAccountsAccessPoliciesUpdatesQuery projectServiceAccountsAccessPoliciesUpdatesQuery,
|
||||||
IUpdateServiceAccountGrantedPoliciesCommand updateServiceAccountGrantedPoliciesCommand,
|
IUpdateServiceAccountGrantedPoliciesCommand updateServiceAccountGrantedPoliciesCommand,
|
||||||
IUpdateProjectServiceAccountsAccessPoliciesCommand updateProjectServiceAccountsAccessPoliciesCommand)
|
IUpdateProjectServiceAccountsAccessPoliciesCommand updateProjectServiceAccountsAccessPoliciesCommand,
|
||||||
|
IEventService eventService)
|
||||||
{
|
{
|
||||||
_authorizationService = authorizationService;
|
_authorizationService = authorizationService;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
@@ -61,6 +63,7 @@ public class AccessPoliciesController : Controller
|
|||||||
_serviceAccountGrantedPolicyUpdatesQuery = serviceAccountGrantedPolicyUpdatesQuery;
|
_serviceAccountGrantedPolicyUpdatesQuery = serviceAccountGrantedPolicyUpdatesQuery;
|
||||||
_projectServiceAccountsAccessPoliciesUpdatesQuery = projectServiceAccountsAccessPoliciesUpdatesQuery;
|
_projectServiceAccountsAccessPoliciesUpdatesQuery = projectServiceAccountsAccessPoliciesUpdatesQuery;
|
||||||
_updateProjectServiceAccountsAccessPoliciesCommand = updateProjectServiceAccountsAccessPoliciesCommand;
|
_updateProjectServiceAccountsAccessPoliciesCommand = updateProjectServiceAccountsAccessPoliciesCommand;
|
||||||
|
_eventService = eventService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/organizations/{id}/access-policies/people/potential-grantees")]
|
[HttpGet("/organizations/{id}/access-policies/people/potential-grantees")]
|
||||||
@@ -186,7 +189,9 @@ public class AccessPoliciesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User)!.Value;
|
var userId = _userService.GetProperUserId(User)!.Value;
|
||||||
|
var currentPolicies = await _accessPolicyRepository.GetPeoplePoliciesByGrantedServiceAccountIdAsync(peopleAccessPolicies.Id, userId);
|
||||||
var results = await _accessPolicyRepository.ReplaceServiceAccountPeopleAsync(peopleAccessPolicies, userId);
|
var results = await _accessPolicyRepository.ReplaceServiceAccountPeopleAsync(peopleAccessPolicies, userId);
|
||||||
|
await LogAccessPolicyServiceAccountChanges(currentPolicies, results, userId);
|
||||||
return new ServiceAccountPeopleAccessPoliciesResponseModel(results, userId);
|
return new ServiceAccountPeopleAccessPoliciesResponseModel(results, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,4 +341,39 @@ public class AccessPoliciesController : Controller
|
|||||||
userId, accessClient);
|
userId, accessClient);
|
||||||
return new ServiceAccountGrantedPoliciesPermissionDetailsResponseModel(results);
|
return new ServiceAccountGrantedPoliciesPermissionDetailsResponseModel(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task LogAccessPolicyServiceAccountChanges(IEnumerable<BaseAccessPolicy> currentPolicies, IEnumerable<BaseAccessPolicy> updatedPolicies, Guid userId)
|
||||||
|
{
|
||||||
|
foreach (var current in currentPolicies.OfType<GroupServiceAccountAccessPolicy>())
|
||||||
|
{
|
||||||
|
if (!updatedPolicies.Any(r => r.Id == current.Id))
|
||||||
|
{
|
||||||
|
await _eventService.LogServiceAccountGroupEventAsync(userId, current, EventType.ServiceAccount_GroupRemoved, _currentContext.IdentityClientType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var policy in updatedPolicies.OfType<GroupServiceAccountAccessPolicy>())
|
||||||
|
{
|
||||||
|
if (!currentPolicies.Any(e => e.Id == policy.Id))
|
||||||
|
{
|
||||||
|
await _eventService.LogServiceAccountGroupEventAsync(userId, policy, EventType.ServiceAccount_GroupAdded, _currentContext.IdentityClientType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var current in currentPolicies.OfType<UserServiceAccountAccessPolicy>())
|
||||||
|
{
|
||||||
|
if (!updatedPolicies.Any(r => r.Id == current.Id))
|
||||||
|
{
|
||||||
|
await _eventService.LogServiceAccountPeopleEventAsync(userId, current, EventType.ServiceAccount_UserRemoved, _currentContext.IdentityClientType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var policy in updatedPolicies.OfType<UserServiceAccountAccessPolicy>())
|
||||||
|
{
|
||||||
|
if (!currentPolicies.Any(e => e.Id == policy.Id))
|
||||||
|
{
|
||||||
|
await _eventService.LogServiceAccountPeopleEventAsync(userId, policy, EventType.ServiceAccount_UserAdded, _currentContext.IdentityClientType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ public class ServiceAccountsController : Controller
|
|||||||
private readonly IDeleteServiceAccountsCommand _deleteServiceAccountsCommand;
|
private readonly IDeleteServiceAccountsCommand _deleteServiceAccountsCommand;
|
||||||
private readonly IRevokeAccessTokensCommand _revokeAccessTokensCommand;
|
private readonly IRevokeAccessTokensCommand _revokeAccessTokensCommand;
|
||||||
private readonly IPricingClient _pricingClient;
|
private readonly IPricingClient _pricingClient;
|
||||||
|
private readonly IEventService _eventService;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
|
||||||
public ServiceAccountsController(
|
public ServiceAccountsController(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
@@ -58,7 +60,9 @@ public class ServiceAccountsController : Controller
|
|||||||
IUpdateServiceAccountCommand updateServiceAccountCommand,
|
IUpdateServiceAccountCommand updateServiceAccountCommand,
|
||||||
IDeleteServiceAccountsCommand deleteServiceAccountsCommand,
|
IDeleteServiceAccountsCommand deleteServiceAccountsCommand,
|
||||||
IRevokeAccessTokensCommand revokeAccessTokensCommand,
|
IRevokeAccessTokensCommand revokeAccessTokensCommand,
|
||||||
IPricingClient pricingClient)
|
IPricingClient pricingClient,
|
||||||
|
IEventService eventService,
|
||||||
|
IOrganizationUserRepository organizationUserRepository)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
@@ -75,6 +79,8 @@ public class ServiceAccountsController : Controller
|
|||||||
_pricingClient = pricingClient;
|
_pricingClient = pricingClient;
|
||||||
_createAccessTokenCommand = createAccessTokenCommand;
|
_createAccessTokenCommand = createAccessTokenCommand;
|
||||||
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
||||||
|
_eventService = eventService;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("/organizations/{organizationId}/service-accounts")]
|
[HttpGet("/organizations/{organizationId}/service-accounts")]
|
||||||
@@ -139,8 +145,15 @@ public class ServiceAccountsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
|
||||||
var result =
|
var result =
|
||||||
await _createServiceAccountCommand.CreateAsync(createRequest.ToServiceAccount(organizationId), userId);
|
await _createServiceAccountCommand.CreateAsync(serviceAccount, userId);
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
{
|
||||||
|
await _eventService.LogServiceAccountEventAsync(userId, [serviceAccount], EventType.ServiceAccount_Created, _currentContext.IdentityClientType);
|
||||||
|
}
|
||||||
|
|
||||||
return new ServiceAccountResponseModel(result);
|
return new ServiceAccountResponseModel(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +210,9 @@ public class ServiceAccountsController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _deleteServiceAccountsCommand.DeleteServiceAccounts(serviceAccountsToDelete);
|
await _deleteServiceAccountsCommand.DeleteServiceAccounts(serviceAccountsToDelete);
|
||||||
|
var userId = _userService.GetProperUserId(User)!.Value;
|
||||||
|
await _eventService.LogServiceAccountEventAsync(userId, serviceAccountsToDelete, EventType.ServiceAccount_Deleted, _currentContext.IdentityClientType);
|
||||||
|
|
||||||
var responses = results.Select(r => new BulkDeleteResponseModel(r.ServiceAccount.Id, r.Error));
|
var responses = results.Select(r => new BulkDeleteResponseModel(r.ServiceAccount.Id, r.Error));
|
||||||
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
return new ListResponseModel<BulkDeleteResponseModel>(responses);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class Event : ITableObject<Guid>, IEvent
|
|||||||
SecretId = e.SecretId;
|
SecretId = e.SecretId;
|
||||||
ProjectId = e.ProjectId;
|
ProjectId = e.ProjectId;
|
||||||
ServiceAccountId = e.ServiceAccountId;
|
ServiceAccountId = e.ServiceAccountId;
|
||||||
|
GrantedServiceAccountId = e.GrantedServiceAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
@@ -59,7 +60,7 @@ public class Event : ITableObject<Guid>, IEvent
|
|||||||
public Guid? SecretId { get; set; }
|
public Guid? SecretId { get; set; }
|
||||||
public Guid? ProjectId { get; set; }
|
public Guid? ProjectId { get; set; }
|
||||||
public Guid? ServiceAccountId { get; set; }
|
public Guid? ServiceAccountId { get; set; }
|
||||||
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
public void SetNewId()
|
public void SetNewId()
|
||||||
{
|
{
|
||||||
Id = CoreHelpers.GenerateComb();
|
Id = CoreHelpers.GenerateComb();
|
||||||
|
|||||||
@@ -109,4 +109,11 @@ public enum EventType : int
|
|||||||
Project_Created = 2201,
|
Project_Created = 2201,
|
||||||
Project_Edited = 2202,
|
Project_Edited = 2202,
|
||||||
Project_Deleted = 2203,
|
Project_Deleted = 2203,
|
||||||
|
|
||||||
|
ServiceAccount_UserAdded = 2300,
|
||||||
|
ServiceAccount_UserRemoved = 2301,
|
||||||
|
ServiceAccount_GroupAdded = 2302,
|
||||||
|
ServiceAccount_GroupRemoved = 2303,
|
||||||
|
ServiceAccount_Created = 2304,
|
||||||
|
ServiceAccount_Deleted = 2305,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,4 +39,5 @@ public class EventMessage : IEvent
|
|||||||
public Guid? SecretId { get; set; }
|
public Guid? SecretId { get; set; }
|
||||||
public Guid? ProjectId { get; set; }
|
public Guid? ProjectId { get; set; }
|
||||||
public Guid? ServiceAccountId { get; set; }
|
public Guid? ServiceAccountId { get; set; }
|
||||||
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public class AzureEvent : ITableEntity
|
|||||||
public Guid? SecretId { get; set; }
|
public Guid? SecretId { get; set; }
|
||||||
public Guid? ProjectId { get; set; }
|
public Guid? ProjectId { get; set; }
|
||||||
public Guid? ServiceAccountId { get; set; }
|
public Guid? ServiceAccountId { get; set; }
|
||||||
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
|
|
||||||
public EventTableEntity ToEventTableEntity()
|
public EventTableEntity ToEventTableEntity()
|
||||||
{
|
{
|
||||||
@@ -68,6 +69,7 @@ public class AzureEvent : ITableEntity
|
|||||||
SecretId = SecretId,
|
SecretId = SecretId,
|
||||||
ServiceAccountId = ServiceAccountId,
|
ServiceAccountId = ServiceAccountId,
|
||||||
ProjectId = ProjectId,
|
ProjectId = ProjectId,
|
||||||
|
GrantedServiceAccountId = GrantedServiceAccountId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,6 +101,7 @@ public class EventTableEntity : IEvent
|
|||||||
SecretId = e.SecretId;
|
SecretId = e.SecretId;
|
||||||
ProjectId = e.ProjectId;
|
ProjectId = e.ProjectId;
|
||||||
ServiceAccountId = e.ServiceAccountId;
|
ServiceAccountId = e.ServiceAccountId;
|
||||||
|
GrantedServiceAccountId = e.GrantedServiceAccountId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string PartitionKey { get; set; }
|
public string PartitionKey { get; set; }
|
||||||
@@ -127,6 +130,7 @@ public class EventTableEntity : IEvent
|
|||||||
public Guid? SecretId { get; set; }
|
public Guid? SecretId { get; set; }
|
||||||
public Guid? ProjectId { get; set; }
|
public Guid? ProjectId { get; set; }
|
||||||
public Guid? ServiceAccountId { get; set; }
|
public Guid? ServiceAccountId { get; set; }
|
||||||
|
public Guid? GrantedServiceAccountId { get; set; }
|
||||||
|
|
||||||
public AzureEvent ToAzureEvent()
|
public AzureEvent ToAzureEvent()
|
||||||
{
|
{
|
||||||
@@ -157,7 +161,8 @@ public class EventTableEntity : IEvent
|
|||||||
DomainName = DomainName,
|
DomainName = DomainName,
|
||||||
SecretId = SecretId,
|
SecretId = SecretId,
|
||||||
ProjectId = ProjectId,
|
ProjectId = ProjectId,
|
||||||
ServiceAccountId = ServiceAccountId
|
ServiceAccountId = ServiceAccountId,
|
||||||
|
GrantedServiceAccountId = GrantedServiceAccountId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,6 +237,15 @@ public class EventTableEntity : IEvent
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.GrantedServiceAccountId.HasValue)
|
||||||
|
{
|
||||||
|
entities.Add(new EventTableEntity(e)
|
||||||
|
{
|
||||||
|
PartitionKey = pKey,
|
||||||
|
RowKey = $"GrantedServiceAccountId={e.GrantedServiceAccountId}__Date={dateKey}__Uniquifier={uniquifier}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,4 +28,5 @@ public interface IEvent
|
|||||||
Guid? SecretId { get; set; }
|
Guid? SecretId { get; set; }
|
||||||
Guid? ProjectId { get; set; }
|
Guid? ProjectId { get; set; }
|
||||||
Guid? ServiceAccountId { get; set; }
|
Guid? ServiceAccountId { get; set; }
|
||||||
|
Guid? GrantedServiceAccountId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public interface IEventRepository
|
|||||||
DateTime startDate, DateTime endDate, PageOptions pageOptions);
|
DateTime startDate, DateTime endDate, PageOptions pageOptions);
|
||||||
Task<PagedResult<IEvent>> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate,
|
Task<PagedResult<IEvent>> GetManyByCipherAsync(Cipher cipher, DateTime startDate, DateTime endDate,
|
||||||
PageOptions pageOptions);
|
PageOptions pageOptions);
|
||||||
|
|
||||||
Task CreateAsync(IEvent e);
|
Task CreateAsync(IEvent e);
|
||||||
Task CreateManyAsync(IEnumerable<IEvent> e);
|
Task CreateManyAsync(IEnumerable<IEvent> e);
|
||||||
Task<PagedResult<IEvent>> GetManyByOrganizationServiceAccountAsync(Guid organizationId, Guid serviceAccountId,
|
Task<PagedResult<IEvent>> GetManyByOrganizationServiceAccountAsync(Guid organizationId, Guid serviceAccountId,
|
||||||
|
|||||||
@@ -77,12 +77,18 @@ public class EventRepository : IEventRepository
|
|||||||
return await GetManyAsync(partitionKey, $"CipherId={cipher.Id}__Date={{0}}", startDate, endDate, pageOptions);
|
return await GetManyAsync(partitionKey, $"CipherId={cipher.Id}__Date={{0}}", startDate, endDate, pageOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PagedResult<IEvent>> GetManyByOrganizationServiceAccountAsync(Guid organizationId,
|
public async Task<PagedResult<IEvent>> GetManyByOrganizationServiceAccountAsync(
|
||||||
Guid serviceAccountId, DateTime startDate, DateTime endDate, PageOptions pageOptions)
|
Guid organizationId,
|
||||||
|
Guid serviceAccountId,
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate,
|
||||||
|
PageOptions pageOptions)
|
||||||
{
|
{
|
||||||
|
return await GetManyServiceAccountAsync(
|
||||||
|
$"OrganizationId={organizationId}",
|
||||||
|
serviceAccountId.ToString(),
|
||||||
|
startDate, endDate, pageOptions);
|
||||||
|
|
||||||
return await GetManyAsync($"OrganizationId={organizationId}",
|
|
||||||
$"ServiceAccountId={serviceAccountId}__Date={{0}}", startDate, endDate, pageOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateAsync(IEvent e)
|
public async Task CreateAsync(IEvent e)
|
||||||
@@ -141,6 +147,40 @@ public class EventRepository : IEventRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<PagedResult<IEvent>> GetManyServiceAccountAsync(
|
||||||
|
string partitionKey,
|
||||||
|
string serviceAccountId,
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate,
|
||||||
|
PageOptions pageOptions)
|
||||||
|
{
|
||||||
|
var start = CoreHelpers.DateTimeToTableStorageKey(startDate);
|
||||||
|
var end = CoreHelpers.DateTimeToTableStorageKey(endDate);
|
||||||
|
var filter = MakeFilterForServiceAccount(partitionKey, serviceAccountId, startDate, endDate);
|
||||||
|
|
||||||
|
var result = new PagedResult<IEvent>();
|
||||||
|
var query = _tableClient.QueryAsync<AzureEvent>(filter, pageOptions.PageSize);
|
||||||
|
|
||||||
|
await using (var enumerator = query.AsPages(pageOptions.ContinuationToken,
|
||||||
|
pageOptions.PageSize).GetAsyncEnumerator())
|
||||||
|
{
|
||||||
|
if (await enumerator.MoveNextAsync())
|
||||||
|
{
|
||||||
|
result.ContinuationToken = enumerator.Current.ContinuationToken;
|
||||||
|
|
||||||
|
var events = enumerator.Current.Values
|
||||||
|
.Select(e => e.ToEventTableEntity())
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
events = events.OrderByDescending(e => e.Date).ToList();
|
||||||
|
|
||||||
|
result.Data.AddRange(events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<PagedResult<IEvent>> GetManyAsync(string partitionKey, string rowKey,
|
public async Task<PagedResult<IEvent>> GetManyAsync(string partitionKey, string rowKey,
|
||||||
DateTime startDate, DateTime endDate, PageOptions pageOptions)
|
DateTime startDate, DateTime endDate, PageOptions pageOptions)
|
||||||
{
|
{
|
||||||
@@ -172,4 +212,27 @@ public class EventRepository : IEventRepository
|
|||||||
{
|
{
|
||||||
return $"PartitionKey eq '{partitionKey}' and RowKey le '{rowStart}' and RowKey ge '{rowEnd}'";
|
return $"PartitionKey eq '{partitionKey}' and RowKey le '{rowStart}' and RowKey ge '{rowEnd}'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string MakeFilterForServiceAccount(
|
||||||
|
string partitionKey,
|
||||||
|
string machineAccountId,
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate)
|
||||||
|
{
|
||||||
|
var start = CoreHelpers.DateTimeToTableStorageKey(startDate);
|
||||||
|
var end = CoreHelpers.DateTimeToTableStorageKey(endDate);
|
||||||
|
|
||||||
|
var rowKey1Start = $"ServiceAccountId={machineAccountId}__Date={start}";
|
||||||
|
var rowKey1End = $"ServiceAccountId={machineAccountId}__Date={end}";
|
||||||
|
|
||||||
|
var rowKey2Start = $"GrantedServiceAccountId={machineAccountId}__Date={start}";
|
||||||
|
var rowKey2End = $"GrantedServiceAccountId={machineAccountId}__Date={end}";
|
||||||
|
|
||||||
|
var left = $"PartitionKey eq '{partitionKey}' and RowKey le '{rowKey1Start}' and RowKey ge '{rowKey1End}'";
|
||||||
|
var right = $"PartitionKey eq '{partitionKey}' and RowKey le '{rowKey2Start}' and RowKey ge '{rowKey2End}'";
|
||||||
|
|
||||||
|
return $"({left}) or ({right})";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Interfaces;
|
using Bit.Core.AdminConsole.Interfaces;
|
||||||
|
using Bit.Core.Auth.Identity;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
@@ -37,4 +38,7 @@ public interface IEventService
|
|||||||
Task LogServiceAccountSecretsEventAsync(Guid serviceAccountId, 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 LogUserProjectsEventAsync(Guid userId, IEnumerable<Project> projects, EventType type, DateTime? date = null);
|
||||||
Task LogServiceAccountProjectsEventAsync(Guid serviceAccountId, IEnumerable<Project> projects, EventType type, DateTime? date = null);
|
Task LogServiceAccountProjectsEventAsync(Guid serviceAccountId, IEnumerable<Project> projects, EventType type, DateTime? date = null);
|
||||||
|
Task LogServiceAccountPeopleEventAsync(Guid userId, UserServiceAccountAccessPolicy policy, EventType type, IdentityClientType identityClientType, DateTime? date = null);
|
||||||
|
Task LogServiceAccountGroupEventAsync(Guid userId, GroupServiceAccountAccessPolicy policy, EventType type, IdentityClientType identityClientType, DateTime? date = null);
|
||||||
|
Task LogServiceAccountEventAsync(Guid userId, List<ServiceAccount> serviceAccount, EventType type, IdentityClientType identityClientType, DateTime? date = null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Bit.Core.AdminConsole.Entities.Provider;
|
|||||||
using Bit.Core.AdminConsole.Interfaces;
|
using Bit.Core.AdminConsole.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
|
using Bit.Core.Auth.Identity;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@@ -516,6 +517,135 @@ public class EventService : IEventService
|
|||||||
await _eventWriteService.CreateManyAsync(eventMessages);
|
await _eventWriteService.CreateManyAsync(eventMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task LogServiceAccountPeopleEventAsync(Guid userId, UserServiceAccountAccessPolicy policy, EventType type, IdentityClientType identityClientType, DateTime? date = null)
|
||||||
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
var eventMessages = new List<IEvent>();
|
||||||
|
var orgUser = await _organizationUserRepository.GetByIdAsync((Guid)policy.OrganizationUserId);
|
||||||
|
|
||||||
|
if (!CanUseEvents(orgAbilities, orgUser.OrganizationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (actingUserId, serviceAccountId) = MapIdentityClientType(userId, identityClientType);
|
||||||
|
|
||||||
|
if (actingUserId is null && serviceAccountId is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy.OrganizationUserId != null)
|
||||||
|
{
|
||||||
|
var e = new EventMessage(_currentContext)
|
||||||
|
{
|
||||||
|
OrganizationId = orgUser.OrganizationId,
|
||||||
|
Type = type,
|
||||||
|
GrantedServiceAccountId = policy.GrantedServiceAccountId,
|
||||||
|
ServiceAccountId = serviceAccountId,
|
||||||
|
UserId = policy.OrganizationUserId,
|
||||||
|
ActingUserId = actingUserId,
|
||||||
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
||||||
|
};
|
||||||
|
eventMessages.Add(e);
|
||||||
|
|
||||||
|
await _eventWriteService.CreateManyAsync(eventMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LogServiceAccountGroupEventAsync(Guid userId, GroupServiceAccountAccessPolicy policy, EventType type, IdentityClientType identityClientType, DateTime? date = null)
|
||||||
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
var eventMessages = new List<IEvent>();
|
||||||
|
|
||||||
|
if (!CanUseEvents(orgAbilities, policy.Group.OrganizationId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (actingUserId, serviceAccountId) = MapIdentityClientType(userId, identityClientType);
|
||||||
|
|
||||||
|
if (actingUserId is null && serviceAccountId is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy.GroupId != null)
|
||||||
|
{
|
||||||
|
var e = new EventMessage(_currentContext)
|
||||||
|
{
|
||||||
|
OrganizationId = policy.Group.OrganizationId,
|
||||||
|
Type = type,
|
||||||
|
GrantedServiceAccountId = policy.GrantedServiceAccountId,
|
||||||
|
ServiceAccountId = serviceAccountId,
|
||||||
|
GroupId = policy.GroupId,
|
||||||
|
ActingUserId = actingUserId,
|
||||||
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
||||||
|
};
|
||||||
|
eventMessages.Add(e);
|
||||||
|
|
||||||
|
await _eventWriteService.CreateManyAsync(eventMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LogServiceAccountEventAsync(Guid userId, List<ServiceAccount> serviceAccounts, EventType type, IdentityClientType identityClientType, DateTime? date = null)
|
||||||
|
{
|
||||||
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||||
|
var eventMessages = new List<IEvent>();
|
||||||
|
|
||||||
|
foreach (var serviceAccount in serviceAccounts)
|
||||||
|
{
|
||||||
|
if (!CanUseEvents(orgAbilities, serviceAccount.OrganizationId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (actingUserId, serviceAccountId) = MapIdentityClientType(userId, identityClientType);
|
||||||
|
|
||||||
|
if (actingUserId is null && serviceAccountId is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceAccount != null)
|
||||||
|
{
|
||||||
|
var e = new EventMessage(_currentContext)
|
||||||
|
{
|
||||||
|
OrganizationId = serviceAccount.OrganizationId,
|
||||||
|
Type = type,
|
||||||
|
GrantedServiceAccountId = serviceAccount.Id,
|
||||||
|
ServiceAccountId = serviceAccountId,
|
||||||
|
ActingUserId = actingUserId,
|
||||||
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
||||||
|
};
|
||||||
|
eventMessages.Add(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventMessages.Any())
|
||||||
|
{
|
||||||
|
await _eventWriteService.CreateManyAsync(eventMessages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private (Guid? actingUserId, Guid? serviceAccountId) MapIdentityClientType(
|
||||||
|
Guid userId, IdentityClientType identityClientType)
|
||||||
|
{
|
||||||
|
if (identityClientType == IdentityClientType.Organization)
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return identityClientType switch
|
||||||
|
{
|
||||||
|
IdentityClientType.User => (userId, null),
|
||||||
|
IdentityClientType.ServiceAccount => (null, userId),
|
||||||
|
_ => throw new InvalidOperationException("Unknown identity client type.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<Guid?> GetProviderIdAsync(Guid? orgId)
|
private async Task<Guid?> GetProviderIdAsync(Guid? orgId)
|
||||||
{
|
{
|
||||||
if (_currentContext == null || !orgId.HasValue)
|
if (_currentContext == null || !orgId.HasValue)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Interfaces;
|
using Bit.Core.AdminConsole.Interfaces;
|
||||||
|
using Bit.Core.Auth.Identity;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.SecretsManager.Entities;
|
using Bit.Core.SecretsManager.Entities;
|
||||||
@@ -139,4 +140,19 @@ public class NoopEventService : IEventService
|
|||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task LogServiceAccountPeopleEventAsync(Guid userId, UserServiceAccountAccessPolicy policy, EventType type, IdentityClientType identityClientType, DateTime? date = null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task LogServiceAccountGroupEventAsync(Guid userId, GroupServiceAccountAccessPolicy policy, EventType type, IdentityClientType identityClientType, DateTime? date = null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task LogServiceAccountEventAsync(Guid userId, List<ServiceAccount> serviceAccount, EventType type, IdentityClientType identityClientType, DateTime? date = null)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,16 @@ public class EventEntityTypeConfiguration : IEntityTypeConfiguration<Event>
|
|||||||
.Property(e => e.Id)
|
.Property(e => e.Id)
|
||||||
.ValueGeneratedNever();
|
.ValueGeneratedNever();
|
||||||
|
|
||||||
builder
|
builder.HasKey(e => e.Id)
|
||||||
.HasIndex(e => new { e.Date, e.OrganizationId, e.ActingUserId, e.CipherId })
|
.IsClustered();
|
||||||
.IsClustered(false);
|
|
||||||
|
var index = builder.HasIndex(e => new { e.Date, e.OrganizationId, e.ActingUserId, e.CipherId })
|
||||||
|
.IsClustered(false)
|
||||||
|
.HasDatabaseName("IX_Event_DateOrganizationIdUserId");
|
||||||
|
|
||||||
|
SqlServerIndexBuilderExtensions.IncludeProperties(
|
||||||
|
index,
|
||||||
|
e => new { e.ServiceAccountId, e.GrantedServiceAccountId });
|
||||||
|
|
||||||
builder.ToTable(nameof(Event));
|
builder.ToTable(nameof(Event));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class EventReadPageByOrganizationIdServiceAccountIdQuery : IQuery<Event>
|
|||||||
(_beforeDate != null || e.Date <= _endDate) &&
|
(_beforeDate != null || e.Date <= _endDate) &&
|
||||||
(_beforeDate == null || e.Date < _beforeDate.Value) &&
|
(_beforeDate == null || e.Date < _beforeDate.Value) &&
|
||||||
e.OrganizationId == _organizationId &&
|
e.OrganizationId == _organizationId &&
|
||||||
e.ServiceAccountId == _serviceAccountId
|
(e.ServiceAccountId == _serviceAccountId || e.GrantedServiceAccountId == _serviceAccountId)
|
||||||
orderby e.Date descending
|
orderby e.Date descending
|
||||||
select e;
|
select e;
|
||||||
return q.Skip(0).Take(_pageOptions.PageSize);
|
return q.Skip(0).Take(_pageOptions.PageSize);
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.SecretsManager.Entities;
|
||||||
|
using Event = Bit.Infrastructure.EntityFramework.Models.Event;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||||
|
|
||||||
|
public class EventReadPageByServiceAccountQuery : IQuery<Event>
|
||||||
|
{
|
||||||
|
private readonly ServiceAccount _serviceAccount;
|
||||||
|
private readonly DateTime _startDate;
|
||||||
|
private readonly DateTime _endDate;
|
||||||
|
private readonly DateTime? _beforeDate;
|
||||||
|
private readonly PageOptions _pageOptions;
|
||||||
|
|
||||||
|
public EventReadPageByServiceAccountQuery(ServiceAccount serviceAccount, DateTime startDate, DateTime endDate, PageOptions pageOptions)
|
||||||
|
{
|
||||||
|
_serviceAccount = serviceAccount;
|
||||||
|
_startDate = startDate;
|
||||||
|
_endDate = endDate;
|
||||||
|
_beforeDate = null;
|
||||||
|
_pageOptions = pageOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EventReadPageByServiceAccountQuery(ServiceAccount serviceAccount, DateTime startDate, DateTime endDate, DateTime? beforeDate, PageOptions pageOptions)
|
||||||
|
{
|
||||||
|
_serviceAccount = serviceAccount;
|
||||||
|
_startDate = startDate;
|
||||||
|
_endDate = endDate;
|
||||||
|
_beforeDate = beforeDate;
|
||||||
|
_pageOptions = pageOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQueryable<Event> Run(DatabaseContext dbContext)
|
||||||
|
{
|
||||||
|
var q = from e in dbContext.Events
|
||||||
|
where e.Date >= _startDate &&
|
||||||
|
(_beforeDate == null || e.Date < _beforeDate.Value) &&
|
||||||
|
(
|
||||||
|
(_serviceAccount.OrganizationId == Guid.Empty && !e.OrganizationId.HasValue) ||
|
||||||
|
(_serviceAccount.OrganizationId != Guid.Empty && e.OrganizationId == _serviceAccount.OrganizationId)
|
||||||
|
) &&
|
||||||
|
e.GrantedServiceAccountId == _serviceAccount.Id
|
||||||
|
orderby e.Date descending
|
||||||
|
select e;
|
||||||
|
|
||||||
|
return q.Take(_pageOptions.PageSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ BEGIN
|
|||||||
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
|
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
|
||||||
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
|
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
|
||||||
AND [OrganizationId] = @OrganizationId
|
AND [OrganizationId] = @OrganizationId
|
||||||
AND [ServiceAccountId] = @ServiceAccountId
|
AND ([ServiceAccountId] = @ServiceAccountId OR [GrantedServiceAccountId] = @ServiceAccountId)
|
||||||
ORDER BY [Date] DESC
|
ORDER BY [Date] DESC
|
||||||
OFFSET 0 ROWS
|
OFFSET 0 ROWS
|
||||||
FETCH NEXT @PageSize ROWS ONLY
|
FETCH NEXT @PageSize ROWS ONLY
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[Event_ReadPageByServiceAccountId]
|
||||||
|
@GrantedServiceAccountId UNIQUEIDENTIFIER,
|
||||||
|
@StartDate DATETIME2(7),
|
||||||
|
@EndDate DATETIME2(7),
|
||||||
|
@BeforeDate DATETIME2(7),
|
||||||
|
@PageSize INT
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
e.Id,
|
||||||
|
e.Date,
|
||||||
|
e.Type,
|
||||||
|
e.UserId,
|
||||||
|
e.OrganizationId,
|
||||||
|
e.InstallationId,
|
||||||
|
e.ProviderId,
|
||||||
|
e.CipherId,
|
||||||
|
e.CollectionId,
|
||||||
|
e.PolicyId,
|
||||||
|
e.GroupId,
|
||||||
|
e.OrganizationUserId,
|
||||||
|
e.ProviderUserId,
|
||||||
|
e.ProviderOrganizationId,
|
||||||
|
e.DeviceType,
|
||||||
|
e.IpAddress,
|
||||||
|
e.ActingUserId,
|
||||||
|
e.SystemUser,
|
||||||
|
e.DomainName,
|
||||||
|
e.SecretId,
|
||||||
|
e.ServiceAccountId,
|
||||||
|
e.ProjectId,
|
||||||
|
e.GrantedServiceAccountId
|
||||||
|
FROM
|
||||||
|
[dbo].[EventView] e
|
||||||
|
WHERE
|
||||||
|
[Date] >= @StartDate
|
||||||
|
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
|
||||||
|
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
|
||||||
|
AND [GrantedServiceAccountId] = @GrantedServiceAccountId
|
||||||
|
ORDER BY [Date] DESC
|
||||||
|
OFFSET 0 ROWS
|
||||||
|
FETCH NEXT @PageSize ROWS ONLY
|
||||||
|
END
|
||||||
@@ -20,7 +20,8 @@
|
|||||||
@DomainName VARCHAR(256),
|
@DomainName VARCHAR(256),
|
||||||
@SecretId UNIQUEIDENTIFIER = null,
|
@SecretId UNIQUEIDENTIFIER = null,
|
||||||
@ServiceAccountId UNIQUEIDENTIFIER = null,
|
@ServiceAccountId UNIQUEIDENTIFIER = null,
|
||||||
@ProjectId UNIQUEIDENTIFIER = null
|
@ProjectId UNIQUEIDENTIFIER = null,
|
||||||
|
@GrantedServiceAccountId UNIQUEIDENTIFIER = null
|
||||||
AS
|
AS
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
@@ -48,7 +49,8 @@ BEGIN
|
|||||||
[DomainName],
|
[DomainName],
|
||||||
[SecretId],
|
[SecretId],
|
||||||
[ServiceAccountId],
|
[ServiceAccountId],
|
||||||
[ProjectId]
|
[ProjectId],
|
||||||
|
[GrantedServiceAccountId]
|
||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
@@ -73,6 +75,7 @@ BEGIN
|
|||||||
@DomainName,
|
@DomainName,
|
||||||
@SecretId,
|
@SecretId,
|
||||||
@ServiceAccountId,
|
@ServiceAccountId,
|
||||||
@ProjectId
|
@ProjectId,
|
||||||
|
@GrantedServiceAccountId
|
||||||
)
|
)
|
||||||
END
|
END
|
||||||
|
|||||||
@@ -21,11 +21,12 @@
|
|||||||
[SecretId] UNIQUEIDENTIFIER NULL,
|
[SecretId] UNIQUEIDENTIFIER NULL,
|
||||||
[ServiceAccountId] UNIQUEIDENTIFIER NULL,
|
[ServiceAccountId] UNIQUEIDENTIFIER NULL,
|
||||||
[ProjectId] UNIQUEIDENTIFIER NULL,
|
[ProjectId] UNIQUEIDENTIFIER NULL,
|
||||||
|
[GrantedServiceAccountId] UNIQUEIDENTIFIER NULL,
|
||||||
CONSTRAINT [PK_Event] PRIMARY KEY CLUSTERED ([Id] ASC)
|
CONSTRAINT [PK_Event] PRIMARY KEY CLUSTERED ([Id] ASC)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
GO
|
GO
|
||||||
CREATE NONCLUSTERED INDEX [IX_Event_DateOrganizationIdUserId]
|
CREATE NONCLUSTERED INDEX [IX_Event_DateOrganizationIdUserId]
|
||||||
ON [dbo].[Event]([Date] DESC, [OrganizationId] ASC, [ActingUserId] ASC, [CipherId] ASC);
|
ON [dbo].[Event]([Date] DESC, [OrganizationId] ASC, [ActingUserId] ASC, [CipherId] ASC) INCLUDE ([ServiceAccountId], [GrantedServiceAccountId]);
|
||||||
|
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ public class ServiceAccountsControllerTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task BulkDelete_ReturnsAccessDeniedForProjectsWithoutAccess_Success(SutProvider<ServiceAccountsController> sutProvider, List<ServiceAccount> data)
|
public async Task BulkDelete_ReturnsAccessDeniedForProjectsWithoutAccess_Success(SutProvider<ServiceAccountsController> sutProvider, List<ServiceAccount> data, Guid userId)
|
||||||
{
|
{
|
||||||
var ids = data.Select(sa => sa.Id).ToList();
|
var ids = data.Select(sa => sa.Id).ToList();
|
||||||
var organizationId = data.First().OrganizationId;
|
var organizationId = data.First().OrganizationId;
|
||||||
@@ -377,6 +377,7 @@ public class ServiceAccountsControllerTests
|
|||||||
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Failed());
|
Arg.Any<IEnumerable<IAuthorizationRequirement>>()).Returns(AuthorizationResult.Failed());
|
||||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true);
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
||||||
|
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||||
|
|
||||||
var results = await sutProvider.Sut.BulkDeleteAsync(ids);
|
var results = await sutProvider.Sut.BulkDeleteAsync(ids);
|
||||||
|
|
||||||
@@ -390,7 +391,7 @@ public class ServiceAccountsControllerTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task BulkDelete_Success(SutProvider<ServiceAccountsController> sutProvider, List<ServiceAccount> data)
|
public async Task BulkDelete_Success(SutProvider<ServiceAccountsController> sutProvider, List<ServiceAccount> data, Guid userId)
|
||||||
{
|
{
|
||||||
var ids = data.Select(sa => sa.Id).ToList();
|
var ids = data.Select(sa => sa.Id).ToList();
|
||||||
var organizationId = data.First().OrganizationId;
|
var organizationId = data.First().OrganizationId;
|
||||||
@@ -404,6 +405,7 @@ public class ServiceAccountsControllerTests
|
|||||||
|
|
||||||
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true);
|
sutProvider.GetDependency<ICurrentContext>().AccessSecretsManager(Arg.Is(organizationId)).ReturnsForAnyArgs(true);
|
||||||
sutProvider.GetDependency<IServiceAccountRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
sutProvider.GetDependency<IServiceAccountRepository>().GetManyByIds(Arg.Is(ids)).ReturnsForAnyArgs(data);
|
||||||
|
sutProvider.GetDependency<IUserService>().GetProperUserId(default).ReturnsForAnyArgs(userId);
|
||||||
|
|
||||||
var results = await sutProvider.Sut.BulkDeleteAsync(ids);
|
var results = await sutProvider.Sut.BulkDeleteAsync(ids);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
IF COL_LENGTH('[dbo].[Event]', 'GrantedServiceAccountId') IS NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER TABLE [dbo].[Event]
|
||||||
|
ADD [GrantedServiceAccountId] UNIQUEIDENTIFIER NULL;
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID('[dbo].[EventView]', 'V') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
EXECUTE sp_refreshview N'[dbo].[EventView]'
|
||||||
|
END
|
||||||
|
GO
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
-- Create or alter Event_Create procedure
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[Event_Create]
|
||||||
|
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||||
|
@Type INT,
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER,
|
||||||
|
@InstallationId UNIQUEIDENTIFIER,
|
||||||
|
@ProviderId UNIQUEIDENTIFIER,
|
||||||
|
@CipherId UNIQUEIDENTIFIER,
|
||||||
|
@CollectionId UNIQUEIDENTIFIER,
|
||||||
|
@PolicyId UNIQUEIDENTIFIER,
|
||||||
|
@GroupId UNIQUEIDENTIFIER,
|
||||||
|
@OrganizationUserId UNIQUEIDENTIFIER,
|
||||||
|
@ProviderUserId UNIQUEIDENTIFIER,
|
||||||
|
@ProviderOrganizationId UNIQUEIDENTIFIER = NULL,
|
||||||
|
@ActingUserId UNIQUEIDENTIFIER,
|
||||||
|
@DeviceType SMALLINT,
|
||||||
|
@IpAddress VARCHAR(50),
|
||||||
|
@Date DATETIME2(7),
|
||||||
|
@SystemUser TINYINT = NULL,
|
||||||
|
@DomainName VARCHAR(256),
|
||||||
|
@SecretId UNIQUEIDENTIFIER = NULL,
|
||||||
|
@ServiceAccountId UNIQUEIDENTIFIER = NULL,
|
||||||
|
@ProjectId UNIQUEIDENTIFIER = NULL,
|
||||||
|
@GrantedServiceAccountId UNIQUEIDENTIFIER = NULL
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
INSERT INTO [dbo].[Event]
|
||||||
|
(
|
||||||
|
[Id],
|
||||||
|
[Type],
|
||||||
|
[UserId],
|
||||||
|
[OrganizationId],
|
||||||
|
[InstallationId],
|
||||||
|
[ProviderId],
|
||||||
|
[CipherId],
|
||||||
|
[CollectionId],
|
||||||
|
[PolicyId],
|
||||||
|
[GroupId],
|
||||||
|
[OrganizationUserId],
|
||||||
|
[ProviderUserId],
|
||||||
|
[ProviderOrganizationId],
|
||||||
|
[ActingUserId],
|
||||||
|
[DeviceType],
|
||||||
|
[IpAddress],
|
||||||
|
[Date],
|
||||||
|
[SystemUser],
|
||||||
|
[DomainName],
|
||||||
|
[SecretId],
|
||||||
|
[ServiceAccountId],
|
||||||
|
[ProjectId],
|
||||||
|
[GrantedServiceAccountId]
|
||||||
|
)
|
||||||
|
VALUES
|
||||||
|
(
|
||||||
|
@Id,
|
||||||
|
@Type,
|
||||||
|
@UserId,
|
||||||
|
@OrganizationId,
|
||||||
|
@InstallationId,
|
||||||
|
@ProviderId,
|
||||||
|
@CipherId,
|
||||||
|
@CollectionId,
|
||||||
|
@PolicyId,
|
||||||
|
@GroupId,
|
||||||
|
@OrganizationUserId,
|
||||||
|
@ProviderUserId,
|
||||||
|
@ProviderOrganizationId,
|
||||||
|
@ActingUserId,
|
||||||
|
@DeviceType,
|
||||||
|
@IpAddress,
|
||||||
|
@Date,
|
||||||
|
@SystemUser,
|
||||||
|
@DomainName,
|
||||||
|
@SecretId,
|
||||||
|
@ServiceAccountId,
|
||||||
|
@ProjectId,
|
||||||
|
@GrantedServiceAccountId
|
||||||
|
);
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
-- Create or alter Event_ReadPageByServiceAccountId procedure
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[Event_ReadPageByServiceAccountId]
|
||||||
|
@GrantedServiceAccountId UNIQUEIDENTIFIER,
|
||||||
|
@StartDate DATETIME2(7),
|
||||||
|
@EndDate DATETIME2(7),
|
||||||
|
@BeforeDate DATETIME2(7),
|
||||||
|
@PageSize INT
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
e.Id,
|
||||||
|
e.Date,
|
||||||
|
e.Type,
|
||||||
|
e.UserId,
|
||||||
|
e.OrganizationId,
|
||||||
|
e.InstallationId,
|
||||||
|
e.ProviderId,
|
||||||
|
e.CipherId,
|
||||||
|
e.CollectionId,
|
||||||
|
e.PolicyId,
|
||||||
|
e.GroupId,
|
||||||
|
e.OrganizationUserId,
|
||||||
|
e.ProviderUserId,
|
||||||
|
e.ProviderOrganizationId,
|
||||||
|
e.DeviceType,
|
||||||
|
e.IpAddress,
|
||||||
|
e.ActingUserId,
|
||||||
|
e.SystemUser,
|
||||||
|
e.DomainName,
|
||||||
|
e.SecretId,
|
||||||
|
e.ServiceAccountId,
|
||||||
|
e.ProjectId,
|
||||||
|
e.GrantedServiceAccountId
|
||||||
|
FROM
|
||||||
|
[dbo].[EventView] e
|
||||||
|
WHERE
|
||||||
|
[Date] >= @StartDate
|
||||||
|
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
|
||||||
|
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
|
||||||
|
AND [GrantedServiceAccountId] = @GrantedServiceAccountId
|
||||||
|
ORDER BY [Date] DESC
|
||||||
|
OFFSET 0 ROWS
|
||||||
|
FETCH NEXT @PageSize ROWS ONLY;
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE OR ALTER PROCEDURE [dbo].[Event_ReadPageByOrganizationIdServiceAccountId]
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER,
|
||||||
|
@ServiceAccountId UNIQUEIDENTIFIER,
|
||||||
|
@StartDate DATETIME2(7),
|
||||||
|
@EndDate DATETIME2(7),
|
||||||
|
@BeforeDate DATETIME2(7),
|
||||||
|
@PageSize INT
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
[dbo].[EventView]
|
||||||
|
WHERE
|
||||||
|
[Date] >= @StartDate
|
||||||
|
AND (@BeforeDate IS NOT NULL OR [Date] <= @EndDate)
|
||||||
|
AND (@BeforeDate IS NULL OR [Date] < @BeforeDate)
|
||||||
|
AND [OrganizationId] = @OrganizationId
|
||||||
|
AND ([ServiceAccountId] = @ServiceAccountId OR [GrantedServiceAccountId] = @ServiceAccountId)
|
||||||
|
ORDER BY [Date] DESC
|
||||||
|
OFFSET 0 ROWS
|
||||||
|
FETCH NEXT @PageSize ROWS ONLY
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF EXISTS(SELECT 1 FROM sys.indexes WHERE name = 'IX_Event_DateOrganizationIdUserId')
|
||||||
|
BEGIN
|
||||||
|
-- Check if neither ServiceAccountId nor GrantedServiceAccountId are included columns
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM
|
||||||
|
sys.indexes i
|
||||||
|
INNER JOIN
|
||||||
|
sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
|
||||||
|
INNER JOIN
|
||||||
|
sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
|
||||||
|
WHERE
|
||||||
|
i.object_id = OBJECT_ID('[dbo].[Event]')
|
||||||
|
AND i.name = 'IX_Event_DateOrganizationIdUserId'
|
||||||
|
AND c.name IN ('ServiceAccountId', 'GrantedServiceAccountId')
|
||||||
|
AND ic.is_included_column = 1
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX [IX_Event_DateOrganizationIdUserId]
|
||||||
|
ON [dbo].[Event]
|
||||||
|
( [Date] DESC,
|
||||||
|
[OrganizationId] ASC,
|
||||||
|
[ActingUserId] ASC,
|
||||||
|
[CipherId] ASC
|
||||||
|
)
|
||||||
|
INCLUDE ([ServiceAccountId], [GrantedServiceAccountId])
|
||||||
|
WITH (DROP_EXISTING = ON)
|
||||||
|
END
|
||||||
|
END
|
||||||
|
GO
|
||||||
3278
util/MySqlMigrations/Migrations/20250910211149_AddingMAEventLog.Designer.cs
generated
Normal file
3278
util/MySqlMigrations/Migrations/20250910211149_AddingMAEventLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.MySqlMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddingMAEventLog : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "GrantedServiceAccountId",
|
||||||
|
table: "Event",
|
||||||
|
type: "char(36)",
|
||||||
|
nullable: true,
|
||||||
|
collation: "ascii_general_ci");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "GrantedServiceAccountId",
|
||||||
|
table: "Event");
|
||||||
|
}
|
||||||
|
}
|
||||||
3284
util/MySqlMigrations/Migrations/20250926144434_AddingIndexToEvents.Designer.cs
generated
Normal file
3284
util/MySqlMigrations/Migrations/20250926144434_AddingIndexToEvents.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.MySqlMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddingIndexToEvents : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_Event_Date_OrganizationId_ActingUserId_CipherId",
|
||||||
|
table: "Event",
|
||||||
|
newName: "IX_Event_DateOrganizationIdUserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_Event_DateOrganizationIdUserId",
|
||||||
|
table: "Event",
|
||||||
|
newName: "IX_Event_Date_OrganizationId_ActingUserId_CipherId");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1282,6 +1282,9 @@ namespace Bit.MySqlMigrations.Migrations
|
|||||||
b.Property<string>("DomainName")
|
b.Property<string>("DomainName")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<Guid?>("GrantedServiceAccountId")
|
||||||
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
b.Property<Guid?>("GroupId")
|
b.Property<Guid?>("GroupId")
|
||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
@@ -1328,10 +1331,13 @@ namespace Bit.MySqlMigrations.Migrations
|
|||||||
b.Property<Guid?>("UserId")
|
b.Property<Guid?>("UserId")
|
||||||
.HasColumnType("char(36)");
|
.HasColumnType("char(36)");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", true);
|
||||||
|
|
||||||
b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId")
|
b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId")
|
||||||
.HasAnnotation("SqlServer:Clustered", false);
|
.HasDatabaseName("IX_Event_DateOrganizationIdUserId")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", false)
|
||||||
|
.HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" });
|
||||||
|
|
||||||
b.ToTable("Event", (string)null);
|
b.ToTable("Event", (string)null);
|
||||||
});
|
});
|
||||||
|
|||||||
3284
util/PostgresMigrations/Migrations/20250910211124_AddingMAEventLog.Designer.cs
generated
Normal file
3284
util/PostgresMigrations/Migrations/20250910211124_AddingMAEventLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.PostgresMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddingMAEventLog : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "GrantedServiceAccountId",
|
||||||
|
table: "Event",
|
||||||
|
type: "uuid",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "GrantedServiceAccountId",
|
||||||
|
table: "Event");
|
||||||
|
}
|
||||||
|
}
|
||||||
3290
util/PostgresMigrations/Migrations/20250926144506_AddingIndexToEvents.Designer.cs
generated
Normal file
3290
util/PostgresMigrations/Migrations/20250926144506_AddingIndexToEvents.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.PostgresMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddingIndexToEvents : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_Event_Date_OrganizationId_ActingUserId_CipherId",
|
||||||
|
table: "Event",
|
||||||
|
newName: "IX_Event_DateOrganizationIdUserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_Event_DateOrganizationIdUserId",
|
||||||
|
table: "Event",
|
||||||
|
newName: "IX_Event_Date_OrganizationId_ActingUserId_CipherId");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1287,6 +1287,9 @@ namespace Bit.PostgresMigrations.Migrations
|
|||||||
b.Property<string>("DomainName")
|
b.Property<string>("DomainName")
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid?>("GrantedServiceAccountId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.Property<Guid?>("GroupId")
|
b.Property<Guid?>("GroupId")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
@@ -1333,10 +1336,13 @@ namespace Bit.PostgresMigrations.Migrations
|
|||||||
b.Property<Guid?>("UserId")
|
b.Property<Guid?>("UserId")
|
||||||
.HasColumnType("uuid");
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", true);
|
||||||
|
|
||||||
b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId")
|
b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId")
|
||||||
.HasAnnotation("SqlServer:Clustered", false);
|
.HasDatabaseName("IX_Event_DateOrganizationIdUserId")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", false)
|
||||||
|
.HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" });
|
||||||
|
|
||||||
b.ToTable("Event", (string)null);
|
b.ToTable("Event", (string)null);
|
||||||
});
|
});
|
||||||
|
|||||||
3267
util/SqliteMigrations/Migrations/20250910211136_AddingMAEventLog.Designer.cs
generated
Normal file
3267
util/SqliteMigrations/Migrations/20250910211136_AddingMAEventLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.SqliteMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddingMAEventLog : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<Guid>(
|
||||||
|
name: "GrantedServiceAccountId",
|
||||||
|
table: "Event",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "GrantedServiceAccountId",
|
||||||
|
table: "Event");
|
||||||
|
}
|
||||||
|
}
|
||||||
3273
util/SqliteMigrations/Migrations/20250926144450_AddingIndexToEvents.Designer.cs
generated
Normal file
3273
util/SqliteMigrations/Migrations/20250926144450_AddingIndexToEvents.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Bit.SqliteMigrations.Migrations;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddingIndexToEvents : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_Event_Date_OrganizationId_ActingUserId_CipherId",
|
||||||
|
table: "Event",
|
||||||
|
newName: "IX_Event_DateOrganizationIdUserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameIndex(
|
||||||
|
name: "IX_Event_DateOrganizationIdUserId",
|
||||||
|
table: "Event",
|
||||||
|
newName: "IX_Event_Date_OrganizationId_ActingUserId_CipherId");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1271,6 +1271,9 @@ namespace Bit.SqliteMigrations.Migrations
|
|||||||
b.Property<string>("DomainName")
|
b.Property<string>("DomainName")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("GrantedServiceAccountId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<Guid?>("GroupId")
|
b.Property<Guid?>("GroupId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
@@ -1317,10 +1320,13 @@ namespace Bit.SqliteMigrations.Migrations
|
|||||||
b.Property<Guid?>("UserId")
|
b.Property<Guid?>("UserId")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", true);
|
||||||
|
|
||||||
b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId")
|
b.HasIndex("Date", "OrganizationId", "ActingUserId", "CipherId")
|
||||||
.HasAnnotation("SqlServer:Clustered", false);
|
.HasDatabaseName("IX_Event_DateOrganizationIdUserId")
|
||||||
|
.HasAnnotation("SqlServer:Clustered", false)
|
||||||
|
.HasAnnotation("SqlServer:Include", new[] { "ServiceAccountId", "GrantedServiceAccountId" });
|
||||||
|
|
||||||
b.ToTable("Event", (string)null);
|
b.ToTable("Event", (string)null);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user