mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01: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:
parent
2d5235b43d
commit
37ed4f43b2
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.OrganizationFeatures.Groups.Interfaces;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Scim.Groups.Interfaces;
|
||||
@ -96,7 +97,7 @@ public class GroupsController : Controller
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(Guid organizationId, Guid id)
|
||||
{
|
||||
await _deleteGroupCommand.DeleteGroupAsync(organizationId, id);
|
||||
await _deleteGroupCommand.DeleteGroupAsync(organizationId, id, EventSystemUser.SCIM);
|
||||
return new NoContentResult();
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ public class UsersController : Controller
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<IActionResult> Delete(Guid organizationId, Guid id)
|
||||
{
|
||||
await _deleteOrganizationUserCommand.DeleteUserAsync(organizationId, id, null);
|
||||
await _deleteOrganizationUserCommand.DeleteUserAsync(organizationId, id, EventSystemUser.SCIM);
|
||||
return new NoContentResult();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -48,7 +49,7 @@ public class PatchGroupCommand : IPatchGroupCommand
|
||||
else if (operation.Path?.ToLowerInvariant() == "displayname")
|
||||
{
|
||||
group.Name = operation.Value.GetString();
|
||||
await _groupService.SaveAsync(group);
|
||||
await _groupService.SaveAsync(group, EventSystemUser.SCIM);
|
||||
operationHandled = true;
|
||||
}
|
||||
// Replace group name from value object
|
||||
@ -56,7 +57,7 @@ public class PatchGroupCommand : IPatchGroupCommand
|
||||
operation.Value.TryGetProperty("displayName", out var displayNameProperty))
|
||||
{
|
||||
group.Name = displayNameProperty.GetString();
|
||||
await _groupService.SaveAsync(group);
|
||||
await _groupService.SaveAsync(group, EventSystemUser.SCIM);
|
||||
operationHandled = true;
|
||||
}
|
||||
}
|
||||
@ -94,7 +95,7 @@ public class PatchGroupCommand : IPatchGroupCommand
|
||||
var removeId = GetOperationPathId(operation.Path);
|
||||
if (removeId.HasValue)
|
||||
{
|
||||
await _groupService.DeleteUserAsync(group, removeId.Value);
|
||||
await _groupService.DeleteUserAsync(group, removeId.Value, EventSystemUser.SCIM);
|
||||
operationHandled = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -38,7 +39,7 @@ public class PostGroupCommand : IPostGroupCommand
|
||||
}
|
||||
|
||||
var group = model.ToGroup(organizationId);
|
||||
await _groupService.SaveAsync(group, null);
|
||||
await _groupService.SaveAsync(group, EventSystemUser.SCIM, null);
|
||||
await UpdateGroupMembersAsync(group, model);
|
||||
|
||||
return group;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -33,7 +34,7 @@ public class PutGroupCommand : IPutGroupCommand
|
||||
}
|
||||
|
||||
group.Name = model.DisplayName;
|
||||
await _groupService.SaveAsync(group);
|
||||
await _groupService.SaveAsync(group, EventSystemUser.SCIM);
|
||||
await UpdateGroupMembersAsync(group, model);
|
||||
|
||||
return group;
|
||||
|
@ -74,12 +74,12 @@ public class PatchUserCommand : IPatchUserCommand
|
||||
{
|
||||
if (active && orgUser.Status == OrganizationUserStatusType.Revoked)
|
||||
{
|
||||
await _organizationService.RestoreUserAsync(orgUser, null, _userService);
|
||||
await _organizationService.RestoreUserAsync(orgUser, EventSystemUser.SCIM, _userService);
|
||||
return true;
|
||||
}
|
||||
else if (!active && orgUser.Status != OrganizationUserStatusType.Revoked)
|
||||
{
|
||||
await _organizationService.RevokeUserAsync(orgUser, null);
|
||||
await _organizationService.RevokeUserAsync(orgUser, EventSystemUser.SCIM);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -79,7 +79,7 @@ public class PostUserCommand : IPostUserCommand
|
||||
throw new ConflictException();
|
||||
}
|
||||
|
||||
var invitedOrgUser = await _organizationService.InviteUserAsync(organizationId, null, email,
|
||||
var invitedOrgUser = await _organizationService.InviteUserAsync(organizationId, EventSystemUser.SCIM, email,
|
||||
OrganizationUserType.User, false, externalId, new List<SelectionReadOnly>());
|
||||
var orgUser = await _organizationUserRepository.GetDetailsByIdAsync(invitedOrgUser.Id);
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -67,7 +68,7 @@ public class PatchGroupCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchGroupAsync(group.OrganizationId, group.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group);
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, EventSystemUser.SCIM);
|
||||
Assert.Equal(displayName, group.Name);
|
||||
}
|
||||
|
||||
@ -94,7 +95,7 @@ public class PatchGroupCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchGroupAsync(group.OrganizationId, group.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group);
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, EventSystemUser.SCIM);
|
||||
Assert.Equal(displayName, group.Name);
|
||||
}
|
||||
|
||||
@ -182,7 +183,7 @@ public class PatchGroupCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchGroupAsync(group.OrganizationId, group.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).DeleteUserAsync(group, userId);
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).DeleteUserAsync(group, userId, EventSystemUser.SCIM);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -42,7 +43,7 @@ public class PostGroupCommandTests
|
||||
|
||||
var group = await sutProvider.Sut.PostGroupAsync(organizationId, scimGroupRequestModel);
|
||||
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, null);
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, EventSystemUser.SCIM, null);
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(0).UpdateUsersAsync(Arg.Any<Guid>(), Arg.Any<IEnumerable<Guid>>());
|
||||
|
||||
AssertHelper.AssertPropertyEqual(expectedResult, group, "Id", "CreationDate", "RevisionDate");
|
||||
@ -77,7 +78,7 @@ public class PostGroupCommandTests
|
||||
|
||||
var group = await sutProvider.Sut.PostGroupAsync(organizationId, scimGroupRequestModel);
|
||||
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, null);
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, EventSystemUser.SCIM, null);
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(1).UpdateUsersAsync(Arg.Any<Guid>(), Arg.Is<IEnumerable<Guid>>(arg => arg.All(id => membersUserIds.Contains(id))));
|
||||
|
||||
AssertHelper.AssertPropertyEqual(expectedResult, group, "Id", "CreationDate", "RevisionDate");
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -45,7 +46,7 @@ public class PutGroupCommandTests
|
||||
AssertHelper.AssertPropertyEqual(expectedResult, result, "CreationDate", "RevisionDate");
|
||||
Assert.Equal(displayName, group.Name);
|
||||
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group);
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, EventSystemUser.SCIM);
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(0).UpdateUsersAsync(group.Id, Arg.Any<IEnumerable<Guid>>());
|
||||
}
|
||||
|
||||
@ -82,7 +83,7 @@ public class PutGroupCommandTests
|
||||
AssertHelper.AssertPropertyEqual(expectedResult, result, "CreationDate", "RevisionDate");
|
||||
Assert.Equal(displayName, group.Name);
|
||||
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group);
|
||||
await sutProvider.GetDependency<IGroupService>().Received(1).SaveAsync(group, EventSystemUser.SCIM);
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(1).UpdateUsersAsync(group.Id, Arg.Is<IEnumerable<Guid>>(arg => arg.All(id => membersUserIds.Contains(id))));
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@ -42,7 +43,7 @@ public class PatchUserCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchUserAsync(organizationUser.OrganizationId, organizationUser.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RestoreUserAsync(organizationUser, null, Arg.Any<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RestoreUserAsync(organizationUser, EventSystemUser.SCIM, Arg.Any<IUserService>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -70,7 +71,7 @@ public class PatchUserCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchUserAsync(organizationUser.OrganizationId, organizationUser.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RestoreUserAsync(organizationUser, null, Arg.Any<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RestoreUserAsync(organizationUser, EventSystemUser.SCIM, Arg.Any<IUserService>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -99,7 +100,7 @@ public class PatchUserCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchUserAsync(organizationUser.OrganizationId, organizationUser.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RevokeUserAsync(organizationUser, null);
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RevokeUserAsync(organizationUser, EventSystemUser.SCIM);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -127,7 +128,7 @@ public class PatchUserCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchUserAsync(organizationUser.OrganizationId, organizationUser.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RevokeUserAsync(organizationUser, null);
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).RevokeUserAsync(organizationUser, EventSystemUser.SCIM);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -146,8 +147,8 @@ public class PatchUserCommandTests
|
||||
|
||||
await sutProvider.Sut.PatchUserAsync(organizationUser.OrganizationId, organizationUser.Id, scimPatchModel);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(0).RestoreUserAsync(organizationUser, null, Arg.Any<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(0).RevokeUserAsync(organizationUser, null);
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(0).RestoreUserAsync(organizationUser, EventSystemUser.SCIM, Arg.Any<IUserService>());
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(0).RevokeUserAsync(organizationUser, EventSystemUser.SCIM);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
@ -34,12 +34,12 @@ public class PostUserCommandTests
|
||||
.Returns(organizationUsers);
|
||||
|
||||
sutProvider.GetDependency<IOrganizationService>()
|
||||
.InviteUserAsync(organizationId, null, scimUserRequestModel.PrimaryEmail.ToLowerInvariant(), OrganizationUserType.User, false, externalId, Arg.Any<List<SelectionReadOnly>>())
|
||||
.InviteUserAsync(organizationId, EventSystemUser.SCIM, scimUserRequestModel.PrimaryEmail.ToLowerInvariant(), OrganizationUserType.User, false, externalId, Arg.Any<List<SelectionReadOnly>>())
|
||||
.Returns(newUser);
|
||||
|
||||
var user = await sutProvider.Sut.PostUserAsync(organizationId, scimUserRequestModel);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).InviteUserAsync(organizationId, null, scimUserRequestModel.PrimaryEmail.ToLowerInvariant(),
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).InviteUserAsync(organizationId, EventSystemUser.SCIM, scimUserRequestModel.PrimaryEmail.ToLowerInvariant(),
|
||||
OrganizationUserType.User, false, scimUserRequestModel.ExternalId, Arg.Any<List<SelectionReadOnly>>());
|
||||
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetDetailsByIdAsync(newUser.Id);
|
||||
}
|
||||
|
@ -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)
|
||||
);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.OrganizationFeatures.Groups;
|
||||
using Bit.Core.Repositories;
|
||||
@ -48,4 +49,18 @@ public class DeleteGroupCommandTests
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.DeleteGroupAsync(organizationId, groupId));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteGroup_WithEventSystemUser_Success(SutProvider<DeleteGroupCommand> sutProvider, Group group, EventSystemUser eventSystemUser)
|
||||
{
|
||||
sutProvider.GetDependency<IGroupRepository>()
|
||||
.GetByIdAsync(group.Id)
|
||||
.Returns(group);
|
||||
|
||||
await sutProvider.Sut.DeleteGroupAsync(group.OrganizationId, group.Id, eventSystemUser);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received(1).DeleteAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogGroupEventAsync(group, Core.Enums.EventType.Group_Deleted, eventSystemUser);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationUsers;
|
||||
using Bit.Core.Repositories;
|
||||
@ -51,4 +52,21 @@ public class DeleteOrganizationUserCommandTests
|
||||
|
||||
await Assert.ThrowsAsync<NotFoundException>(async () => await sutProvider.Sut.DeleteUserAsync(organizationId, organizationUserId, null));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
public async Task DeleteUser_WithEventSystemUser_Success(SutProvider<DeleteOrganizationUserCommand> sutProvider, Guid organizationId, Guid organizationUserId, EventSystemUser eventSystemUser)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>()
|
||||
.GetByIdAsync(organizationUserId)
|
||||
.Returns(new OrganizationUser
|
||||
{
|
||||
Id = organizationUserId,
|
||||
OrganizationId = organizationId
|
||||
});
|
||||
|
||||
await sutProvider.Sut.DeleteUserAsync(organizationId, organizationUserId, eventSystemUser);
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().Received(1).DeleteUserAsync(organizationId, organizationUserId, eventSystemUser);
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,56 @@ public class EventServiceTests
|
||||
Enum.GetValues<EventType>().Select(e => (object)e)
|
||||
).Select(p => p.ToArray());
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task LogGroupEvent_LogsRequiredInfo(Group group, EventType eventType, DateTime date,
|
||||
Guid actingUserId, Guid providerId, DeviceType deviceType, SutProvider<EventService> sutProvider)
|
||||
{
|
||||
var orgAbilities = new Dictionary<Guid, OrganizationAbility>()
|
||||
{
|
||||
{ group.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } }
|
||||
};
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().DeviceType.Returns(deviceType);
|
||||
sutProvider.GetDependency<ICurrentContext>().ProviderIdForOrg(Arg.Any<Guid>()).Returns(providerId);
|
||||
|
||||
await sutProvider.Sut.LogGroupEventAsync(group, eventType, date);
|
||||
|
||||
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateAsync(Arg.Is<IEvent>(e =>
|
||||
e.OrganizationId == group.OrganizationId &&
|
||||
e.GroupId == group.Id &&
|
||||
e.Type == eventType &&
|
||||
e.ActingUserId == actingUserId &&
|
||||
e.ProviderId == providerId &&
|
||||
e.Date == date &&
|
||||
e.SystemUser == null));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task LogGroupEvent_WithEventSystemUser_LogsRequiredInfo(Group group, EventType eventType, EventSystemUser eventSystemUser, DateTime date,
|
||||
Guid actingUserId, Guid providerId, DeviceType deviceType, SutProvider<EventService> sutProvider)
|
||||
{
|
||||
var orgAbilities = new Dictionary<Guid, OrganizationAbility>()
|
||||
{
|
||||
{ group.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } }
|
||||
};
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().DeviceType.Returns(deviceType);
|
||||
sutProvider.GetDependency<ICurrentContext>().ProviderIdForOrg(Arg.Any<Guid>()).Returns(providerId);
|
||||
|
||||
await sutProvider.Sut.LogGroupEventAsync(group, eventType, eventSystemUser, date);
|
||||
|
||||
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateAsync(Arg.Is<IEvent>(e =>
|
||||
e.OrganizationId == group.OrganizationId &&
|
||||
e.GroupId == group.Id &&
|
||||
e.Type == eventType &&
|
||||
e.ActingUserId == actingUserId &&
|
||||
e.ProviderId == providerId &&
|
||||
e.Date == date &&
|
||||
e.SystemUser == eventSystemUser));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitMemberAutoData(nameof(InstallationIdTestCases))]
|
||||
public async Task LogOrganizationEvent_ProvidesInstallationId(Guid? installationId, EventType eventType,
|
||||
@ -73,6 +123,41 @@ public class EventServiceTests
|
||||
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task LogOrganizationUserEvent_WithEventSystemUser_LogsRequiredInfo(OrganizationUser orgUser, EventType eventType, EventSystemUser eventSystemUser, DateTime date,
|
||||
Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider<EventService> sutProvider)
|
||||
{
|
||||
var orgAbilities = new Dictionary<Guid, OrganizationAbility>()
|
||||
{
|
||||
{orgUser.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } }
|
||||
};
|
||||
sutProvider.GetDependency<IApplicationCacheService>().GetOrganizationAbilitiesAsync().Returns(orgAbilities);
|
||||
sutProvider.GetDependency<ICurrentContext>().UserId.Returns(actingUserId);
|
||||
sutProvider.GetDependency<ICurrentContext>().IpAddress.Returns(ipAddress);
|
||||
sutProvider.GetDependency<ICurrentContext>().ProviderIdForOrg(Arg.Any<Guid>()).Returns(providerId);
|
||||
sutProvider.GetDependency<ICurrentContext>().DeviceType.Returns(deviceType);
|
||||
|
||||
await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, eventSystemUser, date);
|
||||
|
||||
var expected = new List<IEvent>() {
|
||||
new EventMessage()
|
||||
{
|
||||
IpAddress = ipAddress,
|
||||
DeviceType = deviceType,
|
||||
OrganizationId = orgUser.OrganizationId,
|
||||
UserId = orgUser.UserId,
|
||||
OrganizationUserId = orgUser.Id,
|
||||
ProviderId = providerId,
|
||||
Type = eventType,
|
||||
ActingUserId = actingUserId,
|
||||
Date = date,
|
||||
SystemUser = eventSystemUser
|
||||
}
|
||||
};
|
||||
|
||||
await sutProvider.GetDependency<IEventWriteService>().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual<IEvent>(expected, new[] { "IdempotencyId" })));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task LogProviderUserEvent_LogsRequiredInfo(ProviderUser providerUser, EventType eventType, DateTime date,
|
||||
Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider<EventService> sutProvider)
|
||||
|
@ -27,8 +27,23 @@ public class GroupServiceTests
|
||||
await sutProvider.Sut.SaveAsync(group);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().CreateAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogGroupEventAsync(group, EventType.Group_Created);
|
||||
await sutProvider.GetDependency<IEventService>().Received().LogGroupEventAsync(group, EventType.Group_Created);
|
||||
Assert.True(group.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
Assert.True(group.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task SaveAsync_DefaultGroupId_WithEventSystemUser_CreatesGroupInRepository(Group group, Organization organization, EventSystemUser eventSystemUser, SutProvider<GroupService> sutProvider)
|
||||
{
|
||||
group.Id = default(Guid);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
organization.UseGroups = true;
|
||||
var utcNow = DateTime.UtcNow;
|
||||
|
||||
await sutProvider.Sut.SaveAsync(group, eventSystemUser);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().CreateAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received().LogGroupEventAsync(group, EventType.Group_Created, eventSystemUser);
|
||||
Assert.True(group.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
Assert.True(group.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
}
|
||||
@ -44,8 +59,7 @@ public class GroupServiceTests
|
||||
await sutProvider.Sut.SaveAsync(group, collections);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().CreateAsync(group, collections);
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogGroupEventAsync(group, EventType.Group_Created);
|
||||
await sutProvider.GetDependency<IEventService>().Received().LogGroupEventAsync(group, EventType.Group_Created);
|
||||
Assert.True(group.CreationDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
Assert.True(group.RevisionDate - utcNow < TimeSpan.FromSeconds(1));
|
||||
}
|
||||
@ -59,8 +73,7 @@ public class GroupServiceTests
|
||||
await sutProvider.Sut.SaveAsync(group, collections);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().ReplaceAsync(group, collections);
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogGroupEventAsync(group, EventType.Group_Updated);
|
||||
await sutProvider.GetDependency<IEventService>().Received().LogGroupEventAsync(group, EventType.Group_Updated);
|
||||
Assert.True(group.RevisionDate - DateTime.UtcNow < TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
@ -73,8 +86,7 @@ public class GroupServiceTests
|
||||
await sutProvider.Sut.SaveAsync(group, null);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().ReplaceAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogGroupEventAsync(group, EventType.Group_Updated);
|
||||
await sutProvider.GetDependency<IEventService>().Received().LogGroupEventAsync(group, EventType.Group_Updated);
|
||||
Assert.True(group.RevisionDate - DateTime.UtcNow < TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
@ -109,8 +121,16 @@ public class GroupServiceTests
|
||||
await sutProvider.Sut.DeleteAsync(group);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().DeleteAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogGroupEventAsync(group, EventType.Group_Deleted);
|
||||
await sutProvider.GetDependency<IEventService>().Received().LogGroupEventAsync(group, EventType.Group_Deleted);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DeleteAsync_ValidData_WithEventSystemUser_DeletesGroup(Group group, EventSystemUser eventSystemUser, SutProvider<GroupService> sutProvider)
|
||||
{
|
||||
await sutProvider.Sut.DeleteAsync(group, eventSystemUser);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().DeleteAsync(group);
|
||||
await sutProvider.GetDependency<IEventService>().Received().LogGroupEventAsync(group, EventType.Group_Deleted, eventSystemUser);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -130,6 +150,23 @@ public class GroupServiceTests
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UpdatedGroups);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DeleteUserAsync_ValidData_WithEventSystemUser_DeletesUserInGroupRepository(Group group, Organization organization, OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<GroupService> sutProvider)
|
||||
{
|
||||
group.OrganizationId = organization.Id;
|
||||
organization.UseGroups = true;
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
organizationUser.OrganizationId = organization.Id;
|
||||
sutProvider.GetDependency<IOrganizationUserRepository>().GetByIdAsync(organizationUser.Id)
|
||||
.Returns(organizationUser);
|
||||
|
||||
await sutProvider.Sut.DeleteUserAsync(group, organizationUser.Id, eventSystemUser);
|
||||
|
||||
await sutProvider.GetDependency<IGroupRepository>().Received().DeleteUserAsync(group.Id, organizationUser.Id);
|
||||
await sutProvider.GetDependency<IEventService>().Received()
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UpdatedGroups, eventSystemUser);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DeleteUserAsync_InvalidUser_ThrowsNotFound(Group group, Organization organization, OrganizationUser organizationUser, SutProvider<GroupService> sutProvider)
|
||||
{
|
||||
|
@ -186,11 +186,13 @@ public class OrganizationServiceTests
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize, BitAutoData]
|
||||
[OrganizationInviteCustomize(InviteeUserType = OrganizationUserType.User,
|
||||
InvitorUserType = OrganizationUserType.Owner), BitAutoData]
|
||||
public async Task InviteUser_NoEmails_Throws(Organization organization, OrganizationUser invitor,
|
||||
OrganizationUserInvite invite, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
invite.Emails = null;
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
await Assert.ThrowsAsync<NotFoundException>(
|
||||
() => sutProvider.Sut.InviteUsersAsync(organization.Id, invitor.UserId, new (OrganizationUserInvite, string)[] { (invite, null) }));
|
||||
@ -357,11 +359,6 @@ public class OrganizationServiceTests
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
// Autofixture will add collections for all of the invites, remove the first and for all the rest set all access false
|
||||
invites.First().invite.AccessAll = true;
|
||||
invites.First().invite.Collections = null;
|
||||
invites.Skip(1).ToList().ForEach(i => i.invite.AccessAll = false);
|
||||
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
@ -382,6 +379,42 @@ public class OrganizationServiceTests
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.BulkSendOrganizationInviteEmailAsync(organization.Name,
|
||||
Arg.Is<IEnumerable<(OrganizationUser, ExpiringToken)>>(v => v.Count() == invites.SelectMany(i => i.invite.Emails).Count()));
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[OrganizationInviteCustomize(
|
||||
InviteeUserType = OrganizationUserType.User,
|
||||
InvitorUserType = OrganizationUserType.Custom
|
||||
), BitAutoData]
|
||||
public async Task InviteUser_WithEventSystemUser_Passes(Organization organization, EventSystemUser eventSystemUser, IEnumerable<(OrganizationUserInvite invite, string externalId)> invites,
|
||||
OrganizationUser invitor,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
invitor.Permissions = JsonSerializer.Serialize(new Permissions() { ManageUsers = true },
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
});
|
||||
|
||||
var organizationRepository = sutProvider.GetDependency<IOrganizationRepository>();
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||
|
||||
organizationRepository.GetByIdAsync(organization.Id).Returns(organization);
|
||||
organizationUserRepository.GetManyByOrganizationAsync(organization.Id, OrganizationUserType.Owner)
|
||||
.Returns(new[] { owner });
|
||||
currentContext.ManageUsers(organization.Id).Returns(true);
|
||||
|
||||
await sutProvider.Sut.InviteUsersAsync(organization.Id, eventSystemUser, invites);
|
||||
|
||||
await sutProvider.GetDependency<IMailService>().Received(1)
|
||||
.BulkSendOrganizationInviteEmailAsync(organization.Name,
|
||||
Arg.Is<IEnumerable<(OrganizationUser, ExpiringToken)>>(v => v.Count() == invites.SelectMany(i => i.invite.Emails).Count()));
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventsAsync(Arg.Any<IEnumerable<(OrganizationUser, EventType, EventSystemUser, DateTime?)>>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -505,6 +538,29 @@ public class OrganizationServiceTests
|
||||
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
||||
|
||||
await sutProvider.Sut.DeleteUserAsync(deletingUser.OrganizationId, organizationUser.Id, deletingUser.UserId);
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task DeleteUser_WithEventSystemUser_Success(
|
||||
OrganizationUser organizationUser,
|
||||
[OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser deletingUser, EventSystemUser eventSystemUser,
|
||||
SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var currentContext = sutProvider.GetDependency<ICurrentContext>();
|
||||
|
||||
organizationUser.OrganizationId = deletingUser.OrganizationId;
|
||||
organizationUserRepository.GetByIdAsync(organizationUser.Id).Returns(organizationUser);
|
||||
organizationUserRepository.GetByIdAsync(deletingUser.Id).Returns(deletingUser);
|
||||
organizationUserRepository.GetManyByOrganizationAsync(deletingUser.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns(new[] { deletingUser, organizationUser });
|
||||
currentContext.OrganizationOwner(deletingUser.OrganizationId).Returns(true);
|
||||
|
||||
await sutProvider.Sut.DeleteUserAsync(deletingUser.OrganizationId, organizationUser.Id, eventSystemUser);
|
||||
|
||||
await sutProvider.GetDependency<IEventService>().Received(1).LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Removed, eventSystemUser);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@ -962,4 +1018,77 @@ public class OrganizationServiceTests
|
||||
await organizationRepository.DidNotReceiveWithAnyArgs().DeleteAsync(default);
|
||||
await applicationCacheService.DidNotReceiveWithAnyArgs().DeleteOrganizationAbilityAsync(default);
|
||||
}
|
||||
|
||||
private void RestoreRevokeUser_Setup(Organization organization, OrganizationUser owner, OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organizationUser.OrganizationId).Returns(organization);
|
||||
sutProvider.GetDependency<ICurrentContext>().OrganizationOwner(organization.Id).Returns(true);
|
||||
sutProvider.GetDependency<ICurrentContext>().ManageUsers(organization.Id).Returns(true);
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
organizationUserRepository.GetManyByOrganizationAsync(organizationUser.OrganizationId, OrganizationUserType.Owner)
|
||||
.Returns(new[] { owner });
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RevokeUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var eventService = sutProvider.GetDependency<IEventService>();
|
||||
|
||||
await sutProvider.Sut.RevokeUserAsync(organizationUser, owner.Id);
|
||||
|
||||
await organizationUserRepository.Received().RevokeAsync(organizationUser.Id);
|
||||
await eventService.Received()
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RevokeUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var eventService = sutProvider.GetDependency<IEventService>();
|
||||
|
||||
await sutProvider.Sut.RevokeUserAsync(organizationUser, eventSystemUser);
|
||||
|
||||
await organizationUserRepository.Received().RevokeAsync(organizationUser.Id);
|
||||
await eventService.Received()
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Revoked, eventSystemUser);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
var userService = Substitute.For<IUserService>();
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var eventService = sutProvider.GetDependency<IEventService>();
|
||||
|
||||
await sutProvider.Sut.RestoreUserAsync(organizationUser, owner.Id, userService);
|
||||
|
||||
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
|
||||
await eventService.Received()
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RestoreUser_WithEventSystemUser_Success(Organization organization, [OrganizationUser(OrganizationUserStatusType.Confirmed, OrganizationUserType.Owner)] OrganizationUser owner,
|
||||
[OrganizationUser(OrganizationUserStatusType.Revoked)] OrganizationUser organizationUser, EventSystemUser eventSystemUser, SutProvider<OrganizationService> sutProvider)
|
||||
{
|
||||
RestoreRevokeUser_Setup(organization, owner, organizationUser, sutProvider);
|
||||
var userService = Substitute.For<IUserService>();
|
||||
var organizationUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
|
||||
var eventService = sutProvider.GetDependency<IEventService>();
|
||||
|
||||
await sutProvider.Sut.RestoreUserAsync(organizationUser, eventSystemUser, userService);
|
||||
|
||||
await organizationUserRepository.Received().RestoreAsync(organizationUser.Id, OrganizationUserStatusType.Invited);
|
||||
await eventService.Received()
|
||||
.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_Restored, eventSystemUser);
|
||||
}
|
||||
}
|
||||
|
92
util/Migrator/DbScripts/2022-09-26_00_EventsSystemUser.sql
Normal file
92
util/Migrator/DbScripts/2022-09-26_00_EventsSystemUser.sql
Normal file
@ -0,0 +1,92 @@
|
||||
-- Add column SystemUser to Event table
|
||||
IF COL_LENGTH('[dbo].[Event]', 'SystemUser') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE
|
||||
[dbo].[Event]
|
||||
ADD
|
||||
[SystemUser] TINYINT NULL;
|
||||
END
|
||||
GO
|
||||
|
||||
-- Recreate EventView so that it includes the SystemUser column
|
||||
IF OBJECT_ID('[dbo].[EventView]') IS NOT NULL
|
||||
BEGIN
|
||||
DROP VIEW [dbo].[EventView]
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE VIEW [dbo].[EventView]
|
||||
AS
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[Event]
|
||||
GO
|
||||
|
||||
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
|
||||
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]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@Id,
|
||||
@Type,
|
||||
@UserId,
|
||||
@OrganizationId,
|
||||
@InstallationId,
|
||||
@ProviderId,
|
||||
@CipherId,
|
||||
@CollectionId,
|
||||
@PolicyId,
|
||||
@GroupId,
|
||||
@OrganizationUserId,
|
||||
@ProviderUserId,
|
||||
@ProviderOrganizationId,
|
||||
@ActingUserId,
|
||||
@DeviceType,
|
||||
@IpAddress,
|
||||
@Date,
|
||||
@SystemUser
|
||||
)
|
||||
END
|
||||
GO
|
1599
util/MySqlMigrations/Migrations/20220927142038_EventsSystemUser.Designer.cs
generated
Normal file
1599
util/MySqlMigrations/Migrations/20220927142038_EventsSystemUser.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
public partial class EventsSystemUser : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "SystemUser",
|
||||
table: "Event",
|
||||
type: "tinyint unsigned",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SystemUser",
|
||||
table: "Event");
|
||||
}
|
||||
}
|
@ -351,6 +351,9 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
b.Property<Guid?>("ProviderUserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<byte?>("SystemUser")
|
||||
.HasColumnType("tinyint unsigned");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
1610
util/PostgresMigrations/Migrations/20220927142152_EventsSystemUser.Designer.cs
generated
Normal file
1610
util/PostgresMigrations/Migrations/20220927142152_EventsSystemUser.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
public partial class EventsSystemUser : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "SystemUser",
|
||||
table: "Event",
|
||||
type: "smallint",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SystemUser",
|
||||
table: "Event");
|
||||
}
|
||||
}
|
@ -352,6 +352,9 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
b.Property<Guid?>("ProviderUserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte?>("SystemUser")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("integer");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user