From 12f21b0c33a0719a5ae6593907775e57f7314339 Mon Sep 17 00:00:00 2001 From: Daniel James Smith Date: Tue, 16 May 2023 16:21:57 +0200 Subject: [PATCH] [PM-214] Extend Reference Events (#2926) * Extend ReferenceEvents Add ClientId and ClientVersion Modify all callsites to pass in currentContext if available to fill ClientId and ClientVersion * Extend ReferenceEvent to save if Send has notes --- .../Controllers/OrganizationsController.cs | 8 ++++++-- .../Controllers/SecretsController.cs | 2 +- src/Billing/Controllers/StripeController.cs | 10 +++++++--- .../Groups/CreateGroupCommand.cs | 10 +++++++--- .../Implementations/CollectionService.cs | 2 +- .../Implementations/OrganizationService.cs | 20 +++++++++---------- .../Services/Implementations/UserService.cs | 16 +++++++-------- .../Tools/Models/Business/ReferenceEvent.cs | 13 +++++++++++- .../Services/Implementations/SendService.cs | 3 +++ .../Services/Implementations/CipherService.cs | 4 ++-- 10 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/Admin/Controllers/OrganizationsController.cs b/src/Admin/Controllers/OrganizationsController.cs index d95e88341..01e55f86a 100644 --- a/src/Admin/Controllers/OrganizationsController.cs +++ b/src/Admin/Controllers/OrganizationsController.cs @@ -2,6 +2,7 @@ using Bit.Admin.Models; using Bit.Admin.Services; using Bit.Admin.Utilities; +using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.OrganizationConnectionConfigs; @@ -40,6 +41,7 @@ public class OrganizationsController : Controller private readonly IProviderRepository _providerRepository; private readonly ILogger _logger; private readonly IAccessControlService _accessControlService; + private readonly ICurrentContext _currentContext; public OrganizationsController( IOrganizationService organizationService, @@ -59,7 +61,8 @@ public class OrganizationsController : Controller IUserService userService, IProviderRepository providerRepository, ILogger logger, - IAccessControlService accessControlService) + IAccessControlService accessControlService, + ICurrentContext currentContext) { _organizationService = organizationService; _organizationRepository = organizationRepository; @@ -79,6 +82,7 @@ public class OrganizationsController : Controller _providerRepository = providerRepository; _logger = logger; _accessControlService = accessControlService; + _currentContext = currentContext; } [RequirePermission(Permission.Org_List_View)] @@ -174,7 +178,7 @@ public class OrganizationsController : Controller await _organizationRepository.ReplaceAsync(organization); await _applicationCacheService.UpsertOrganizationAbilityAsync(organization); - await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization) + await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationEditedByAdmin, organization, _currentContext) { EventRaisedByUser = _userService.GetUserName(User), SalesAssistedTrialStarted = model.SalesAssistedTrialStarted, diff --git a/src/Api/SecretsManager/Controllers/SecretsController.cs b/src/Api/SecretsManager/Controllers/SecretsController.cs index dc9f22bf1..549f8fe04 100644 --- a/src/Api/SecretsManager/Controllers/SecretsController.cs +++ b/src/Api/SecretsManager/Controllers/SecretsController.cs @@ -120,7 +120,7 @@ public class SecretsController : Controller await _eventService.LogServiceAccountSecretEventAsync(userId, secret, EventType.Secret_Retrieved); var org = await _organizationRepository.GetByIdAsync(secret.OrganizationId); - await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.SmServiceAccountAccessedSecret, org)); + await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.SmServiceAccountAccessedSecret, org, _currentContext)); } return new SecretResponseModel(secret, access.Read, access.Write); diff --git a/src/Billing/Controllers/StripeController.cs b/src/Billing/Controllers/StripeController.cs index e533bcefc..fff638bf2 100644 --- a/src/Billing/Controllers/StripeController.cs +++ b/src/Billing/Controllers/StripeController.cs @@ -1,4 +1,5 @@ using Bit.Billing.Constants; +using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces; @@ -39,6 +40,7 @@ public class StripeController : Controller private readonly IReferenceEventService _referenceEventService; private readonly ITaxRateRepository _taxRateRepository; private readonly IUserRepository _userRepository; + private readonly ICurrentContext _currentContext; public StripeController( GlobalSettings globalSettings, @@ -55,7 +57,8 @@ public class StripeController : Controller IReferenceEventService referenceEventService, ILogger logger, ITaxRateRepository taxRateRepository, - IUserRepository userRepository) + IUserRepository userRepository, + ICurrentContext currentContext) { _billingSettings = billingSettings?.Value; _hostingEnvironment = hostingEnvironment; @@ -79,6 +82,7 @@ public class StripeController : Controller PublicKey = globalSettings.Braintree.PublicKey, PrivateKey = globalSettings.Braintree.PrivateKey }; + _currentContext = currentContext; } [HttpPost("webhook")] @@ -419,7 +423,7 @@ public class StripeController : Controller var organization = await _organizationRepository.GetByIdAsync(ids.Item1.Value); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.Rebilled, organization) + new ReferenceEvent(ReferenceEventType.Rebilled, organization, _currentContext) { PlanName = organization?.Plan, PlanType = organization?.PlanType, @@ -437,7 +441,7 @@ public class StripeController : Controller var user = await _userRepository.GetByIdAsync(ids.Item2.Value); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.Rebilled, user) + new ReferenceEvent(ReferenceEventType.Rebilled, user, _currentContext) { PlanName = PremiumPlanId, Storage = user?.MaxStorageGb, diff --git a/src/Core/OrganizationFeatures/Groups/CreateGroupCommand.cs b/src/Core/OrganizationFeatures/Groups/CreateGroupCommand.cs index f70367b55..10633a7e9 100644 --- a/src/Core/OrganizationFeatures/Groups/CreateGroupCommand.cs +++ b/src/Core/OrganizationFeatures/Groups/CreateGroupCommand.cs @@ -1,4 +1,5 @@ -using Bit.Core.Entities; +using Bit.Core.Context; +using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Models.Data; @@ -17,17 +18,20 @@ public class CreateGroupCommand : ICreateGroupCommand private readonly IGroupRepository _groupRepository; private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IReferenceEventService _referenceEventService; + private readonly ICurrentContext _currentContext; public CreateGroupCommand( IEventService eventService, IGroupRepository groupRepository, IOrganizationUserRepository organizationUserRepository, - IReferenceEventService referenceEventService) + IReferenceEventService referenceEventService, + ICurrentContext currentContext) { _eventService = eventService; _groupRepository = groupRepository; _organizationUserRepository = organizationUserRepository; _referenceEventService = referenceEventService; + _currentContext = currentContext; } public async Task CreateGroupAsync(Group group, Organization organization, @@ -73,7 +77,7 @@ public class CreateGroupCommand : ICreateGroupCommand await _groupRepository.CreateAsync(group, collections); } - await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.GroupCreated, organization)); + await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.GroupCreated, organization, _currentContext)); } private async Task GroupRepositoryUpdateUsersAsync(Group group, IEnumerable userIds, diff --git a/src/Core/Services/Implementations/CollectionService.cs b/src/Core/Services/Implementations/CollectionService.cs index 4dd5c38b9..d5f650705 100644 --- a/src/Core/Services/Implementations/CollectionService.cs +++ b/src/Core/Services/Implementations/CollectionService.cs @@ -76,7 +76,7 @@ public class CollectionService : ICollectionService } await _eventService.LogCollectionEventAsync(collection, Enums.EventType.Collection_Created); - await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.CollectionCreated, org)); + await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.CollectionCreated, org, _currentContext)); } else { diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index 1cdeb4268..e707df1be 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -147,7 +147,7 @@ public class OrganizationService : IOrganizationService await _paymentService.CancelSubscriptionAsync(organization, eop); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.CancelSubscription, organization) + new ReferenceEvent(ReferenceEventType.CancelSubscription, organization, _currentContext) { EndOfPeriod = endOfPeriod, }); @@ -163,7 +163,7 @@ public class OrganizationService : IOrganizationService await _paymentService.ReinstateSubscriptionAsync(organization); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.ReinstateSubscription, organization)); + new ReferenceEvent(ReferenceEventType.ReinstateSubscription, organization, _currentContext)); } public async Task> UpgradePlanAsync(Guid organizationId, OrganizationUpgrade upgrade) @@ -357,7 +357,7 @@ public class OrganizationService : IOrganizationService if (success) { await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.UpgradePlan, organization) + new ReferenceEvent(ReferenceEventType.UpgradePlan, organization, _currentContext) { PlanName = newPlan.Name, PlanType = newPlan.Type, @@ -393,7 +393,7 @@ public class OrganizationService : IOrganizationService var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb, plan.StripeStoragePlanId); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.AdjustStorage, organization) + new ReferenceEvent(ReferenceEventType.AdjustStorage, organization, _currentContext) { PlanName = plan.Name, PlanType = plan.Type, @@ -530,7 +530,7 @@ public class OrganizationService : IOrganizationService var paymentIntentClientSecret = await _paymentService.AdjustSeatsAsync(organization, plan, additionalSeats, prorationDate); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.AdjustSeats, organization) + new ReferenceEvent(ReferenceEventType.AdjustSeats, organization, _currentContext) { PlanName = plan.Name, PlanType = plan.Type, @@ -681,7 +681,7 @@ public class OrganizationService : IOrganizationService var ownerId = provider ? default : signup.Owner.Id; var returnValue = await SignUpAsync(organization, ownerId, signup.OwnerKey, signup.CollectionName, true); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.Signup, organization) + new ReferenceEvent(ReferenceEventType.Signup, organization, _currentContext) { PlanName = plan.Name, PlanType = plan.Type, @@ -853,7 +853,7 @@ public class OrganizationService : IOrganizationService organization.ExpirationDate.Value >= DateTime.UtcNow; await _paymentService.CancelSubscriptionAsync(organization, eop); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.DeleteAccount, organization)); + new ReferenceEvent(ReferenceEventType.DeleteAccount, organization, _currentContext)); } catch (GatewayException) { } } @@ -1136,7 +1136,7 @@ public class OrganizationService : IOrganizationService await SendInvitesAsync(orgUsers.Concat(limitedCollectionOrgUsers.Select(u => u.Item1)), organization); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.InvitedUsers, organization) + new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, _currentContext) { Users = orgUserInvitedCount }); @@ -1971,7 +1971,7 @@ public class OrganizationService : IOrganizationService } await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.DirectorySynced, organization)); + new ReferenceEvent(ReferenceEventType.DirectorySynced, organization, _currentContext)); } public async Task DeleteSsoUserAsync(Guid userId, Guid? organizationId) @@ -2472,7 +2472,7 @@ public class OrganizationService : IOrganizationService await SendInviteAsync(ownerOrganizationUser, organization, true); await _eventService.LogOrganizationUserEventAsync(ownerOrganizationUser, EventType.OrganizationUser_Invited); - await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationCreatedByAdmin, organization) + await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.OrganizationCreatedByAdmin, organization, _currentContext) { EventRaisedByUser = userService.GetUserName(user), SalesAssistedTrialStarted = salesAssistedTrialStarted, diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index f1a0974a7..e80ea347a 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -253,7 +253,7 @@ public class UserService : UserManager, IUserService, IDisposable await _userRepository.DeleteAsync(user); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.DeleteAccount, user)); + new ReferenceEvent(ReferenceEventType.DeleteAccount, user, _currentContext)); await _pushService.PushLogOutAsync(user.Id); return IdentityResult.Success; } @@ -324,7 +324,7 @@ public class UserService : UserManager, IUserService, IDisposable if (result == IdentityResult.Success) { await _mailService.SendWelcomeEmailAsync(user); - await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user)); + await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext)); } return result; @@ -336,7 +336,7 @@ public class UserService : UserManager, IUserService, IDisposable if (result == IdentityResult.Success) { await _mailService.SendWelcomeEmailAsync(user); - await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user)); + await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext)); } return result; @@ -1049,7 +1049,7 @@ public class UserService : UserManager, IUserService, IDisposable await SaveUserAsync(user); await _pushService.PushSyncVaultAsync(user.Id); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.UpgradePlan, user) + new ReferenceEvent(ReferenceEventType.UpgradePlan, user, _currentContext) { Storage = user.MaxStorageGb, PlanName = PremiumPlanId, @@ -1138,7 +1138,7 @@ public class UserService : UserManager, IUserService, IDisposable var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, user, storageAdjustmentGb, StoragePlanId); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.AdjustStorage, user) + new ReferenceEvent(ReferenceEventType.AdjustStorage, user, _currentContext) { Storage = storageAdjustmentGb, PlanName = StoragePlanId, @@ -1171,7 +1171,7 @@ public class UserService : UserManager, IUserService, IDisposable } await _paymentService.CancelSubscriptionAsync(user, eop, accountDelete); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.CancelSubscription, user) + new ReferenceEvent(ReferenceEventType.CancelSubscription, user, _currentContext) { EndOfPeriod = eop, }); @@ -1181,7 +1181,7 @@ public class UserService : UserManager, IUserService, IDisposable { await _paymentService.ReinstateSubscriptionAsync(user); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.ReinstateSubscription, user)); + new ReferenceEvent(ReferenceEventType.ReinstateSubscription, user, _currentContext)); } public async Task EnablePremiumAsync(Guid userId, DateTime? expirationDate) @@ -1436,7 +1436,7 @@ public class UserService : UserManager, IUserService, IDisposable if (result.Succeeded) { await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.ConfirmEmailAddress, user)); + new ReferenceEvent(ReferenceEventType.ConfirmEmailAddress, user, _currentContext)); } return result; } diff --git a/src/Core/Tools/Models/Business/ReferenceEvent.cs b/src/Core/Tools/Models/Business/ReferenceEvent.cs index 6dc974c24..62dfa2053 100644 --- a/src/Core/Tools/Models/Business/ReferenceEvent.cs +++ b/src/Core/Tools/Models/Business/ReferenceEvent.cs @@ -1,4 +1,5 @@ using System.Text.Json.Serialization; +using Bit.Core.Context; using Bit.Core.Enums; using Bit.Core.Tools.Entities; using Bit.Core.Tools.Enums; @@ -9,7 +10,7 @@ public class ReferenceEvent { public ReferenceEvent() { } - public ReferenceEvent(ReferenceEventType type, IReferenceable source) + public ReferenceEvent(ReferenceEventType type, IReferenceable source, ICurrentContext currentContext) { Type = type; if (source != null) @@ -18,6 +19,11 @@ public class ReferenceEvent Id = source.Id; ReferenceData = source.ReferenceData; } + if (currentContext != null) + { + ClientId = currentContext.ClientId; + ClientVersion = currentContext.ClientVersion; + } } [JsonConverter(typeof(JsonStringEnumConverter))] @@ -52,6 +58,8 @@ public class ReferenceEvent [JsonConverter(typeof(JsonStringEnumConverter))] public SendType? SendType { get; set; } + public bool? SendHasNotes { get; set; } + public int? MaxAccessCount { get; set; } public bool? HasPassword { get; set; } @@ -59,4 +67,7 @@ public class ReferenceEvent public string EventRaisedByUser { get; set; } public bool? SalesAssistedTrialStarted { get; set; } + + public string ClientId { get; set; } + public Version? ClientVersion { get; set; } } diff --git a/src/Core/Tools/Services/Implementations/SendService.cs b/src/Core/Tools/Services/Implementations/SendService.cs index 5347cb010..1c1cfab4e 100644 --- a/src/Core/Tools/Services/Implementations/SendService.cs +++ b/src/Core/Tools/Services/Implementations/SendService.cs @@ -270,6 +270,9 @@ public class SendService : ISendService SendType = send.Type, MaxAccessCount = send.MaxAccessCount, HasPassword = !string.IsNullOrWhiteSpace(send.Password), + SendHasNotes = send.Data?.Contains("Notes"), + ClientId = _currentContext.ClientId, + ClientVersion = _currentContext.ClientVersion }); } diff --git a/src/Core/Vault/Services/Implementations/CipherService.cs b/src/Core/Vault/Services/Implementations/CipherService.cs index 65aeb47ce..868a13a7f 100644 --- a/src/Core/Vault/Services/Implementations/CipherService.cs +++ b/src/Core/Vault/Services/Implementations/CipherService.cs @@ -88,7 +88,7 @@ public class CipherService : ICipherService await _cipherRepository.CreateAsync(cipher, collectionIds); await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.CipherCreated, await _organizationRepository.GetByIdAsync(cipher.OrganizationId.Value))); + new ReferenceEvent(ReferenceEventType.CipherCreated, await _organizationRepository.GetByIdAsync(cipher.OrganizationId.Value), _currentContext)); } else { @@ -757,7 +757,7 @@ public class CipherService : ICipherService if (org != null) { await _referenceEventService.RaiseEventAsync( - new ReferenceEvent(ReferenceEventType.VaultImported, org)); + new ReferenceEvent(ReferenceEventType.VaultImported, org, _currentContext)); } }