mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
[EC-449] Event log user for SCIM events (#2306)
* [EC-449] Added new Enum EventSystemUser * [EC-449] Added SystemUser property to Event model * [EC-449] Added SQL migration to add new column 'SystemUserType' to Event * [EC-449] EF migrations * [EC-449] Added EventSystemUser to EventResponseModel * [EC-449] Saving EventSystemUser.SCIM on SCIM controller actions * [EC-449] Updated Event_Create stored procedure on Sql project * [EC-449] Fixed SystemUser column name on Event table * [EC-507] SCIM CQRS Refactor - Groups/Put (#2269) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-531] Implemented CQRS for Groups Put and added unit tests * [EC-507] Created ScimServiceCollectionExtensions * [EC-507] Renamed AddScimCommands to AddScimGroupCommands * [EC-507] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-507] Removed unneeded dependencies from GroupsController * [EC-507] Update PutGroupCommand to return Group PutGroupCommand returns Group and GroupsController creates ScimGroupResponseModel response * [EC-507] Remove Queries/Commands folders from Scim and Scim.Tests * [EC-507] Remove unneeded check on empty provided memberIds * [EC-507] SCIM CQRS Refactor - Groups/GetList (#2272) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-508] Implemented CQRS for Groups GetList and added unit tests * [EC-507] Created ScimServiceCollectionExtensions and renamed GetGroupsListCommand to GetGroupsListQuery * [EC-507] Renamed AddScimCommands to AddScimGroupQueries * [EC-507] Removed unneeded dependencies from GroupsController * [EC-507] Remove 'Queries' folder from Scim and Scim.Test * [EC-507] Move ScimListResponseModel from GetGroupsListQuery to Scim.GroupsController * [EC-507] Remove asserts on IGroupRepository.GetManyByOrganizationIdAsync from unit tests * [EC-507] SCIM CQRS Refactor - Groups/Get (#2271) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-507] Implemented CQRS for Groups Get and added unit tests * [EC-507] Created ScimServiceCollectionExtensions and renamed GetGroupCommand to GetGroupQuery * [EC-507] Renamed AddScimCommands to AddScimGroupQueries * [EC-507] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-507] Removed unneeded dependencies from GroupsController * [EC-507] Sorted order of methods * [EC-507] Removed GetGroupQuery and moved logic to controller * [EC-507] Remove 'Queries' folder from Scim and Scim.Test * [EC-507] SCIM CQRS Refactor - Groups/Patch (#2268) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-532] Implemented CQRS for Groups Patch and added unit tests * [EC-507] Created ScimServiceCollectionExtensions * [EC-507] Renamed AddScimCommands to AddScimGroupCommands * [EC-507] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-507] Removed unneeded dependencies from GroupsController * [EC-507] Remove Queries/Commands folders from Scim and Scim.Tests * [EC-507] Assert group.Name after saving. Assert userIds saved. * [EC-508] SCIM CQRS Refactor - Users/Delete (#2261) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-539] Implemented CQRS for Users Delete and added unit tests * [EC-508] Created ScimServiceCollectionExtensions * [EC-508] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-508] Removed unneeded model from DeleteUserCommand. Removed unneeded dependencies from UsersController * [EC-508] Removed Bit.Scim.Models dependency from DeleteUserCommandTests * [EC-508] Deleted 'DeleteUserCommand' from SCIM; Created commands on Core 'DeleteOrganizationUserCommand', 'PushDeleteUserRegistrationOrganizationCommand' and 'OrganizationHasConfirmedOwnersExceptQuery' * [EC-508] Changed DeleteOrganizationUserCommand back to using IOrganizationService * [EC-508] Fixed DeleteOrganizationUserCommand unit tests * [EC-508] Remove unneeded obsolete comments. Update DeleteUserAsync Obsolete comment with ticket reference * [EC-508] Move DeleteOrganizationUserCommand to OrganizationFeatures folder * [EC-508] SCIM CQRS Refactor - Users/Post (#2264) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-536] Implemented CQRS for Users Post and added unit tests * [EC-508] Created ScimServiceCollectionExtensions * [EC-508] Renamed AddScimCommands to AddScimUserCommands * [EC-508] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-508] Catching NotFoundException on ExceptionHandlerFilter * [EC-508] Remove Queries/Commands folders from Scim and Scim.Tests * [EC-508] SCIM CQRS Refactor - Users/Patch (#2262) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-538] Implemented CQRS for Users Patch and added unit tests * [EC-508] Added ScimServiceCollectionExtensions * [EC-508] Removed HandleActiveOperationAsync method from UsersController * [EC-508] Renamed AddScimCommands to AddScimUserCommands * [EC-508] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-508] Removed unneeded dependencies from UsersController * [EC-508] Remove 'Query' folder from Scim and Scim.Test * [EC-507] SCIM CQRS Refactor - Groups/Post (#2270) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-530] Implemented CQRS for Groups Post and added unit tests * [EC-507] Created ScimServiceCollectionExtensions * [EC-507] Renamed AddScimCommands to AddScimGroupCommands * [EC-507] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-507] Removed unneeded dependencies from GroupsController * [EC-507] Remove Queries/Commands folders from Scim and Scim.Test * [EC-507] Remove unneeded skipIfEmpty argument. Updated unit test to check provided userIds * [EC-507] Remove UpdateGroupMembersAsync from GroupsController * [EC-508] SCIM CQRS Refactor - Users/GetList (#2265) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-535] Implemented CQRS for Users GetList and added unit tests * [EC-508] Created ScimServiceCollectionExtensions and renamed GetUsersListCommand to GetUsersListQuery * [EC-508] Renamed AddScimCommands to AddScimUserQueries * [EC-508] Removed unneeded IUserRepository and IOptions<ScimSettings> from UsersController * [EC-508] Sorted UsersController properties and dependencies * [EC-508] Remove 'Queries' folder from Scim and Scim.Test * [EC-508] Move ScimListResponseModel creation to Scim.UsersController * [EC-508] Move ScimUserResponseModel creation to Scim.UsersController Co-authored-by: Thomas Rittson <trittson@bitwarden.com> * [EC-507] SCIM CQRS Refactor - Groups/Delete (#2267) * [EC-390] Added Scim.Test unit tests project * [EC-390] Added ConflictException type. Updated BadRequestException to have parameterless constructor. Updated NotFoundException to have constructor with a message parameter * [EC-533] Implemented CQRS for Groups Delete and added unit tests * [EC-507] Created ScimServiceCollectionExtensions * [EC-507] Renamed AddScimCommands to AddScimGroupCommands * [EC-507] Created ExceptionHandlerFilterAttribute on SCIM project * [EC-507] Removed unneeded dependencies from GroupsController * [EC-507] Move DeleteGroupCommand to OrganizationFeatures/OrganizationUsers * [EC-507] Remove IGetUserQuery and move logic to UsersController. Remove unused references. * [EC-449] Add overloads for EventService and GroupService methods that accept EventSystemUser as an argument * [EC-507] Move IDeleteGroupCommand to Groups folder * [EC-449] Add method overloads in IOrganizationService without EventSystemUser * [EC-449] Add RevokeUserAsync overload without EventSystemUser * [EC-449] Reverted OrganizationUsersController to not pass EventSystemUser argument * [EC-449] Uncomment assertion in GroupServiceTests * [EC-449] Update method overloads to not have nullable EventSystemUser * [EC-449] Add unit tests around events that can store EventSystemUser * [EC-449] Deleted private method GroupService.GroupRepositoryDeleteAsync * [EC-449] Move Event log call to public DeleteUserAsync methods * [EC-449] Move call to EventService log to public OrganizationService.InviteUsersAsync methods * [EC-449] Move EventService call to public OrganizationService.DeleteUserAsync methods * [EC-449] Move EventService call to OrganizationService.RevokeUserAsync methods * [EC-449] Move EventService call to OrganizationService.RestoreUserAsync methods * [EC-449] Add missing comma in SQL script for new SystemUser column on the Event table * [EC-449] Remove Autofixture hack from OrganizationServiceTests * [EC-449] Remove invitingUser param when methods expect an EventSystemUser param * [EC-449] Move DeleteUserAsync validation to private method * [EC-449] Move revokingUserId from RevokeUserAsync private method * [EC-449] Move restoringUserId to RestoreUserAsync public method * [EC-449] Set up OrganizationServiceTest Restore and Revoke tests on a single method * [EC-449] SaveUsersSendInvitesAsync to return both OrganizationUsers and Events list * [EC-449] Undo unintended change on CipherRepository * [EC-449] Add SystemUser value to EventTableEntity Co-authored-by: Thomas Rittson <trittson@bitwarden.com>
This commit is contained in:
@ -30,6 +30,7 @@ public class EventResponseModel : ResponseModel
|
||||
DeviceType = ev.DeviceType;
|
||||
IpAddress = ev.IpAddress;
|
||||
InstallationId = ev.InstallationId;
|
||||
SystemUser = ev.SystemUser;
|
||||
}
|
||||
|
||||
public EventType Type { get; set; }
|
||||
@ -48,4 +49,5 @@ public class EventResponseModel : ResponseModel
|
||||
public DateTime Date { get; set; }
|
||||
public DeviceType? DeviceType { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
public EventSystemUser? SystemUser { get; set; }
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ public class Event : ITableObject<Guid>, IEvent
|
||||
DeviceType = e.DeviceType;
|
||||
IpAddress = e.IpAddress;
|
||||
ActingUserId = e.ActingUserId;
|
||||
SystemUser = e.SystemUser;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
@ -47,6 +48,7 @@ public class Event : ITableObject<Guid>, IEvent
|
||||
[MaxLength(50)]
|
||||
public string IpAddress { get; set; }
|
||||
public Guid? ActingUserId { get; set; }
|
||||
public EventSystemUser? SystemUser { get; set; }
|
||||
|
||||
public void SetNewId()
|
||||
{
|
||||
|
6
src/Core/Enums/EventSystemUser.cs
Normal file
6
src/Core/Enums/EventSystemUser.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace Bit.Core.Enums;
|
||||
|
||||
public enum EventSystemUser : byte
|
||||
{
|
||||
SCIM = 1
|
||||
}
|
@ -31,4 +31,5 @@ public class EventMessage : IEvent
|
||||
public DeviceType? DeviceType { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
public Guid? IdempotencyId { get; private set; } = Guid.NewGuid();
|
||||
public EventSystemUser? SystemUser { get; set; }
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ public class EventTableEntity : TableEntity, IEvent
|
||||
DeviceType = e.DeviceType;
|
||||
IpAddress = e.IpAddress;
|
||||
ActingUserId = e.ActingUserId;
|
||||
SystemUser = e.SystemUser;
|
||||
}
|
||||
|
||||
public DateTime Date { get; set; }
|
||||
@ -44,6 +45,7 @@ public class EventTableEntity : TableEntity, IEvent
|
||||
public DeviceType? DeviceType { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
public Guid? ActingUserId { get; set; }
|
||||
public EventSystemUser? SystemUser { get; set; }
|
||||
|
||||
public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
|
||||
{
|
||||
@ -69,6 +71,16 @@ public class EventTableEntity : TableEntity, IEvent
|
||||
result.Add(deviceTypeName, new EntityProperty((int?)DeviceType));
|
||||
}
|
||||
|
||||
var systemUserTypeName = nameof(SystemUser);
|
||||
if (result.ContainsKey(systemUserTypeName))
|
||||
{
|
||||
result[systemUserTypeName] = new EntityProperty((int?)SystemUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(systemUserTypeName, new EntityProperty((int?)SystemUser));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -88,6 +100,12 @@ public class EventTableEntity : TableEntity, IEvent
|
||||
{
|
||||
DeviceType = (DeviceType)properties[deviceTypeName].Int32Value.Value;
|
||||
}
|
||||
|
||||
var systemUserTypeName = nameof(SystemUser);
|
||||
if (properties.ContainsKey(systemUserTypeName) && properties[systemUserTypeName].Int32Value.HasValue)
|
||||
{
|
||||
SystemUser = (EventSystemUser)properties[systemUserTypeName].Int32Value.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<EventTableEntity> IndexEvent(EventMessage e)
|
||||
|
@ -20,4 +20,5 @@ public interface IEvent
|
||||
DeviceType? DeviceType { get; set; }
|
||||
string IpAddress { get; set; }
|
||||
DateTime Date { get; set; }
|
||||
EventSystemUser? SystemUser { get; set; }
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.OrganizationFeatures.Groups.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -17,6 +19,18 @@ public class DeleteGroupCommand : IDeleteGroupCommand
|
||||
}
|
||||
|
||||
public async Task DeleteGroupAsync(Guid organizationId, Guid id)
|
||||
{
|
||||
var group = await GroupRepositoryDeleteGroupAsync(organizationId, id);
|
||||
await _eventService.LogGroupEventAsync(group, Core.Enums.EventType.Group_Deleted);
|
||||
}
|
||||
|
||||
public async Task DeleteGroupAsync(Guid organizationId, Guid id, EventSystemUser eventSystemUser)
|
||||
{
|
||||
var group = await GroupRepositoryDeleteGroupAsync(organizationId, id);
|
||||
await _eventService.LogGroupEventAsync(group, Core.Enums.EventType.Group_Deleted, eventSystemUser);
|
||||
}
|
||||
|
||||
private async Task<Group> GroupRepositoryDeleteGroupAsync(Guid organizationId, Guid id)
|
||||
{
|
||||
var group = await _groupRepository.GetByIdAsync(id);
|
||||
if (group == null || group.OrganizationId != organizationId)
|
||||
@ -25,6 +39,7 @@ public class DeleteGroupCommand : IDeleteGroupCommand
|
||||
}
|
||||
|
||||
await _groupRepository.DeleteAsync(group);
|
||||
await _eventService.LogGroupEventAsync(group, Core.Enums.EventType.Group_Deleted);
|
||||
|
||||
return group;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
namespace Bit.Core.OrganizationFeatures.Groups.Interfaces;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.OrganizationFeatures.Groups.Interfaces;
|
||||
|
||||
public interface IDeleteGroupCommand
|
||||
{
|
||||
Task DeleteGroupAsync(Guid organizationId, Guid id);
|
||||
Task DeleteGroupAsync(Guid organizationId, Guid id, EventSystemUser eventSystemUser);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -20,13 +21,25 @@ public class DeleteOrganizationUserCommand : IDeleteOrganizationUserCommand
|
||||
}
|
||||
|
||||
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
||||
{
|
||||
await ValidateDeleteUserAsync(organizationId, organizationUserId);
|
||||
|
||||
await _organizationService.DeleteUserAsync(organizationId, organizationUserId, deletingUserId);
|
||||
}
|
||||
|
||||
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser)
|
||||
{
|
||||
await ValidateDeleteUserAsync(organizationId, organizationUserId);
|
||||
|
||||
await _organizationService.DeleteUserAsync(organizationId, organizationUserId, eventSystemUser);
|
||||
}
|
||||
|
||||
private async Task ValidateDeleteUserAsync(Guid organizationId, Guid organizationUserId)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
||||
if (orgUser == null || orgUser.OrganizationId != organizationId)
|
||||
{
|
||||
throw new NotFoundException("User not found.");
|
||||
}
|
||||
|
||||
await _organizationService.DeleteUserAsync(organizationId, organizationUserId, deletingUserId);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
namespace Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
|
||||
|
||||
public interface IDeleteOrganizationUserCommand
|
||||
{
|
||||
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
||||
|
||||
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser);
|
||||
}
|
||||
|
@ -11,9 +11,12 @@ public interface IEventService
|
||||
Task LogCipherEventsAsync(IEnumerable<Tuple<Cipher, EventType, DateTime?>> events);
|
||||
Task LogCollectionEventAsync(Collection collection, EventType type, DateTime? date = null);
|
||||
Task LogGroupEventAsync(Group group, EventType type, DateTime? date = null);
|
||||
Task LogGroupEventAsync(Group group, EventType type, EventSystemUser systemUser, DateTime? date = null);
|
||||
Task LogPolicyEventAsync(Policy policy, EventType type, DateTime? date = null);
|
||||
Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type, DateTime? date = null);
|
||||
Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type, EventSystemUser systemUser, DateTime? date = null);
|
||||
Task LogOrganizationUserEventsAsync(IEnumerable<(OrganizationUser, EventType, DateTime?)> events);
|
||||
Task LogOrganizationUserEventsAsync(IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)> events);
|
||||
Task LogOrganizationEventAsync(Organization organization, EventType type, DateTime? date = null);
|
||||
Task LogProviderUserEventAsync(ProviderUser providerUser, EventType type, DateTime? date = null);
|
||||
Task LogProviderUsersEventAsync(IEnumerable<(ProviderUser, EventType, DateTime?)> events);
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
@ -6,7 +7,11 @@ namespace Bit.Core.Services;
|
||||
public interface IGroupService
|
||||
{
|
||||
Task SaveAsync(Group group, IEnumerable<SelectionReadOnly> collections = null);
|
||||
Task SaveAsync(Group group, EventSystemUser systemUser, IEnumerable<SelectionReadOnly> collections = null);
|
||||
[Obsolete("IDeleteGroupCommand should be used instead. To be removed by EC-608.")]
|
||||
Task DeleteAsync(Group group);
|
||||
[Obsolete("IDeleteGroupCommand should be used instead. To be removed by EC-608.")]
|
||||
Task DeleteAsync(Group group, EventSystemUser systemUser);
|
||||
Task DeleteUserAsync(Group group, Guid organizationUserId);
|
||||
Task DeleteUserAsync(Group group, Guid organizationUserId, EventSystemUser systemUser);
|
||||
}
|
||||
|
@ -31,8 +31,12 @@ public interface IOrganizationService
|
||||
Task DisableTwoFactorProviderAsync(Organization organization, TwoFactorProviderType type);
|
||||
Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, Guid? invitingUserId,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites);
|
||||
Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, EventSystemUser systemUser,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites);
|
||||
Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections);
|
||||
Task<OrganizationUser> InviteUserAsync(Guid organizationId, EventSystemUser systemUser, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections);
|
||||
Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId, IEnumerable<Guid> organizationUsersId);
|
||||
Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId);
|
||||
Task<OrganizationUser> AcceptUserAsync(Guid organizationUserId, User user, string token,
|
||||
@ -45,6 +49,8 @@ public interface IOrganizationService
|
||||
Task SaveUserAsync(OrganizationUser user, Guid? savingUserId, IEnumerable<SelectionReadOnly> collections);
|
||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId);
|
||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||
Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, EventSystemUser systemUser);
|
||||
Task DeleteUserAsync(Guid organizationId, Guid userId);
|
||||
Task<List<Tuple<OrganizationUser, string>>> DeleteUsersAsync(Guid organizationId,
|
||||
IEnumerable<Guid> organizationUserIds, Guid? deletingUserId);
|
||||
@ -60,9 +66,11 @@ public interface IOrganizationService
|
||||
Task<Organization> UpdateOrganizationKeysAsync(Guid orgId, string publicKey, string privateKey);
|
||||
Task<bool> HasConfirmedOwnersExceptAsync(Guid organizationId, IEnumerable<Guid> organizationUsersId, bool includeProvider = true);
|
||||
Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId);
|
||||
Task RevokeUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser);
|
||||
Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId,
|
||||
IEnumerable<Guid> organizationUserIds, Guid? revokingUserId);
|
||||
Task RestoreUserAsync(OrganizationUser organizationUser, Guid? restoringUserId, IUserService userService);
|
||||
Task RestoreUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser, IUserService userService);
|
||||
Task<List<Tuple<OrganizationUser, string>>> RestoreUsersAsync(Guid organizationId,
|
||||
IEnumerable<Guid> organizationUserIds, Guid? restoringUserId, IUserService userService);
|
||||
Task<int> GetOccupiedSeatCount(Organization organization);
|
||||
|
@ -155,7 +155,19 @@ public class EventService : IEventService
|
||||
await _eventWriteService.CreateAsync(e);
|
||||
}
|
||||
|
||||
public async Task LogGroupEventAsync(Group group, EventType type, DateTime? date = null)
|
||||
public async Task LogGroupEventAsync(Group group, EventType type,
|
||||
DateTime? date = null)
|
||||
{
|
||||
await CreateLogGroupEventAsync(group, type, systemUser: null, date);
|
||||
}
|
||||
|
||||
public async Task LogGroupEventAsync(Group group, EventType type, EventSystemUser systemUser,
|
||||
DateTime? date = null)
|
||||
{
|
||||
await CreateLogGroupEventAsync(group, type, systemUser, date);
|
||||
}
|
||||
|
||||
private async Task CreateLogGroupEventAsync(Group group, EventType type, EventSystemUser? systemUser, DateTime? date = null)
|
||||
{
|
||||
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||
if (!CanUseEvents(orgAbilities, group.OrganizationId))
|
||||
@ -170,7 +182,8 @@ public class EventService : IEventService
|
||||
Type = type,
|
||||
ActingUserId = _currentContext?.UserId,
|
||||
ProviderId = await GetProviderIdAsync(@group.OrganizationId),
|
||||
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
||||
Date = date.GetValueOrDefault(DateTime.UtcNow),
|
||||
SystemUser = systemUser
|
||||
};
|
||||
await _eventWriteService.CreateAsync(e);
|
||||
}
|
||||
@ -197,13 +210,29 @@ public class EventService : IEventService
|
||||
|
||||
public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type,
|
||||
DateTime? date = null) =>
|
||||
await LogOrganizationUserEventsAsync(new[] { (organizationUser, type, date) });
|
||||
await CreateLogOrganizationUserEventsAsync(new (OrganizationUser, EventType, EventSystemUser?, DateTime?)[] { (organizationUser, type, null, date) });
|
||||
|
||||
public async Task LogOrganizationUserEventsAsync(IEnumerable<(OrganizationUser, EventType, DateTime?)> events)
|
||||
public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type,
|
||||
EventSystemUser systemUser, DateTime? date = null) =>
|
||||
await CreateLogOrganizationUserEventsAsync(new (OrganizationUser, EventType, EventSystemUser?, DateTime?)[] { (organizationUser, type, systemUser, date) });
|
||||
|
||||
public async Task LogOrganizationUserEventsAsync(
|
||||
IEnumerable<(OrganizationUser, EventType, DateTime?)> events)
|
||||
{
|
||||
await CreateLogOrganizationUserEventsAsync(events.Select(e => (e.Item1, e.Item2, (EventSystemUser?)null, e.Item3)));
|
||||
}
|
||||
|
||||
public async Task LogOrganizationUserEventsAsync(
|
||||
IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)> events)
|
||||
{
|
||||
await CreateLogOrganizationUserEventsAsync(events.Select(e => (e.Item1, e.Item2, (EventSystemUser?)e.Item3, e.Item4)));
|
||||
}
|
||||
|
||||
private async Task CreateLogOrganizationUserEventsAsync(IEnumerable<(OrganizationUser, EventType, EventSystemUser?, DateTime?)> events)
|
||||
{
|
||||
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
||||
var eventMessages = new List<IEvent>();
|
||||
foreach (var (organizationUser, type, date) in events)
|
||||
foreach (var (organizationUser, type, systemUser, date) in events)
|
||||
{
|
||||
if (!CanUseEvents(orgAbilities, organizationUser.OrganizationId))
|
||||
{
|
||||
@ -218,7 +247,8 @@ public class EventService : IEventService
|
||||
ProviderId = await GetProviderIdAsync(organizationUser.OrganizationId),
|
||||
Type = type,
|
||||
ActingUserId = _currentContext?.UserId,
|
||||
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
||||
Date = date.GetValueOrDefault(DateTime.UtcNow),
|
||||
SystemUser = systemUser
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,19 @@ public class GroupService : IGroupService
|
||||
_referenceEventService = referenceEventService;
|
||||
}
|
||||
|
||||
public async Task SaveAsync(Group group, IEnumerable<SelectionReadOnly> collections = null)
|
||||
public async Task SaveAsync(Group group,
|
||||
IEnumerable<SelectionReadOnly> collections = null)
|
||||
{
|
||||
await GroupRepositorySaveAsync(group, systemUser: null, collections);
|
||||
}
|
||||
|
||||
public async Task SaveAsync(Group group, EventSystemUser systemUser,
|
||||
IEnumerable<SelectionReadOnly> collections = null)
|
||||
{
|
||||
await GroupRepositorySaveAsync(group, systemUser, collections);
|
||||
}
|
||||
|
||||
private async Task GroupRepositorySaveAsync(Group group, EventSystemUser? systemUser, IEnumerable<SelectionReadOnly> collections = null)
|
||||
{
|
||||
var org = await _organizationRepository.GetByIdAsync(group.OrganizationId);
|
||||
if (org == null)
|
||||
@ -55,7 +67,15 @@ public class GroupService : IGroupService
|
||||
await _groupRepository.CreateAsync(group, collections);
|
||||
}
|
||||
|
||||
await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Created);
|
||||
if (systemUser.HasValue)
|
||||
{
|
||||
await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Created, systemUser.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Created);
|
||||
}
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.GroupCreated, org));
|
||||
}
|
||||
else
|
||||
@ -71,7 +91,14 @@ public class GroupService : IGroupService
|
||||
await _groupRepository.ReplaceAsync(group, collections);
|
||||
}
|
||||
|
||||
await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Updated);
|
||||
if (systemUser.HasValue)
|
||||
{
|
||||
await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Updated, systemUser.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Updated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,17 +106,38 @@ public class GroupService : IGroupService
|
||||
public async Task DeleteAsync(Group group)
|
||||
{
|
||||
await _groupRepository.DeleteAsync(group);
|
||||
await _eventService.LogGroupEventAsync(group, Enums.EventType.Group_Deleted);
|
||||
await _eventService.LogGroupEventAsync(group, EventType.Group_Deleted);
|
||||
}
|
||||
|
||||
[Obsolete("IDeleteGroupCommand should be used instead. To be removed by EC-608.")]
|
||||
public async Task DeleteAsync(Group group, EventSystemUser systemUser)
|
||||
{
|
||||
await _groupRepository.DeleteAsync(group);
|
||||
await _eventService.LogGroupEventAsync(group, EventType.Group_Deleted, systemUser);
|
||||
}
|
||||
|
||||
public async Task DeleteUserAsync(Group group, Guid organizationUserId)
|
||||
{
|
||||
var orgUser = await GroupRepositoryDeleteUserAsync(group, organizationUserId, systemUser: null);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_UpdatedGroups);
|
||||
}
|
||||
|
||||
public async Task DeleteUserAsync(Group group, Guid organizationUserId, EventSystemUser systemUser)
|
||||
{
|
||||
var orgUser = await GroupRepositoryDeleteUserAsync(group, organizationUserId, systemUser);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_UpdatedGroups, systemUser);
|
||||
}
|
||||
|
||||
private async Task<OrganizationUser> GroupRepositoryDeleteUserAsync(Group group, Guid organizationUserId, EventSystemUser? systemUser)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
||||
if (orgUser == null || orgUser.OrganizationId != group.OrganizationId)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
await _groupRepository.DeleteUserAsync(group.Id, organizationUserId);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser, Enums.EventType.OrganizationUser_UpdatedGroups);
|
||||
|
||||
return orgUser;
|
||||
}
|
||||
}
|
||||
|
@ -1115,13 +1115,6 @@ public class OrganizationService : IOrganizationService
|
||||
public async Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, Guid? invitingUserId,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
{
|
||||
var organization = await GetOrgById(organizationId);
|
||||
var initialSeatCount = organization.Seats;
|
||||
if (organization == null || invites.Any(i => i.invite.Emails == null))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var inviteTypes = new HashSet<OrganizationUserType>(invites.Where(i => i.invite.Type.HasValue)
|
||||
.Select(i => i.invite.Type.Value));
|
||||
if (invitingUserId.HasValue && inviteTypes.Count > 0)
|
||||
@ -1132,6 +1125,33 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
}
|
||||
|
||||
var (organizationUsers, events) = await SaveUsersSendInvitesAsync(organizationId, invites);
|
||||
|
||||
await _eventService.LogOrganizationUserEventsAsync(events);
|
||||
|
||||
return organizationUsers;
|
||||
}
|
||||
|
||||
public async Task<List<OrganizationUser>> InviteUsersAsync(Guid organizationId, EventSystemUser systemUser,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
{
|
||||
var (organizationUsers, events) = await SaveUsersSendInvitesAsync(organizationId, invites);
|
||||
|
||||
await _eventService.LogOrganizationUserEventsAsync(events.Select(e => (e.Item1, e.Item2, systemUser, e.Item3)));
|
||||
|
||||
return organizationUsers;
|
||||
}
|
||||
|
||||
private async Task<(List<OrganizationUser> organizationUsers, List<(OrganizationUser, EventType, DateTime?)> events)> SaveUsersSendInvitesAsync(Guid organizationId,
|
||||
IEnumerable<(OrganizationUserInvite invite, string externalId)> invites)
|
||||
{
|
||||
var organization = await GetOrgById(organizationId);
|
||||
var initialSeatCount = organization.Seats;
|
||||
if (organization == null || invites.Any(i => i.invite.Emails == null))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var newSeatsRequired = 0;
|
||||
var existingEmails = new HashSet<string>(await _organizationUserRepository.SelectKnownEmailsAsync(
|
||||
organizationId, invites.SelectMany(i => i.invite.Emails), false), StringComparer.InvariantCultureIgnoreCase);
|
||||
@ -1157,7 +1177,6 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
}
|
||||
|
||||
|
||||
var orgUsers = new List<OrganizationUser>();
|
||||
var limitedCollectionOrgUsers = new List<(OrganizationUser, IEnumerable<SelectionReadOnly>)>();
|
||||
var orgUserInvitedCount = 0;
|
||||
@ -1235,7 +1254,6 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
await AutoAddSeatsAsync(organization, newSeatsRequired, prorationDate);
|
||||
await SendInvitesAsync(orgUsers.Concat(limitedCollectionOrgUsers.Select(u => u.Item1)), organization);
|
||||
await _eventService.LogOrganizationUserEventsAsync(events);
|
||||
|
||||
await _referenceEventService.RaiseEventAsync(
|
||||
new ReferenceEvent(ReferenceEventType.InvitedUsers, organization)
|
||||
@ -1263,7 +1281,7 @@ public class OrganizationService : IOrganizationService
|
||||
throw new AggregateException("One or more errors occurred while inviting users.", exceptions);
|
||||
}
|
||||
|
||||
return orgUsers;
|
||||
return (orgUsers, events);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId,
|
||||
@ -1647,6 +1665,20 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
||||
{
|
||||
var orgUser = await RepositoryDeleteUserAsync(organizationId, organizationUserId, deletingUserId);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed);
|
||||
}
|
||||
|
||||
[Obsolete("IDeleteOrganizationUserCommand should be used instead. To be removed by EC-607.")]
|
||||
public async Task DeleteUserAsync(Guid organizationId, Guid organizationUserId,
|
||||
EventSystemUser systemUser)
|
||||
{
|
||||
var orgUser = await RepositoryDeleteUserAsync(organizationId, organizationUserId, null);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed, systemUser);
|
||||
}
|
||||
|
||||
private async Task<OrganizationUser> RepositoryDeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid? deletingUserId)
|
||||
{
|
||||
var orgUser = await _organizationUserRepository.GetByIdAsync(organizationUserId);
|
||||
if (orgUser == null || orgUser.OrganizationId != organizationId)
|
||||
@ -1671,12 +1703,13 @@ public class OrganizationService : IOrganizationService
|
||||
}
|
||||
|
||||
await _organizationUserRepository.DeleteAsync(orgUser);
|
||||
await _eventService.LogOrganizationUserEventAsync(orgUser, EventType.OrganizationUser_Removed);
|
||||
|
||||
if (orgUser.UserId.HasValue)
|
||||
{
|
||||
await DeleteAndPushUserRegistrationAsync(organizationId, orgUser.UserId.Value);
|
||||
}
|
||||
|
||||
return orgUser;
|
||||
}
|
||||
|
||||
public async Task DeleteUserAsync(Guid organizationId, Guid userId)
|
||||
@ -1852,6 +1885,18 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, Guid? invitingUserId, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections)
|
||||
{
|
||||
return await SaveUserSendInviteAsync(organizationId, invitingUserId, systemUser: null, email, type, accessAll, externalId, collections);
|
||||
}
|
||||
|
||||
public async Task<OrganizationUser> InviteUserAsync(Guid organizationId, EventSystemUser systemUser, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections)
|
||||
{
|
||||
return await SaveUserSendInviteAsync(organizationId, invitingUserId: null, systemUser, email, type, accessAll, externalId, collections);
|
||||
}
|
||||
|
||||
private async Task<OrganizationUser> SaveUserSendInviteAsync(Guid organizationId, Guid? invitingUserId, EventSystemUser? systemUser, string email,
|
||||
OrganizationUserType type, bool accessAll, string externalId, IEnumerable<SelectionReadOnly> collections)
|
||||
{
|
||||
var invite = new OrganizationUserInvite()
|
||||
{
|
||||
@ -1860,7 +1905,8 @@ public class OrganizationService : IOrganizationService
|
||||
AccessAll = accessAll,
|
||||
Collections = collections,
|
||||
};
|
||||
var results = await InviteUsersAsync(organizationId, invitingUserId,
|
||||
var results = systemUser.HasValue ? await InviteUsersAsync(organizationId, systemUser.Value,
|
||||
new (OrganizationUserInvite, string)[] { (invite, externalId) }) : await InviteUsersAsync(organizationId, invitingUserId,
|
||||
new (OrganizationUserInvite, string)[] { (invite, externalId) });
|
||||
var result = results.FirstOrDefault();
|
||||
if (result == null)
|
||||
@ -2221,11 +2267,6 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
public async Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId)
|
||||
{
|
||||
if (organizationUser.Status == OrganizationUserStatusType.Revoked)
|
||||
{
|
||||
throw new BadRequestException("Already revoked.");
|
||||
}
|
||||
|
||||
if (revokingUserId.HasValue && organizationUser.UserId == revokingUserId.Value)
|
||||
{
|
||||
throw new BadRequestException("You cannot revoke yourself.");
|
||||
@ -2237,6 +2278,24 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Only owners can revoke other owners.");
|
||||
}
|
||||
|
||||
await RepositoryRevokeUserAsync(organizationUser);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
|
||||
}
|
||||
|
||||
public async Task RevokeUserAsync(OrganizationUser organizationUser,
|
||||
EventSystemUser systemUser)
|
||||
{
|
||||
await RepositoryRevokeUserAsync(organizationUser);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, systemUser);
|
||||
}
|
||||
|
||||
private async Task RepositoryRevokeUserAsync(OrganizationUser organizationUser)
|
||||
{
|
||||
if (organizationUser.Status == OrganizationUserStatusType.Revoked)
|
||||
{
|
||||
throw new BadRequestException("Already revoked.");
|
||||
}
|
||||
|
||||
if (!await HasConfirmedOwnersExceptAsync(organizationUser.OrganizationId, new[] { organizationUser.Id }))
|
||||
{
|
||||
throw new BadRequestException("Organization must have at least one confirmed owner.");
|
||||
@ -2244,7 +2303,6 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
await _organizationUserRepository.RevokeAsync(organizationUser.Id);
|
||||
organizationUser.Status = OrganizationUserStatusType.Revoked;
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
|
||||
}
|
||||
|
||||
public async Task<List<Tuple<OrganizationUser, string>>> RevokeUsersAsync(Guid organizationId,
|
||||
@ -2306,13 +2364,9 @@ public class OrganizationService : IOrganizationService
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task RestoreUserAsync(OrganizationUser organizationUser, Guid? restoringUserId, IUserService userService)
|
||||
public async Task RestoreUserAsync(OrganizationUser organizationUser, Guid? restoringUserId,
|
||||
IUserService userService)
|
||||
{
|
||||
if (organizationUser.Status != OrganizationUserStatusType.Revoked)
|
||||
{
|
||||
throw new BadRequestException("Already active.");
|
||||
}
|
||||
|
||||
if (restoringUserId.HasValue && organizationUser.UserId == restoringUserId.Value)
|
||||
{
|
||||
throw new BadRequestException("You cannot restore yourself.");
|
||||
@ -2324,6 +2378,24 @@ public class OrganizationService : IOrganizationService
|
||||
throw new BadRequestException("Only owners can restore other owners.");
|
||||
}
|
||||
|
||||
await RepositoryRestoreUserAsync(organizationUser, userService);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
||||
}
|
||||
|
||||
public async Task RestoreUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser,
|
||||
IUserService userService)
|
||||
{
|
||||
await RepositoryRestoreUserAsync(organizationUser, userService);
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, systemUser);
|
||||
}
|
||||
|
||||
private async Task RepositoryRestoreUserAsync(OrganizationUser organizationUser, IUserService userService)
|
||||
{
|
||||
if (organizationUser.Status != OrganizationUserStatusType.Revoked)
|
||||
{
|
||||
throw new BadRequestException("Already active.");
|
||||
}
|
||||
|
||||
var organization = await _organizationRepository.GetByIdAsync(organizationUser.OrganizationId);
|
||||
var occupiedSeats = await GetOccupiedSeatCount(organization);
|
||||
var availableSeats = organization.Seats.GetValueOrDefault(0) - occupiedSeats;
|
||||
@ -2338,7 +2410,6 @@ public class OrganizationService : IOrganizationService
|
||||
|
||||
await _organizationUserRepository.RestoreAsync(organizationUser.Id, status);
|
||||
organizationUser.Status = status;
|
||||
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
||||
}
|
||||
|
||||
public async Task<List<Tuple<OrganizationUser, string>>> RestoreUsersAsync(Guid organizationId,
|
||||
|
@ -31,6 +31,11 @@ public class NoopEventService : IEventService
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task LogGroupEventAsync(Group group, EventType type, EventSystemUser systemUser, DateTime? date = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task LogOrganizationEventAsync(Organization organization, EventType type, DateTime? date = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
@ -52,8 +57,13 @@ public class NoopEventService : IEventService
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type, DateTime? date = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type,
|
||||
DateTime? date = null)
|
||||
EventSystemUser systemUser, DateTime? date = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
@ -63,6 +73,11 @@ public class NoopEventService : IEventService
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task LogOrganizationUserEventsAsync(IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)> events)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task LogUserEventAsync(Guid userId, EventType type, DateTime? date = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
|
@ -562,8 +562,7 @@ public class CipherRepository : Repository<Core.Entities.Cipher, Cipher, Guid>,
|
||||
var attachments = string.IsNullOrWhiteSpace(cipher.Attachments) ?
|
||||
new Dictionary<string, CipherAttachment.MetaData>() :
|
||||
JsonConvert.DeserializeObject<Dictionary<string, CipherAttachment.MetaData>>(cipher.Attachments);
|
||||
var metaData = JsonConvert.DeserializeObject<CipherAttachment.MetaData>(attachment.AttachmentData);
|
||||
attachments[attachment.AttachmentId] = metaData;
|
||||
attachments.Add(attachment.AttachmentId, JsonConvert.DeserializeObject<CipherAttachment.MetaData>(attachment.AttachmentData));
|
||||
cipher.Attachments = JsonConvert.SerializeObject(attachments);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
|
@ -15,7 +15,8 @@
|
||||
@ActingUserId UNIQUEIDENTIFIER,
|
||||
@DeviceType SMALLINT,
|
||||
@IpAddress VARCHAR(50),
|
||||
@Date DATETIME2(7)
|
||||
@Date DATETIME2(7),
|
||||
@SystemUser TINYINT = null
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
@ -38,7 +39,8 @@ BEGIN
|
||||
[ActingUserId],
|
||||
[DeviceType],
|
||||
[IpAddress],
|
||||
[Date]
|
||||
[Date],
|
||||
[SystemUser]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@ -58,6 +60,7 @@ BEGIN
|
||||
@ActingUserId,
|
||||
@DeviceType,
|
||||
@IpAddress,
|
||||
@Date
|
||||
@Date,
|
||||
@SystemUser
|
||||
)
|
||||
END
|
||||
|
@ -16,6 +16,7 @@
|
||||
[ProviderId] UNIQUEIDENTIFIER NULL,
|
||||
[ProviderUserId] UNIQUEIDENTIFIER NULL,
|
||||
[ProviderOrganizationId] UNIQUEIDENTIFIER NULL,
|
||||
[SystemUser] TINYINT NULL,
|
||||
CONSTRAINT [PK_Event] PRIMARY KEY CLUSTERED ([Id] ASC)
|
||||
);
|
||||
|
||||
|
Reference in New Issue
Block a user