using Bit.Core.AdminConsole.Context; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities.Provider; using Bit.Core.AdminConsole.Models.Data.Provider; using Bit.Core.AdminConsole.Repositories; using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Data; using Bit.Core.Models.Data.Organizations; using Bit.Core.Repositories; using Bit.Core.Services; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.Helpers; using NSubstitute; using Xunit; namespace Bit.Core.Test.Services; [SutProviderCustomize] public class EventServiceTests { public static IEnumerable InstallationIdTestCases => TestCaseHelper.GetCombinationsOfMultipleLists( new object[] { Guid.NewGuid(), null }, Enum.GetValues().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, string ipAddress, DeviceType deviceType, SutProvider sutProvider) { var orgAbilities = new Dictionary() { { group.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency().GetOrganizationAbilitiesAsync(Arg.Any>()).Returns(orgAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); await sutProvider.Sut.LogGroupEventAsync(group, eventType, date); var expected = new List() { new EventMessage() { IpAddress = ipAddress, DeviceType = deviceType, OrganizationId = group.OrganizationId, GroupId = group.Id, Type = eventType, ActingUserId = actingUserId, ProviderId = providerId, Date = date, SystemUser = null } }; await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(expected, new[] { "IdempotencyId" }))); } [Theory, BitAutoData] public async Task LogGroupEvent_WithEventSystemUser_LogsRequiredInfo(Group group, EventType eventType, EventSystemUser eventSystemUser, DateTime date, Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider sutProvider) { var orgAbilities = new Dictionary() { { group.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency().GetOrganizationAbilitiesAsync(Arg.Any>()).Returns(orgAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); await sutProvider.Sut.LogGroupEventAsync(group, eventType, eventSystemUser, date); var eventMessage = new EventMessage() { IpAddress = ipAddress, DeviceType = deviceType, OrganizationId = group.OrganizationId, GroupId = group.Id, Type = eventType, ActingUserId = actingUserId, ProviderId = providerId, Date = date, SystemUser = eventSystemUser }; if (eventSystemUser is EventSystemUser.SCIM) { eventMessage.DeviceType = DeviceType.Server; } var expected = new List() { eventMessage }; await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(expected, new[] { "IdempotencyId" }))); } [Theory] [BitMemberAutoData(nameof(InstallationIdTestCases))] public async Task LogOrganizationEvent_ProvidesInstallationId(Guid? installationId, EventType eventType, Organization organization, SutProvider sutProvider) { organization.Enabled = true; organization.UseEvents = true; sutProvider.GetDependency().InstallationId.Returns(installationId); await sutProvider.Sut.LogOrganizationEventAsync(organization, eventType); await sutProvider.GetDependency().Received(1).CreateAsync(Arg.Is(e => e.OrganizationId == organization.Id && e.Type == eventType && e.InstallationId == installationId)); } [Theory, BitAutoData] public async Task LogOrganizationEvent_WithEventSystemUser_LogsRequiredInfo(Organization organization, EventType eventType, EventSystemUser eventSystemUser, DateTime date, Guid providerId, SutProvider sutProvider) { organization.Enabled = true; organization.UseEvents = true; sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); await sutProvider.Sut.LogOrganizationEventAsync(organization, eventType, eventSystemUser, date); await sutProvider.GetDependency().Received(1).CreateAsync(Arg.Is(e => e.OrganizationId == organization.Id && e.Type == eventType && e.SystemUser == eventSystemUser && e.DeviceType == DeviceType.Server && e.Date == date && e.ProviderId == providerId)); } [Theory, BitAutoData] public async Task LogOrganizationUserEvent_LogsRequiredInfo(OrganizationUser orgUser, EventType eventType, DateTime date, Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider sutProvider) { var orgAbilities = new Dictionary() { {orgUser.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency().GetOrganizationAbilitiesAsync(Arg.Any>()).Returns(orgAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, date); var expected = new List() { new EventMessage() { IpAddress = ipAddress, DeviceType = deviceType, OrganizationId = orgUser.OrganizationId, UserId = orgUser.UserId, OrganizationUserId = orgUser.Id, ProviderId = providerId, Type = eventType, ActingUserId = actingUserId, Date = date } }; await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(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, SutProvider sutProvider) { var orgAbilities = new Dictionary() { {orgUser.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency().GetOrganizationAbilitiesAsync(Arg.Any>()).Returns(orgAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); await sutProvider.Sut.LogOrganizationUserEventAsync(orgUser, eventType, eventSystemUser, date); var expected = new List() { new EventMessage() { IpAddress = ipAddress, OrganizationId = orgUser.OrganizationId, UserId = orgUser.UserId, OrganizationUserId = orgUser.Id, ProviderId = providerId, Type = eventType, ActingUserId = actingUserId, Date = date, SystemUser = eventSystemUser } }; await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(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 sutProvider) { var providerAbilities = new Dictionary() { {providerUser.ProviderId, new ProviderAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency().GetProviderAbilitiesAsync(Arg.Any>()).Returns(providerAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); await sutProvider.Sut.LogProviderUserEventAsync(providerUser, eventType, date); var expected = new List() { new EventMessage() { IpAddress = ipAddress, DeviceType = deviceType, ProviderId = providerUser.ProviderId, UserId = providerUser.UserId, ProviderUserId = providerUser.Id, Type = eventType, ActingUserId = actingUserId, Date = date } }; await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(expected, new[] { "IdempotencyId" }))); } [Theory, BitAutoData] public async Task LogCollectionEvent_LogsRequiredInfo(Collection collection, EventType eventType, DateTime date, Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider sutProvider) { // Arrange var orgAbilities = new Dictionary() { { collection.OrganizationId, new OrganizationAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency().GetOrganizationAbilitiesAsync(Arg.Any>()).Returns(orgAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); // Act await sutProvider.Sut.LogCollectionEventAsync(collection, eventType, date); // Assert var expected = new List() { new EventMessage() { IpAddress = ipAddress, DeviceType = deviceType, OrganizationId = collection.OrganizationId, CollectionId = collection.Id, Type = eventType, ActingUserId = actingUserId, ProviderId = providerId, Date = date } }; await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(expected, new[] { "IdempotencyId" }))); } [Theory, BitAutoData] public async Task LogCollectionEvent_WhenEventsDisabled_DoesNotLog(Collection collection, EventType eventType, DateTime date, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .GetOrganizationAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); // Act await sutProvider.Sut.LogCollectionEventAsync(collection, eventType, date); // Assert await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is>(e => !e.Any())); } [Theory, BitAutoData] public async Task LogPolicyEvent_LogsRequiredInfo(Policy policy, EventType eventType, DateTime date, Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .GetOrganizationAbilityAsync(policy.OrganizationId) .Returns(new OrganizationAbility { UseEvents = true, Enabled = true }); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); // Act await sutProvider.Sut.LogPolicyEventAsync(policy, eventType, date); // Assert await sutProvider.GetDependency().Received(1).CreateAsync(Arg.Is(e => e.OrganizationId == policy.OrganizationId && e.PolicyId == policy.Id && e.Type == eventType && e.ActingUserId == actingUserId && e.ProviderId == providerId && e.Date == date)); } [Theory, BitAutoData] public async Task LogPolicyEvent_WhenEventsDisabled_DoesNotLog(Policy policy, EventType eventType, DateTime date, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .GetOrganizationAbilityAsync(policy.OrganizationId) .Returns(new OrganizationAbility { UseEvents = false, Enabled = true }); // Act await sutProvider.Sut.LogPolicyEventAsync(policy, eventType, date); // Assert await sutProvider.GetDependency().DidNotReceiveWithAnyArgs().CreateAsync(default); } [Theory, BitAutoData] public async Task LogProviderOrganizationEventsAsync_LogsRequiredInfo(Provider provider, ICollection providerOrganizations, EventType eventType, DateTime date, Guid actingUserId, Guid providerId, string ipAddress, DeviceType deviceType, SutProvider sutProvider) { foreach (var providerOrganization in providerOrganizations) { providerOrganization.ProviderId = provider.Id; } var providerAbilities = new Dictionary() { { provider.Id, new ProviderAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency().GetProviderAbilitiesAsync(Arg.Any>()).Returns(providerAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); sutProvider.GetDependency().ProviderIdForOrg(Arg.Any()).Returns(providerId); await sutProvider.Sut.LogProviderOrganizationEventsAsync(providerOrganizations.Select(po => (po, eventType, (DateTime?)date))); var expected = providerOrganizations.Select(po => new EventMessage() { DeviceType = deviceType, IpAddress = ipAddress, ProviderId = provider.Id, ProviderOrganizationId = po.Id, Type = eventType, ActingUserId = actingUserId, Date = date }).ToList(); await sutProvider.GetDependency().Received(1).CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(expected, new[] { "IdempotencyId" }))); } [Theory, BitAutoData] public async Task LogUserEvent_IncludeAcceptedStatusOrgs_AcceptedOrgUser_CreatesOrgScopedEvent( Guid userId, EventType eventType, OrganizationUser orgUser, SutProvider sutProvider) { orgUser.UserId = userId; orgUser.Status = OrganizationUserStatusType.Accepted; var orgAbilities = new Dictionary { { orgUser.OrganizationId, new OrganizationAbility { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency() .GetOrganizationAbilitiesAsync(Arg.Any>()) .Returns(orgAbilities); sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); sutProvider.GetDependency() .GetManyByUserAsync(userId) .Returns(new List { orgUser }); sutProvider.GetDependency() .ProviderMembershipAsync(Arg.Any(), userId) .Returns(new List()); await sutProvider.Sut.LogUserEventAsync(userId, eventType, includeAcceptedStatusOrgs: true); await sutProvider.GetDependency() .Received(1) .CreateManyAsync(Arg.Is>(events => events.Count() == 2 && events.Any(e => e.OrganizationId == null && e.UserId == userId && e.Type == eventType) && events.Any(e => e.OrganizationId == orgUser.OrganizationId && e.UserId == userId && e.Type == eventType))); } [Theory, BitAutoData] public async Task LogUserEvent_IncludeAcceptedStatusOrgs_InvitedOrgUser_DoesNotCreateOrgScopedEvent( Guid userId, EventType eventType, OrganizationUser orgUser, SutProvider sutProvider) { orgUser.UserId = userId; orgUser.Status = OrganizationUserStatusType.Invited; var orgAbilities = new Dictionary { { orgUser.OrganizationId, new OrganizationAbility { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency() .GetOrganizationAbilitiesAsync(Arg.Any>()) .Returns(orgAbilities); sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); sutProvider.GetDependency() .GetManyByUserAsync(userId) .Returns(new List { orgUser }); sutProvider.GetDependency() .ProviderMembershipAsync( Arg.Any(), userId) .Returns(new List()); await sutProvider.Sut.LogUserEventAsync(userId, eventType, includeAcceptedStatusOrgs: true); await sutProvider.GetDependency() .Received(1) .CreateAsync(Arg.Is(e => e.OrganizationId == null && e.UserId == userId && e.Type == eventType)); await sutProvider.GetDependency() .DidNotReceiveWithAnyArgs() .CreateManyAsync(default); } [Theory, BitAutoData] public async Task LogProviderUsersEventAsync_LogsRequiredInfo(Provider provider, ICollection providerUsers, EventType eventType, DateTime date, Guid actingUserId, string ipAddress, DeviceType deviceType, SutProvider sutProvider) { // Arrange foreach (var providerUser in providerUsers) { providerUser.ProviderId = provider.Id; } var providerAbilities = new Dictionary() { { provider.Id, new ProviderAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(providerAbilities); sutProvider.GetDependency().UserId.Returns(actingUserId); sutProvider.GetDependency().IpAddress.Returns(ipAddress); sutProvider.GetDependency().DeviceType.Returns(deviceType); // Act await sutProvider.Sut.LogProviderUsersEventAsync(providerUsers.Select(providerUser => (providerUser, eventType, (DateTime?)date))); // Assert var expected = providerUsers.Select(pu => new EventMessage() { IpAddress = ipAddress, DeviceType = deviceType, ProviderId = provider.Id, UserId = pu.UserId, ProviderUserId = pu.Id, Type = eventType, ActingUserId = actingUserId, Date = date }).ToList(); await sutProvider.GetDependency().Received(1) .CreateManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(expected, new[] { "IdempotencyId" }))); } [Theory, BitAutoData] public async Task LogProviderUsersEventAsync_WhenEventsDisabled_DoesNotLog(Provider provider, ICollection providerUsers, EventType eventType, DateTime date, SutProvider sutProvider) { // Arrange foreach (var providerUser in providerUsers) { providerUser.ProviderId = provider.Id; } sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary { { provider.Id, new ProviderAbility() { UseEvents = false, Enabled = true } } }); // Act await sutProvider.Sut.LogProviderUsersEventAsync(providerUsers.Select(providerUser => (providerUser, eventType, (DateTime?)date))); // Assert await sutProvider.GetDependency().Received(1) .CreateManyAsync(Arg.Is>(events => !events.Any())); } [Theory, BitAutoData] public async Task LogProviderUsersEventAsync_QueriesOnlyRelevantProviderIds( ICollection providerUsers, EventType eventType, DateTime date, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); // Act await sutProvider.Sut.LogProviderUsersEventAsync(providerUsers.Select(pu => (pu, eventType, (DateTime?)date))); // Assert var expectedIds = providerUsers.Select(pu => pu.ProviderId).Distinct(); await sutProvider.GetDependency().Received(1) .GetProviderAbilitiesAsync(Arg.Is>(ids => ids.OrderBy(x => x).SequenceEqual(expectedIds.OrderBy(x => x)))); } [Theory, BitAutoData] public async Task LogProviderOrganizationEventsAsync_WhenEventsDisabled_DoesNotLog(Provider provider, ICollection providerOrganizations, EventType eventType, DateTime date, SutProvider sutProvider) { // Arrange foreach (var providerOrganization in providerOrganizations) { providerOrganization.ProviderId = provider.Id; } sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary { { provider.Id, new ProviderAbility() { UseEvents = false, Enabled = true } } }); // Act await sutProvider.Sut.LogProviderOrganizationEventsAsync( providerOrganizations.Select(po => (po, eventType, (DateTime?)date))); // Assert await sutProvider.GetDependency().Received(1) .CreateManyAsync(Arg.Is>(events => !events.Any())); } [Theory, BitAutoData] public async Task LogProviderOrganizationEventsAsync_QueriesOnlyRelevantProviderIds( ICollection providerOrganizations, EventType eventType, DateTime date, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); // Act await sutProvider.Sut.LogProviderOrganizationEventsAsync( providerOrganizations.Select(providerOrganization => (providerOrganization, eventType, (DateTime?)date))); // Assert var expectedIds = providerOrganizations.Select(po => po.ProviderId).Distinct(); await sutProvider.GetDependency().Received(1) .GetProviderAbilitiesAsync(Arg.Is>(ids => ids.OrderBy(x => x).SequenceEqual(expectedIds.OrderBy(x => x)))); } [Theory, BitAutoData] public async Task LogUserEventAsync_WithProviderMembership_LogsProviderEvent( Guid userId, EventType eventType, CurrentContextProvider provider, SutProvider sutProvider) { // Arrange var providerAbilities = new Dictionary { { provider.Id, new ProviderAbility() { UseEvents = true, Enabled = true } } }; sutProvider.GetDependency() .GetOrganizationAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(providerAbilities); sutProvider.GetDependency() .OrganizationMembershipAsync(Arg.Any(), userId) .Returns(new List()); sutProvider.GetDependency() .ProviderMembershipAsync(Arg.Any(), userId) .Returns(new List { provider }); // Act await sutProvider.Sut.LogUserEventAsync(userId, eventType); // Assert await sutProvider.GetDependency().Received(1) .CreateManyAsync(Arg.Is>(events => events.Any(e => e.ProviderId == provider.Id && e.UserId == userId && e.Type == eventType))); } [Theory, BitAutoData] public async Task LogUserEventAsync_QueriesOnlyMemberProviderIds( Guid userId, EventType eventType, ICollection providers, SutProvider sutProvider) { // Arrange sutProvider.GetDependency() .GetOrganizationAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); sutProvider.GetDependency() .GetProviderAbilitiesAsync(Arg.Any>()) .Returns(new Dictionary()); sutProvider.GetDependency() .OrganizationMembershipAsync(Arg.Any(), userId) .Returns(new List()); sutProvider.GetDependency() .ProviderMembershipAsync(Arg.Any(), userId) .Returns(providers.ToList()); // Act await sutProvider.Sut.LogUserEventAsync(userId, eventType); // Assert var expectedIds = providers.Select(provider => provider.Id); await sutProvider.GetDependency().Received(1) .GetProviderAbilitiesAsync(Arg.Is>(ids => ids.OrderBy(x => x).SequenceEqual(expectedIds.OrderBy(x => x)))); } }