From 24fade1263469e8d561c889bf509445766f1401c Mon Sep 17 00:00:00 2001 From: voommen-livefront Date: Thu, 9 Apr 2026 08:49:07 -0500 Subject: [PATCH 1/3] PM-22228 Added Phishing events --- src/Core/Dirt/Enums/EventType.cs | 6 ++++++ src/Events/Controllers/CollectController.cs | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/Core/Dirt/Enums/EventType.cs b/src/Core/Dirt/Enums/EventType.cs index 49e627535a75..f230ec3093a0 100644 --- a/src/Core/Dirt/Enums/EventType.cs +++ b/src/Core/Dirt/Enums/EventType.cs @@ -125,4 +125,10 @@ public enum EventType : int ServiceAccount_GroupRemoved = 2303, ServiceAccount_Created = 2304, ServiceAccount_Deleted = 2305, + + PhishingBlocker_SiteAccessed = 2400, + PhishingBlocker_SiteExited = 2401, + PhishingBlocker_Bypassed = 2402, + + } diff --git a/src/Events/Controllers/CollectController.cs b/src/Events/Controllers/CollectController.cs index 0e95fd057d9a..c287aa18a955 100644 --- a/src/Events/Controllers/CollectController.cs +++ b/src/Events/Controllers/CollectController.cs @@ -133,6 +133,9 @@ public async Task Post([FromBody] IEnumerable model) case EventType.Organization_ClientExportedVault: case EventType.Organization_AutoConfirmEnabled_Admin: case EventType.Organization_AutoConfirmDisabled_Admin: + case EventType.PhishingBlocker_SiteAccessed: + case EventType.PhishingBlocker_SiteExited: + case EventType.PhishingBlocker_Bypassed: if (!eventModel.OrganizationId.HasValue) { continue; From 2a2df30157803012f8933080f62a790b3f46a62c Mon Sep 17 00:00:00 2001 From: voommen-livefront Date: Thu, 9 Apr 2026 09:22:05 -0500 Subject: [PATCH 2/3] PM-22228 updated event to test user access to org and added unit tests --- src/Events/Controllers/CollectController.cs | 19 ++++- .../Controllers/CollectControllerTests.cs | 79 +++++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/Events/Controllers/CollectController.cs b/src/Events/Controllers/CollectController.cs index c287aa18a955..abcc64ea5a05 100644 --- a/src/Events/Controllers/CollectController.cs +++ b/src/Events/Controllers/CollectController.cs @@ -133,9 +133,6 @@ public async Task Post([FromBody] IEnumerable model) case EventType.Organization_ClientExportedVault: case EventType.Organization_AutoConfirmEnabled_Admin: case EventType.Organization_AutoConfirmDisabled_Admin: - case EventType.PhishingBlocker_SiteAccessed: - case EventType.PhishingBlocker_SiteExited: - case EventType.PhishingBlocker_Bypassed: if (!eventModel.OrganizationId.HasValue) { continue; @@ -149,7 +146,23 @@ public async Task Post([FromBody] IEnumerable model) await _eventService.LogOrganizationEventAsync(organization, eventModel.Type, eventModel.Date); break; + case EventType.PhishingBlocker_SiteAccessed: + case EventType.PhishingBlocker_SiteExited: + case EventType.PhishingBlocker_Bypassed: + if (!eventModel.OrganizationId.HasValue) + { + continue; + } + // Verify the user belongs to this organization + var orgUserContext = await _organizationUserRepository.GetByOrganizationAsync(eventModel.OrganizationId.Value, _currentContext.UserId.Value); + if (orgUserContext == null) + { + continue; + } + + await _eventService.LogOrganizationUserEventAsync(orgUserContext, eventModel.Type, eventModel.Date); + break; default: continue; } diff --git a/test/Events.Test/Controllers/CollectControllerTests.cs b/test/Events.Test/Controllers/CollectControllerTests.cs index 3d8175a84e7f..d457f876544a 100644 --- a/test/Events.Test/Controllers/CollectControllerTests.cs +++ b/test/Events.Test/Controllers/CollectControllerTests.cs @@ -819,4 +819,83 @@ public async Task Post_OrganizationAutoConfirmAdmin_WithNullOrg_SkipsEvent( await _organizationRepository.Received(1).GetByIdAsync(orgId); await _eventService.DidNotReceiveWithAnyArgs().LogOrganizationEventAsync(default, default, default); } + + [Theory] + [BitAutoData(EventType.PhishingBlocker_SiteAccessed)] + [BitAutoData(EventType.PhishingBlocker_SiteExited)] + [BitAutoData(EventType.PhishingBlocker_Bypassed)] + public async Task Post_PhishingBlocker_WithValidOrgUser_LogsOrganizationUserEvent( + EventType type, Guid userId, Guid orgId, OrganizationUser orgUser) + { + _currentContext.UserId.Returns(userId); + orgUser.OrganizationId = orgId; + _organizationUserRepository.GetByOrganizationAsync(orgId, userId).Returns(orgUser); + var eventDate = DateTime.UtcNow; + var events = new List + { + new EventModel + { + Type = type, + OrganizationId = orgId, + Date = eventDate + } + }; + + var result = await _sut.Post(events); + + Assert.IsType(result); + await _organizationUserRepository.Received(1).GetByOrganizationAsync(orgId, userId); + await _eventService.Received(1).LogOrganizationUserEventAsync( + Arg.Is(o => o == orgUser), type, eventDate); + } + + [Theory] + [BitAutoData(EventType.PhishingBlocker_SiteAccessed)] + [BitAutoData(EventType.PhishingBlocker_SiteExited)] + [BitAutoData(EventType.PhishingBlocker_Bypassed)] + public async Task Post_PhishingBlocker_WithoutOrgId_SkipsEvent(EventType type, Guid userId) + { + _currentContext.UserId.Returns(userId); + var events = new List + { + new EventModel + { + Type = type, + OrganizationId = null, + Date = DateTime.UtcNow + } + }; + + var result = await _sut.Post(events); + + Assert.IsType(result); + await _organizationUserRepository.DidNotReceiveWithAnyArgs().GetByOrganizationAsync(default, default); + await _eventService.DidNotReceiveWithAnyArgs().LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(EventType.PhishingBlocker_SiteAccessed)] + [BitAutoData(EventType.PhishingBlocker_SiteExited)] + [BitAutoData(EventType.PhishingBlocker_Bypassed)] + public async Task Post_PhishingBlocker_WithNullOrgUser_SkipsEvent( + EventType type, Guid userId, Guid orgId) + { + _currentContext.UserId.Returns(userId); + _organizationUserRepository.GetByOrganizationAsync(orgId, userId).Returns((OrganizationUser)null); + var events = new List + { + new EventModel + { + Type = type, + OrganizationId = orgId, + Date = DateTime.UtcNow + } + }; + + var result = await _sut.Post(events); + + Assert.IsType(result); + await _organizationUserRepository.Received(1).GetByOrganizationAsync(orgId, userId); + await _eventService.DidNotReceiveWithAnyArgs().LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); + } } From 1b0ea9fd11157c7a435d9fe17304e2cd8d286e45 Mon Sep 17 00:00:00 2001 From: voommen-livefront Date: Thu, 9 Apr 2026 14:54:36 -0500 Subject: [PATCH 3/3] PM-22228 before logging the event check that the org uses phishingblocker --- src/Events/Controllers/CollectController.cs | 6 +++ .../Controllers/CollectControllerTests.cs | 39 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Events/Controllers/CollectController.cs b/src/Events/Controllers/CollectController.cs index abcc64ea5a05..80f1bbdd091e 100644 --- a/src/Events/Controllers/CollectController.cs +++ b/src/Events/Controllers/CollectController.cs @@ -161,6 +161,12 @@ public async Task Post([FromBody] IEnumerable model) continue; } + var organizationForPhishingEvent = await _organizationRepository.GetByIdAsync(eventModel.OrganizationId.Value); + if (organizationForPhishingEvent == null || !organizationForPhishingEvent.UsePhishingBlocker) + { + continue; + } + await _eventService.LogOrganizationUserEventAsync(orgUserContext, eventModel.Type, eventModel.Date); break; default: diff --git a/test/Events.Test/Controllers/CollectControllerTests.cs b/test/Events.Test/Controllers/CollectControllerTests.cs index d457f876544a..df4fbb61a12b 100644 --- a/test/Events.Test/Controllers/CollectControllerTests.cs +++ b/test/Events.Test/Controllers/CollectControllerTests.cs @@ -825,11 +825,14 @@ public async Task Post_OrganizationAutoConfirmAdmin_WithNullOrg_SkipsEvent( [BitAutoData(EventType.PhishingBlocker_SiteExited)] [BitAutoData(EventType.PhishingBlocker_Bypassed)] public async Task Post_PhishingBlocker_WithValidOrgUser_LogsOrganizationUserEvent( - EventType type, Guid userId, Guid orgId, OrganizationUser orgUser) + EventType type, Guid userId, Guid orgId, OrganizationUser orgUser, Organization organization) { _currentContext.UserId.Returns(userId); orgUser.OrganizationId = orgId; + organization.Id = orgId; + organization.UsePhishingBlocker = true; _organizationUserRepository.GetByOrganizationAsync(orgId, userId).Returns(orgUser); + _organizationRepository.GetByIdAsync(orgId).Returns(organization); var eventDate = DateTime.UtcNow; var events = new List { @@ -844,6 +847,7 @@ public async Task Post_PhishingBlocker_WithValidOrgUser_LogsOrganizationUserEven var result = await _sut.Post(events); Assert.IsType(result); + await _organizationRepository.Received(1).GetByIdAsync(orgId); await _organizationUserRepository.Received(1).GetByOrganizationAsync(orgId, userId); await _eventService.Received(1).LogOrganizationUserEventAsync( Arg.Is(o => o == orgUser), type, eventDate); @@ -869,6 +873,7 @@ public async Task Post_PhishingBlocker_WithoutOrgId_SkipsEvent(EventType type, G var result = await _sut.Post(events); Assert.IsType(result); + await _organizationRepository.DidNotReceiveWithAnyArgs().GetByIdAsync(default); await _organizationUserRepository.DidNotReceiveWithAnyArgs().GetByOrganizationAsync(default, default); await _eventService.DidNotReceiveWithAnyArgs().LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); } @@ -896,6 +901,38 @@ public async Task Post_PhishingBlocker_WithNullOrgUser_SkipsEvent( Assert.IsType(result); await _organizationUserRepository.Received(1).GetByOrganizationAsync(orgId, userId); + await _organizationRepository.DidNotReceiveWithAnyArgs().GetByIdAsync(default); + await _eventService.DidNotReceiveWithAnyArgs().LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); + } + + [Theory] + [BitAutoData(EventType.PhishingBlocker_SiteAccessed)] + [BitAutoData(EventType.PhishingBlocker_SiteExited)] + [BitAutoData(EventType.PhishingBlocker_Bypassed)] + public async Task Post_PhishingBlocker_WithPhishingBlockerDisabled_SkipsEvent( + EventType type, Guid userId, Guid orgId, Organization organization, OrganizationUser orgUser) + { + _currentContext.UserId.Returns(userId); + organization.Id = orgId; + organization.UsePhishingBlocker = false; + orgUser.OrganizationId = orgId; + _organizationRepository.GetByIdAsync(orgId).Returns(organization); + _organizationUserRepository.GetByOrganizationAsync(orgId, userId).Returns(orgUser); + var events = new List + { + new EventModel + { + Type = type, + OrganizationId = orgId, + Date = DateTime.UtcNow + } + }; + + var result = await _sut.Post(events); + + Assert.IsType(result); + await _organizationUserRepository.Received(1).GetByOrganizationAsync(orgId, userId); + await _organizationRepository.Received(1).GetByIdAsync(orgId); await _eventService.DidNotReceiveWithAnyArgs().LogOrganizationUserEventAsync(Arg.Any(), Arg.Any(), Arg.Any()); } }