From 01a293cf765119a5341caa782124b36a9f788a91 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 19 Mar 2019 00:39:03 -0400 Subject: [PATCH] record installation devices --- src/Api/Controllers/PushController.cs | 4 +- .../Api/Request/PushSendRequestModel.cs | 1 + .../Models/Data/InstallationDeviceEntity.cs | 36 +++++++++ .../IInstallationDeviceRepository.cs | 13 +++ .../Noop/InstallationDeviceRepository.cs | 24 ++++++ .../InstallationDeviceRepository.cs | 79 +++++++++++++++++++ src/Core/Services/IPushNotificationService.cs | 5 +- .../AzureQueuePushNotificationService.cs | 8 +- .../MultiServicePushNotificationService.cs | 24 ++++-- .../NotificationHubPushNotificationService.cs | 19 ++++- .../NotificationHubPushRegistrationService.cs | 25 +++++- ...NotificationsApiPushNotificationService.cs | 6 +- .../RelayPushNotificationService.cs | 38 +++++---- .../NoopPushNotificationService.cs | 6 +- .../Utilities/ServiceCollectionExtensions.cs | 3 + 15 files changed, 255 insertions(+), 36 deletions(-) create mode 100644 src/Core/Models/Data/InstallationDeviceEntity.cs create mode 100644 src/Core/Repositories/IInstallationDeviceRepository.cs create mode 100644 src/Core/Repositories/Noop/InstallationDeviceRepository.cs create mode 100644 src/Core/Repositories/TableStorage/InstallationDeviceRepository.cs diff --git a/src/Api/Controllers/PushController.cs b/src/Api/Controllers/PushController.cs index 7e28593d5c..c337073754 100644 --- a/src/Api/Controllers/PushController.cs +++ b/src/Api/Controllers/PushController.cs @@ -76,12 +76,12 @@ namespace Bit.Api.Controllers if(!string.IsNullOrWhiteSpace(model.UserId)) { await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId), - model.Type.Value, model.Payload, Prefix(model.Identifier)); + model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId)); } else if(!string.IsNullOrWhiteSpace(model.OrganizationId)) { await _pushNotificationService.SendPayloadToOrganizationAsync(Prefix(model.OrganizationId), - model.Type.Value, model.Payload, Prefix(model.Identifier)); + model.Type.Value, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId)); } } diff --git a/src/Core/Models/Api/Request/PushSendRequestModel.cs b/src/Core/Models/Api/Request/PushSendRequestModel.cs index e58fb8fa10..36ef816674 100644 --- a/src/Core/Models/Api/Request/PushSendRequestModel.cs +++ b/src/Core/Models/Api/Request/PushSendRequestModel.cs @@ -8,6 +8,7 @@ namespace Bit.Core.Models.Api { public string UserId { get; set; } public string OrganizationId { get; set; } + public string DeviceId { get; set; } public string Identifier { get; set; } [Required] public PushType? Type { get; set; } diff --git a/src/Core/Models/Data/InstallationDeviceEntity.cs b/src/Core/Models/Data/InstallationDeviceEntity.cs new file mode 100644 index 0000000000..d955409512 --- /dev/null +++ b/src/Core/Models/Data/InstallationDeviceEntity.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.WindowsAzure.Storage.Table; + +namespace Bit.Core.Models.Data +{ + public class InstallationDeviceEntity : TableEntity + { + public InstallationDeviceEntity() { } + + public InstallationDeviceEntity(Guid installationId, Guid deviceId) + { + PartitionKey = installationId.ToString(); + RowKey = deviceId.ToString(); + } + + public InstallationDeviceEntity(string prefixedDeviceId) + { + var parts = prefixedDeviceId.Split("_"); + if(parts.Length < 2) + { + throw new ArgumentException("Not enough parts."); + } + if(!Guid.TryParse(parts[0], out var installationId) || !Guid.TryParse(parts[1], out var deviceId)) + { + throw new ArgumentException("Could not parse parts."); + } + PartitionKey = parts[0]; + RowKey = parts[1]; + } + + public static bool IsInstallationDeviceId(string deviceId) + { + return deviceId != null && deviceId.Length == 73 && deviceId[36] == '_'; + } + } +} diff --git a/src/Core/Repositories/IInstallationDeviceRepository.cs b/src/Core/Repositories/IInstallationDeviceRepository.cs new file mode 100644 index 0000000000..de94982eac --- /dev/null +++ b/src/Core/Repositories/IInstallationDeviceRepository.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.Core.Models.Data; + +namespace Bit.Core.Repositories +{ + public interface IInstallationDeviceRepository + { + Task UpsertAsync(InstallationDeviceEntity entity); + Task UpsertManyAsync(IList entities); + Task DeleteAsync(InstallationDeviceEntity entity); + } +} diff --git a/src/Core/Repositories/Noop/InstallationDeviceRepository.cs b/src/Core/Repositories/Noop/InstallationDeviceRepository.cs new file mode 100644 index 0000000000..1618fcdd34 --- /dev/null +++ b/src/Core/Repositories/Noop/InstallationDeviceRepository.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Bit.Core.Models.Data; + +namespace Bit.Core.Repositories.Noop +{ + public class InstallationDeviceRepository : IInstallationDeviceRepository + { + public Task UpsertAsync(InstallationDeviceEntity entity) + { + return Task.FromResult(0); + } + + public Task UpsertManyAsync(IList entities) + { + return Task.FromResult(0); + } + + public Task DeleteAsync(InstallationDeviceEntity entity) + { + return Task.FromResult(0); + } + } +} diff --git a/src/Core/Repositories/TableStorage/InstallationDeviceRepository.cs b/src/Core/Repositories/TableStorage/InstallationDeviceRepository.cs new file mode 100644 index 0000000000..a260fc3be5 --- /dev/null +++ b/src/Core/Repositories/TableStorage/InstallationDeviceRepository.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Bit.Core.Models.Data; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Table; + +namespace Bit.Core.Repositories.TableStorage +{ + public class InstallationDeviceRepository : IInstallationDeviceRepository + { + private readonly CloudTable _table; + + public InstallationDeviceRepository(GlobalSettings globalSettings) + : this(globalSettings.Events.ConnectionString) + { } + + public InstallationDeviceRepository(string storageConnectionString) + { + var storageAccount = CloudStorageAccount.Parse(storageConnectionString); + var tableClient = storageAccount.CreateCloudTableClient(); + _table = tableClient.GetTableReference("installationdevice"); + } + + public async Task UpsertAsync(InstallationDeviceEntity entity) + { + await _table.ExecuteAsync(TableOperation.InsertOrReplace(entity)); + } + + public async Task UpsertManyAsync(IList entities) + { + if(!entities?.Any() ?? true) + { + return; + } + + if(entities.Count == 1) + { + await UpsertAsync(entities.First()); + return; + } + + var entityGroups = entities.GroupBy(ent => ent.PartitionKey); + foreach(var group in entityGroups) + { + var groupEntities = group.ToList(); + if(groupEntities.Count == 1) + { + await UpsertAsync(groupEntities.First()); + continue; + } + + // A batch insert can only contain 100 entities at a time + var iterations = groupEntities.Count / 100; + for(var i = 0; i <= iterations; i++) + { + var batch = new TableBatchOperation(); + var batchEntities = groupEntities.Skip(i * 100).Take(100); + if(!batchEntities.Any()) + { + break; + } + + foreach(var entity in batchEntities) + { + batch.InsertOrReplace(entity); + } + + await _table.ExecuteBatchAsync(batch); + } + } + } + + public async Task DeleteAsync(InstallationDeviceEntity entity) + { + await _table.ExecuteAsync(TableOperation.Delete(entity)); + } + } +} diff --git a/src/Core/Services/IPushNotificationService.cs b/src/Core/Services/IPushNotificationService.cs index ffd9164437..6f55620bc2 100644 --- a/src/Core/Services/IPushNotificationService.cs +++ b/src/Core/Services/IPushNotificationService.cs @@ -19,7 +19,8 @@ namespace Bit.Core.Services Task PushSyncOrgKeysAsync(Guid userId); Task PushSyncSettingsAsync(Guid userId); Task PushLogOutAsync(Guid userId); - Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier); - Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier); + Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, string deviceId = null); + Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, + string deviceId = null); } } diff --git a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs index 9d1c792298..6022d45bae 100644 --- a/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs +++ b/src/Core/Services/Implementations/AzureQueuePushNotificationService.cs @@ -141,7 +141,7 @@ namespace Bit.Core.Services private async Task SendMessageAsync(PushType type, T payload, bool excludeCurrentContext) { var contextId = GetContextIdentifier(excludeCurrentContext); - var message = JsonConvert.SerializeObject(new PushNotificationData(type, payload, contextId), + var message = JsonConvert.SerializeObject(new PushNotificationData(type, payload, contextId), _jsonSettings); var queueMessage = new CloudQueueMessage(message); await _queue.AddMessageAsync(queueMessage); @@ -159,13 +159,15 @@ namespace Bit.Core.Services return currentContext?.DeviceIdentifier; } - public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier) + public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, + string deviceId = null) { // Noop return Task.FromResult(0); } - public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier) + public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, + string deviceId = null) { // Noop return Task.FromResult(0); diff --git a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs index 88ace67f54..dc14d38d2d 100644 --- a/src/Core/Services/Implementations/MultiServicePushNotificationService.cs +++ b/src/Core/Services/Implementations/MultiServicePushNotificationService.cs @@ -6,14 +6,19 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Http; using Bit.Core.Utilities; using Microsoft.Extensions.Logging; +using Bit.Core.Repositories; namespace Bit.Core.Services { public class MultiServicePushNotificationService : IPushNotificationService { private readonly List _services = new List(); + private readonly IDeviceRepository _deviceRepository; + private readonly IInstallationDeviceRepository _installationDeviceRepository; public MultiServicePushNotificationService( + IDeviceRepository deviceRepository, + IInstallationDeviceRepository installationDeviceRepository, GlobalSettings globalSettings, IHttpContextAccessor httpContextAccessor, ILogger relayLogger, @@ -25,7 +30,8 @@ namespace Bit.Core.Services globalSettings.Installation?.Id != null && CoreHelpers.SettingHasValue(globalSettings.Installation?.Key)) { - _services.Add(new RelayPushNotificationService(globalSettings, httpContextAccessor, relayLogger)); + _services.Add(new RelayPushNotificationService(_deviceRepository, globalSettings, + httpContextAccessor, relayLogger)); } if(CoreHelpers.SettingHasValue(globalSettings.InternalIdentityKey) && CoreHelpers.SettingHasValue(globalSettings.BaseServiceUri.InternalNotifications)) @@ -38,13 +44,17 @@ namespace Bit.Core.Services { if(CoreHelpers.SettingHasValue(globalSettings.NotificationHub.ConnectionString)) { - _services.Add(new NotificationHubPushNotificationService(globalSettings, httpContextAccessor)); + _services.Add(new NotificationHubPushNotificationService(_installationDeviceRepository, + globalSettings, httpContextAccessor)); } if(CoreHelpers.SettingHasValue(globalSettings.Notifications?.ConnectionString)) { _services.Add(new AzureQueuePushNotificationService(globalSettings, httpContextAccessor)); } } + + _deviceRepository = deviceRepository; + _installationDeviceRepository = installationDeviceRepository; } public Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable collectionIds) @@ -113,15 +123,17 @@ namespace Bit.Core.Services return Task.FromResult(0); } - public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier) + public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, + string deviceId = null) { - PushToServices((s) => s.SendPayloadToUserAsync(userId, type, payload, identifier)); + PushToServices((s) => s.SendPayloadToUserAsync(userId, type, payload, identifier, deviceId)); return Task.FromResult(0); } - public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier) + public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, + string deviceId = null) { - PushToServices((s) => s.SendPayloadToOrganizationAsync(orgId, type, payload, identifier)); + PushToServices((s) => s.SendPayloadToOrganizationAsync(orgId, type, payload, identifier, deviceId)); return Task.FromResult(0); } diff --git a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs index c4e3ffe5f2..d43705de0f 100644 --- a/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationHubPushNotificationService.cs @@ -7,20 +7,25 @@ using Newtonsoft.Json; using System.Collections.Generic; using Microsoft.AspNetCore.Http; using Bit.Core.Models; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; namespace Bit.Core.Services { public class NotificationHubPushNotificationService : IPushNotificationService { + private readonly IInstallationDeviceRepository _installationDeviceRepository; private readonly GlobalSettings _globalSettings; private readonly IHttpContextAccessor _httpContextAccessor; private NotificationHubClient _client = null; public NotificationHubPushNotificationService( + IInstallationDeviceRepository installationDeviceRepository, GlobalSettings globalSettings, IHttpContextAccessor httpContextAccessor) { + _installationDeviceRepository = installationDeviceRepository; _globalSettings = globalSettings; _httpContextAccessor = httpContextAccessor; _client = NotificationHubClient.CreateClientFromConnectionString( @@ -141,16 +146,26 @@ namespace Bit.Core.Services await SendPayloadToUserAsync(orgId.ToString(), type, payload, GetContextIdentifier(excludeCurrentContext)); } - public async Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier) + public async Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, + string deviceId = null) { var tag = BuildTag($"template:payload_userId:{userId}", identifier); await SendPayloadAsync(tag, type, payload); + if(InstallationDeviceEntity.IsInstallationDeviceId(deviceId)) + { + await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId)); + } } - public async Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier) + public async Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, + string deviceId = null) { var tag = BuildTag($"template:payload && organizationId:{orgId}", identifier); await SendPayloadAsync(tag, type, payload); + if(InstallationDeviceEntity.IsInstallationDeviceId(deviceId)) + { + await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId)); + } } private string GetContextIdentifier(bool excludeCurrentContext) diff --git a/src/Core/Services/Implementations/NotificationHubPushRegistrationService.cs b/src/Core/Services/Implementations/NotificationHubPushRegistrationService.cs index 65a5405a38..f1c2b262f2 100644 --- a/src/Core/Services/Implementations/NotificationHubPushRegistrationService.cs +++ b/src/Core/Services/Implementations/NotificationHubPushRegistrationService.cs @@ -4,18 +4,23 @@ using Microsoft.Azure.NotificationHubs; using Bit.Core.Enums; using System.Linq; using System; +using Bit.Core.Models.Data; +using Bit.Core.Repositories; namespace Bit.Core.Services { public class NotificationHubPushRegistrationService : IPushRegistrationService { + private readonly IInstallationDeviceRepository _installationDeviceRepository; private readonly GlobalSettings _globalSettings; - + private NotificationHubClient _client = null; public NotificationHubPushRegistrationService( + IInstallationDeviceRepository installationDeviceRepository, GlobalSettings globalSettings) { + _installationDeviceRepository = installationDeviceRepository; _globalSettings = globalSettings; _client = NotificationHubClient.CreateClientFromConnectionString( _globalSettings.NotificationHub.ConnectionString, @@ -83,6 +88,10 @@ namespace Bit.Core.Services userId, identifier); await _client.CreateOrUpdateInstallationAsync(installation); + if(InstallationDeviceEntity.IsInstallationDeviceId(deviceId)) + { + await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId)); + } } private void BuildInstallationTemplate(Installation installation, string templateId, string templateBody, @@ -118,6 +127,10 @@ namespace Bit.Core.Services try { await _client.DeleteInstallationAsync(deviceId); + if(InstallationDeviceEntity.IsInstallationDeviceId(deviceId)) + { + await _installationDeviceRepository.DeleteAsync(new InstallationDeviceEntity(deviceId)); + } } catch(Exception e) { @@ -131,12 +144,22 @@ namespace Bit.Core.Services public async Task AddUserRegistrationOrganizationAsync(IEnumerable deviceIds, string organizationId) { await PatchTagsForUserDevicesAsync(deviceIds, UpdateOperationType.Add, $"organizationId:{organizationId}"); + if(deviceIds.Any() && InstallationDeviceEntity.IsInstallationDeviceId(deviceIds.First())) + { + var entities = deviceIds.Select(e => new InstallationDeviceEntity(e)); + await _installationDeviceRepository.UpsertManyAsync(entities.ToList()); + } } public async Task DeleteUserRegistrationOrganizationAsync(IEnumerable deviceIds, string organizationId) { await PatchTagsForUserDevicesAsync(deviceIds, UpdateOperationType.Remove, $"organizationId:{organizationId}"); + if(deviceIds.Any() && InstallationDeviceEntity.IsInstallationDeviceId(deviceIds.First())) + { + var entities = deviceIds.Select(e => new InstallationDeviceEntity(e)); + await _installationDeviceRepository.UpsertManyAsync(entities.ToList()); + } } private async Task PatchTagsForUserDevicesAsync(IEnumerable deviceIds, UpdateOperationType op, diff --git a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs index 8444c0a168..05cb139183 100644 --- a/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs +++ b/src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs @@ -161,13 +161,15 @@ namespace Bit.Core.Services return currentContext?.DeviceIdentifier; } - public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier) + public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, + string deviceId = null) { // Noop return Task.FromResult(0); } - public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier) + public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, + string deviceId = null) { // Noop return Task.FromResult(0); diff --git a/src/Core/Services/Implementations/RelayPushNotificationService.cs b/src/Core/Services/Implementations/RelayPushNotificationService.cs index 831aae1a04..dc309c06f8 100644 --- a/src/Core/Services/Implementations/RelayPushNotificationService.cs +++ b/src/Core/Services/Implementations/RelayPushNotificationService.cs @@ -8,15 +8,18 @@ using System.Net.Http; using Bit.Core.Models.Api; using Microsoft.Extensions.Logging; using System.Collections.Generic; +using Bit.Core.Repositories; namespace Bit.Core.Services { public class RelayPushNotificationService : BaseIdentityClientService, IPushNotificationService { + private readonly IDeviceRepository _deviceRepository; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; public RelayPushNotificationService( + IDeviceRepository deviceRepository, GlobalSettings globalSettings, IHttpContextAccessor httpContextAccessor, ILogger logger) @@ -28,6 +31,7 @@ namespace Bit.Core.Services globalSettings.Installation.Key, logger) { + _deviceRepository = deviceRepository; _httpContextAccessor = httpContextAccessor; _logger = logger; } @@ -143,12 +147,8 @@ namespace Bit.Core.Services Type = type, Payload = payload }; - - if(excludeCurrentContext) - { - ExcludeCurrentContext(request); - } - + + await AddCurrentContextAsync(request, excludeCurrentContext); await SendAsync(HttpMethod.Post, "push/send", request); } @@ -161,30 +161,36 @@ namespace Bit.Core.Services Payload = payload }; - if(excludeCurrentContext) - { - ExcludeCurrentContext(request); - } - + await AddCurrentContextAsync(request, excludeCurrentContext); await SendAsync(HttpMethod.Post, "push/send", request); } - private void ExcludeCurrentContext(PushSendRequestModel request) + private async Task AddCurrentContextAsync(PushSendRequestModel request, bool addIdentifier) { var currentContext = _httpContextAccessor?.HttpContext?. - RequestServices.GetService(typeof(CurrentContext)) as CurrentContext; + RequestServices.GetService(typeof(CurrentContext)) as CurrentContext; if(!string.IsNullOrWhiteSpace(currentContext?.DeviceIdentifier)) { - request.Identifier = currentContext.DeviceIdentifier; + var device = await _deviceRepository.GetByIdentifierAsync(currentContext.DeviceIdentifier); + if(device != null) + { + request.DeviceId = device.Id.ToString(); + } + if(addIdentifier) + { + request.Identifier = currentContext.DeviceIdentifier; + } } } - public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier) + public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, + string deviceId = null) { throw new NotImplementedException(); } - public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier) + public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, + string deviceId = null) { throw new NotImplementedException(); } diff --git a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs index a2ef04dc04..7a85d3ac08 100644 --- a/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs +++ b/src/Core/Services/NoopImplementations/NoopPushNotificationService.cs @@ -63,12 +63,14 @@ namespace Bit.Core.Services return Task.FromResult(0); } - public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier) + public Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier, + string deviceId = null) { return Task.FromResult(0); } - public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier) + public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, + string deviceId = null) { return Task.FromResult(0); } diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index 11effb7982..b697a4c3b0 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -20,6 +20,7 @@ using System; using System.IO; using SqlServerRepos = Bit.Core.Repositories.SqlServer; using PostgreSqlRepos = Bit.Core.Repositories.PostgreSql; +using NoopRepos = Bit.Core.Repositories.Noop; using System.Threading.Tasks; using TableStorageRepos = Bit.Core.Repositories.TableStorage; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -58,10 +59,12 @@ namespace Bit.Core.Utilities if(globalSettings.SelfHosted) { services.AddSingleton(); + services.AddSingleton(); } else { services.AddSingleton(); + services.AddSingleton(); } }