mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
[PM-15084] Push global notification creation to affected clients (#5079)
* PM-10600: Notification push notification * PM-10600: Sending to specific client types for relay push notifications * PM-10600: Sending to specific client types for other clients * PM-10600: Send push notification on notification creation * PM-10600: Explicit group names * PM-10600: Id typos * PM-10600: Revert global push notifications * PM-10600: Added DeviceType claim * PM-10600: Sent to organization typo * PM-10600: UT coverage * PM-10600: Small refactor, UTs coverage * PM-10600: UTs coverage * PM-10600: Startup fix * PM-10600: Test fix * PM-10600: Required attribute, organization group for push notification fix * PM-10600: UT coverage * PM-10600: Fix Mobile devices not registering to organization push notifications We only register devices for organization push notifications when the organization is being created. This does not work, since we have a use case (Notification Center) of delivering notifications to all users of organization. This fixes it, by adding the organization id tag when device registers for push notifications. * PM-10600: Unit Test coverage for NotificationHubPushRegistrationService Fixed IFeatureService substitute mocking for Android tests. Added user part of organization test with organizationId tags expectation. * PM-10600: Unit Tests fix to NotificationHubPushRegistrationService after merge conflict * PM-10600: Organization push notifications not sending to mobile device from self-hosted. Self-hosted instance uses relay to register the mobile device against Bitwarden Cloud Api. Only the self-hosted server knows client's organization membership, which means it needs to pass in the organization id's information to the relay. Similarly, for Bitwarden Cloud, the organizaton id will come directly from the server. * PM-10600: Fix self-hosted organization notification not being received by mobile device. When mobile device registers on self-hosted through the relay, every single id, like user id, device id and now organization id needs to be prefixed with the installation id. This have been missing in the PushController that handles this for organization id. * PM-10600: Broken NotificationsController integration test Device type is now part of JWT access token, so the notification center results in the integration test are now scoped to client type web and all. * PM-10600: Merge conflicts fix * merge conflict fix * PM-10600: Push notification with full notification center content. Notification Center push notification now includes all the fields. * PM-10564: Push notification updates to other clients Cherry-picked and squashed commits:d9711b6031
6e69c8a0ce
01c814595e
3885885d5f
1285a7e994
fcf346985f
28ff53c293
57804ae27c
1c9339b686
* PM-15084: Push global notification creation to affected clients Cherry-picked and squashed commits:ed5051e0eb
181f3e4ae6
49fe7c93fd
a8efb45a63
7b4122c837
d21d4a67b3
186a09bb92
1531f564b5
* PM-15084: Log warning when invalid notification push notification sent * explicit Guid default value * push notification tests in wrong namespace * Installation push notification not received for on global notification center message * wrong merge conflict * wrong merge conflict * installation id type Guid in push registration request
This commit is contained in:
@ -22,14 +22,14 @@ public class PushController : Controller
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly ICurrentContext _currentContext;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
|
||||
public PushController(
|
||||
IPushRegistrationService pushRegistrationService,
|
||||
IPushNotificationService pushNotificationService,
|
||||
IWebHostEnvironment environment,
|
||||
ICurrentContext currentContext,
|
||||
GlobalSettings globalSettings)
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
_currentContext = currentContext;
|
||||
_environment = environment;
|
||||
@ -39,22 +39,23 @@ public class PushController : Controller
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task PostRegister([FromBody] PushRegistrationRequestModel model)
|
||||
public async Task RegisterAsync([FromBody] PushRegistrationRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(model.PushToken, Prefix(model.DeviceId),
|
||||
Prefix(model.UserId), Prefix(model.Identifier), model.Type, model.OrganizationIds.Select(Prefix));
|
||||
Prefix(model.UserId), Prefix(model.Identifier), model.Type, model.OrganizationIds.Select(Prefix),
|
||||
model.InstallationId);
|
||||
}
|
||||
|
||||
[HttpPost("delete")]
|
||||
public async Task PostDelete([FromBody] PushDeviceRequestModel model)
|
||||
public async Task DeleteAsync([FromBody] PushDeviceRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.DeleteRegistrationAsync(Prefix(model.Id));
|
||||
}
|
||||
|
||||
[HttpPut("add-organization")]
|
||||
public async Task PutAddOrganization([FromBody] PushUpdateRequestModel model)
|
||||
public async Task AddOrganizationAsync([FromBody] PushUpdateRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.AddUserRegistrationOrganizationAsync(
|
||||
@ -63,7 +64,7 @@ public class PushController : Controller
|
||||
}
|
||||
|
||||
[HttpPut("delete-organization")]
|
||||
public async Task PutDeleteOrganization([FromBody] PushUpdateRequestModel model)
|
||||
public async Task DeleteOrganizationAsync([FromBody] PushUpdateRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
await _pushRegistrationService.DeleteUserRegistrationOrganizationAsync(
|
||||
@ -72,11 +73,22 @@ public class PushController : Controller
|
||||
}
|
||||
|
||||
[HttpPost("send")]
|
||||
public async Task PostSend([FromBody] PushSendRequestModel model)
|
||||
public async Task SendAsync([FromBody] PushSendRequestModel model)
|
||||
{
|
||||
CheckUsage();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(model.UserId))
|
||||
if (!string.IsNullOrWhiteSpace(model.InstallationId))
|
||||
{
|
||||
if (_currentContext.InstallationId!.Value.ToString() != model.InstallationId!)
|
||||
{
|
||||
throw new BadRequestException("InstallationId does not match current context.");
|
||||
}
|
||||
|
||||
await _pushNotificationService.SendPayloadToInstallationAsync(
|
||||
_currentContext.InstallationId.Value.ToString(), model.Type, model.Payload, Prefix(model.Identifier),
|
||||
Prefix(model.DeviceId), model.ClientType);
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(model.UserId))
|
||||
{
|
||||
await _pushNotificationService.SendPayloadToUserAsync(Prefix(model.UserId),
|
||||
model.Type, model.Payload, Prefix(model.Identifier), Prefix(model.DeviceId), model.ClientType);
|
||||
@ -95,7 +107,7 @@ public class PushController : Controller
|
||||
return null;
|
||||
}
|
||||
|
||||
return $"{_currentContext.InstallationId.Value}_{value}";
|
||||
return $"{_currentContext.InstallationId!.Value}_{value}";
|
||||
}
|
||||
|
||||
private void CheckUsage()
|
||||
|
@ -28,6 +28,6 @@ public enum PushType : byte
|
||||
SyncOrganizationStatusChanged = 18,
|
||||
SyncOrganizationCollectionSettingChanged = 19,
|
||||
|
||||
SyncNotification = 20,
|
||||
SyncNotificationStatus = 21
|
||||
Notification = 20,
|
||||
NotificationStatus = 21
|
||||
}
|
||||
|
@ -5,15 +5,11 @@ namespace Bit.Core.Models.Api;
|
||||
|
||||
public class PushRegistrationRequestModel
|
||||
{
|
||||
[Required]
|
||||
public string DeviceId { get; set; }
|
||||
[Required]
|
||||
public string PushToken { get; set; }
|
||||
[Required]
|
||||
public string UserId { get; set; }
|
||||
[Required]
|
||||
public DeviceType Type { get; set; }
|
||||
[Required]
|
||||
public string Identifier { get; set; }
|
||||
[Required] public string DeviceId { get; set; }
|
||||
[Required] public string PushToken { get; set; }
|
||||
[Required] public string UserId { get; set; }
|
||||
[Required] public DeviceType Type { get; set; }
|
||||
[Required] public string Identifier { get; set; }
|
||||
public IEnumerable<string> OrganizationIds { get; set; }
|
||||
public Guid InstallationId { get; set; }
|
||||
}
|
||||
|
@ -13,12 +13,16 @@ public class PushSendRequestModel : IValidatableObject
|
||||
public required PushType Type { get; set; }
|
||||
public required object Payload { get; set; }
|
||||
public ClientType? ClientType { get; set; }
|
||||
public string? InstallationId { get; set; }
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(UserId) && string.IsNullOrWhiteSpace(OrganizationId))
|
||||
if (string.IsNullOrWhiteSpace(UserId) &&
|
||||
string.IsNullOrWhiteSpace(OrganizationId) &&
|
||||
string.IsNullOrWhiteSpace(InstallationId))
|
||||
{
|
||||
yield return new ValidationResult($"{nameof(UserId)} or {nameof(OrganizationId)} is required.");
|
||||
yield return new ValidationResult(
|
||||
$"{nameof(UserId)} or {nameof(OrganizationId)} or {nameof(InstallationId)} is required.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ public class NotificationPushNotification
|
||||
public ClientType ClientType { get; set; }
|
||||
public Guid? UserId { get; set; }
|
||||
public Guid? OrganizationId { get; set; }
|
||||
public Guid? InstallationId { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Body { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
|
@ -10,6 +10,7 @@ using Bit.Core.Models.Data;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -18,6 +19,11 @@ using Notification = Bit.Core.NotificationCenter.Entities.Notification;
|
||||
|
||||
namespace Bit.Core.NotificationHub;
|
||||
|
||||
/// <summary>
|
||||
/// Sends mobile push notifications to the Azure Notification Hub.
|
||||
/// Used by Cloud-Hosted environments.
|
||||
/// Received by Firebase for Android or APNS for iOS.
|
||||
/// </summary>
|
||||
public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
{
|
||||
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
||||
@ -25,17 +31,25 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
private readonly bool _enableTracing = false;
|
||||
private readonly INotificationHubPool _notificationHubPool;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
|
||||
public NotificationHubPushNotificationService(
|
||||
IInstallationDeviceRepository installationDeviceRepository,
|
||||
INotificationHubPool notificationHubPool,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<NotificationsApiPushNotificationService> logger)
|
||||
ILogger<NotificationHubPushNotificationService> logger,
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
_installationDeviceRepository = installationDeviceRepository;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_notificationHubPool = notificationHubPool;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
|
||||
if (globalSettings.Installation.Id == Guid.Empty)
|
||||
{
|
||||
logger.LogWarning("Installation ID is not set. Push notifications for installations will not work.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
@ -185,6 +199,10 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
|
||||
public async Task PushNotificationAsync(Notification notification)
|
||||
{
|
||||
Guid? installationId = notification.Global && _globalSettings.Installation.Id != Guid.Empty
|
||||
? _globalSettings.Installation.Id
|
||||
: null;
|
||||
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
@ -193,26 +211,49 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = installationId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
};
|
||||
|
||||
if (notification.UserId.HasValue)
|
||||
if (notification.Global)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotification, message, true,
|
||||
if (installationId.HasValue)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(installationId.Value, PushType.Notification, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Invalid global notification id {NotificationId} push notification. No installation id provided.",
|
||||
notification.Id);
|
||||
}
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.Notification, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotification, message,
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.Notification, message,
|
||||
true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
{
|
||||
Guid? installationId = notification.Global && _globalSettings.Installation.Id != Guid.Empty
|
||||
? _globalSettings.Installation.Id
|
||||
: null;
|
||||
|
||||
var message = new NotificationPushNotification
|
||||
{
|
||||
Id = notification.Id,
|
||||
@ -221,6 +262,7 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = installationId,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
@ -229,15 +271,33 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
if (notification.UserId.HasValue)
|
||||
if (notification.Global)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationStatus, message, true,
|
||||
if (installationId.HasValue)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(installationId.Value, PushType.NotificationStatus, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Invalid global notification status id {NotificationId} push notification. No installation id provided.",
|
||||
notification.Id);
|
||||
}
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.NotificationStatus, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationStatus, message,
|
||||
true, notification.ClientType);
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.NotificationStatus,
|
||||
message, true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification status id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,6 +308,13 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
await SendPayloadToUserAsync(authRequest.UserId, type, message, true);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToInstallationAsync(Guid installationId, PushType type, object payload,
|
||||
bool excludeCurrentContext, ClientType? clientType = null)
|
||||
{
|
||||
await SendPayloadToInstallationAsync(installationId.ToString(), type, payload,
|
||||
GetContextIdentifier(excludeCurrentContext), clientType: clientType);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext,
|
||||
ClientType? clientType = null)
|
||||
{
|
||||
@ -262,6 +329,17 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
GetContextIdentifier(excludeCurrentContext), clientType: clientType);
|
||||
}
|
||||
|
||||
public async Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload,
|
||||
string? identifier, string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
var tag = BuildTag($"template:payload && installationId:{installationId}", identifier, clientType);
|
||||
await SendPayloadAsync(tag, type, payload);
|
||||
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
||||
{
|
||||
await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
}
|
||||
|
||||
public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds)
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds, Guid installationId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pushToken))
|
||||
{
|
||||
@ -50,6 +50,11 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
installation.Tags.Add($"organizationId:{organizationId}");
|
||||
}
|
||||
|
||||
if (installationId != Guid.Empty)
|
||||
{
|
||||
installation.Tags.Add($"installationId:{installationId}");
|
||||
}
|
||||
|
||||
string payloadTemplate = null, messageTemplate = null, badgeMessageTemplate = null;
|
||||
switch (type)
|
||||
{
|
||||
@ -80,11 +85,11 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
}
|
||||
|
||||
BuildInstallationTemplate(installation, "payload", payloadTemplate, userId, identifier, clientType,
|
||||
organizationIdsList);
|
||||
organizationIdsList, installationId);
|
||||
BuildInstallationTemplate(installation, "message", messageTemplate, userId, identifier, clientType,
|
||||
organizationIdsList);
|
||||
organizationIdsList, installationId);
|
||||
BuildInstallationTemplate(installation, "badgeMessage", badgeMessageTemplate ?? messageTemplate,
|
||||
userId, identifier, clientType, organizationIdsList);
|
||||
userId, identifier, clientType, organizationIdsList, installationId);
|
||||
|
||||
await ClientFor(GetComb(deviceId)).CreateOrUpdateInstallationAsync(installation);
|
||||
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
||||
@ -94,7 +99,7 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
}
|
||||
|
||||
private void BuildInstallationTemplate(Installation installation, string templateId, string templateBody,
|
||||
string userId, string identifier, ClientType clientType, List<string> organizationIds)
|
||||
string userId, string identifier, ClientType clientType, List<string> organizationIds, Guid installationId)
|
||||
{
|
||||
if (templateBody == null)
|
||||
{
|
||||
@ -122,6 +127,11 @@ public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||
template.Tags.Add($"organizationId:{organizationId}");
|
||||
}
|
||||
|
||||
if (installationId != Guid.Empty)
|
||||
{
|
||||
template.Tags.Add($"installationId:{installationId}");
|
||||
}
|
||||
|
||||
installation.Templates.Add(fullTemplateId, template);
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,13 @@ using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Platform.Push.Internal;
|
||||
|
||||
@ -19,13 +21,22 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
{
|
||||
private readonly QueueClient _queueClient;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
|
||||
public AzureQueuePushNotificationService(
|
||||
[FromKeyedServices("notifications")] QueueClient queueClient,
|
||||
IHttpContextAccessor httpContextAccessor)
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IGlobalSettings globalSettings,
|
||||
ILogger<AzureQueuePushNotificationService> logger)
|
||||
{
|
||||
_queueClient = queueClient;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_globalSettings = globalSettings;
|
||||
|
||||
if (globalSettings.Installation.Id == Guid.Empty)
|
||||
{
|
||||
logger.LogWarning("Installation ID is not set. Push notifications for installations will not work.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
@ -176,13 +187,14 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.SyncNotification, message, true);
|
||||
await SendMessageAsync(PushType.Notification, message, true);
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
@ -195,6 +207,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
@ -203,7 +216,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.SyncNotificationStatus, message, true);
|
||||
await SendMessageAsync(PushType.NotificationStatus, message, true);
|
||||
}
|
||||
|
||||
private async Task PushSendAsync(Send send, PushType type)
|
||||
@ -241,6 +254,11 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
return currentContext?.DeviceIdentifier;
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) =>
|
||||
// Noop
|
||||
Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
|
@ -31,6 +31,9 @@ public interface IPushNotificationService
|
||||
Task PushAuthRequestResponseAsync(AuthRequest authRequest);
|
||||
Task PushSyncOrganizationStatusAsync(Organization organization);
|
||||
Task PushSyncOrganizationCollectionManagementSettingsAsync(Organization organization);
|
||||
|
||||
Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null);
|
||||
Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null);
|
||||
Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string? identifier,
|
||||
|
@ -5,7 +5,7 @@ namespace Bit.Core.Platform.Push;
|
||||
public interface IPushRegistrationService
|
||||
{
|
||||
Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds);
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds, Guid installationId);
|
||||
Task DeleteRegistrationAsync(string deviceId);
|
||||
Task AddUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId);
|
||||
Task DeleteUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId);
|
||||
|
@ -157,6 +157,14 @@ public class MultiServicePushNotificationService : IPushNotificationService
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
PushToServices((s) =>
|
||||
s.SendPayloadToInstallationAsync(installationId, type, payload, identifier, deviceId, clientType));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
|
@ -108,14 +108,17 @@ public class NoopPushNotificationService : IPushNotificationService
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushNotificationAsync(Notification notification) => Task.CompletedTask;
|
||||
|
||||
public Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) => Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public Task PushNotificationAsync(Notification notification) => Task.CompletedTask;
|
||||
|
||||
public Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus) =>
|
||||
Task.CompletedTask;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public class NoopPushRegistrationService : IPushRegistrationService
|
||||
}
|
||||
|
||||
public Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds)
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds, Guid installationId)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
@ -15,8 +15,14 @@ using Microsoft.Extensions.Logging;
|
||||
// This service is not in the `Internal` namespace because it has direct external references.
|
||||
namespace Bit.Core.Platform.Push;
|
||||
|
||||
/// <summary>
|
||||
/// Sends non-mobile push notifications to the Azure Queue Api, later received by Notifications Api.
|
||||
/// Used by Cloud-Hosted environments.
|
||||
/// Received by AzureQueueHostedService message receiver in Notifications project.
|
||||
/// </summary>
|
||||
public class NotificationsApiPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
||||
{
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public NotificationsApiPushNotificationService(
|
||||
@ -33,6 +39,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
globalSettings.InternalIdentityKey,
|
||||
logger)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
@ -193,13 +200,14 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.SyncNotification, message, true);
|
||||
await SendMessageAsync(PushType.Notification, message, true);
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
@ -212,6 +220,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
@ -220,7 +229,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
await SendMessageAsync(PushType.SyncNotificationStatus, message, true);
|
||||
await SendMessageAsync(PushType.NotificationStatus, message, true);
|
||||
}
|
||||
|
||||
private async Task PushSendAsync(Send send, PushType type)
|
||||
@ -257,6 +266,11 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
return currentContext?.DeviceIdentifier;
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) =>
|
||||
// Noop
|
||||
Task.CompletedTask;
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
|
@ -17,9 +17,15 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.Core.Platform.Push.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// Sends mobile push notifications to the Bitwarden Cloud API, then relayed to Azure Notification Hub.
|
||||
/// Used by Self-Hosted environments.
|
||||
/// Received by PushController endpoint in Api project.
|
||||
/// </summary>
|
||||
public class RelayPushNotificationService : BaseIdentityClientService, IPushNotificationService
|
||||
{
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public RelayPushNotificationService(
|
||||
@ -38,6 +44,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
logger)
|
||||
{
|
||||
_deviceRepository = deviceRepository;
|
||||
_globalSettings = globalSettings;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
@ -202,22 +209,31 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
RevisionDate = notification.RevisionDate
|
||||
};
|
||||
|
||||
if (notification.UserId.HasValue)
|
||||
if (notification.Global)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotification, message, true,
|
||||
await SendPayloadToInstallationAsync(PushType.Notification, message, true, notification.ClientType);
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.Notification, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotification, message,
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.Notification, message,
|
||||
true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushNotificationStatusAsync(Notification notification, NotificationStatus notificationStatus)
|
||||
@ -230,6 +246,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
ClientType = notification.ClientType,
|
||||
UserId = notification.UserId,
|
||||
OrganizationId = notification.OrganizationId,
|
||||
InstallationId = notification.Global ? _globalSettings.Installation.Id : null,
|
||||
Title = notification.Title,
|
||||
Body = notification.Body,
|
||||
CreationDate = notification.CreationDate,
|
||||
@ -238,16 +255,24 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
DeletedDate = notificationStatus.DeletedDate
|
||||
};
|
||||
|
||||
if (notification.UserId.HasValue)
|
||||
if (notification.Global)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.SyncNotificationStatus, message, true,
|
||||
await SendPayloadToInstallationAsync(PushType.NotificationStatus, message, true, notification.ClientType);
|
||||
}
|
||||
else if (notification.UserId.HasValue)
|
||||
{
|
||||
await SendPayloadToUserAsync(notification.UserId.Value, PushType.NotificationStatus, message, true,
|
||||
notification.ClientType);
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.SyncNotificationStatus, message,
|
||||
await SendPayloadToOrganizationAsync(notification.OrganizationId.Value, PushType.NotificationStatus, message,
|
||||
true, notification.ClientType);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Invalid notification status id {NotificationId} push notification", notification.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task PushSyncOrganizationStatusAsync(Organization organization)
|
||||
@ -275,6 +300,21 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
false
|
||||
);
|
||||
|
||||
private async Task SendPayloadToInstallationAsync(PushType type, object payload, bool excludeCurrentContext,
|
||||
ClientType? clientType = null)
|
||||
{
|
||||
var request = new PushSendRequestModel
|
||||
{
|
||||
InstallationId = _globalSettings.Installation.Id.ToString(),
|
||||
Type = type,
|
||||
Payload = payload,
|
||||
ClientType = clientType
|
||||
};
|
||||
|
||||
await AddCurrentContextAsync(request, excludeCurrentContext);
|
||||
await SendAsync(HttpMethod.Post, "push/send", request);
|
||||
}
|
||||
|
||||
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext,
|
||||
ClientType? clientType = null)
|
||||
{
|
||||
@ -324,6 +364,10 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
}
|
||||
}
|
||||
|
||||
public Task SendPayloadToInstallationAsync(string installationId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string? identifier,
|
||||
string? deviceId = null, ClientType? clientType = null)
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegi
|
||||
}
|
||||
|
||||
public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds)
|
||||
string identifier, DeviceType type, IEnumerable<string> organizationIds, Guid installationId)
|
||||
{
|
||||
var requestModel = new PushRegistrationRequestModel
|
||||
{
|
||||
@ -34,7 +34,8 @@ public class RelayPushRegistrationService : BaseIdentityClientService, IPushRegi
|
||||
PushToken = pushToken,
|
||||
Type = type,
|
||||
UserId = userId,
|
||||
OrganizationIds = organizationIds
|
||||
OrganizationIds = organizationIds,
|
||||
InstallationId = installationId
|
||||
};
|
||||
await SendAsync(HttpMethod.Post, "push/register", requestModel);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
@ -13,15 +14,18 @@ public class DeviceService : IDeviceService
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IPushRegistrationService _pushRegistrationService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
|
||||
public DeviceService(
|
||||
IDeviceRepository deviceRepository,
|
||||
IPushRegistrationService pushRegistrationService,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IGlobalSettings globalSettings)
|
||||
{
|
||||
_deviceRepository = deviceRepository;
|
||||
_pushRegistrationService = pushRegistrationService;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
_globalSettings = globalSettings;
|
||||
}
|
||||
|
||||
public async Task SaveAsync(Device device)
|
||||
@ -42,7 +46,8 @@ public class DeviceService : IDeviceService
|
||||
.Select(ou => ou.OrganizationId.ToString());
|
||||
|
||||
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(device.PushToken, device.Id.ToString(),
|
||||
device.UserId.ToString(), device.Identifier, device.Type, organizationIdsString);
|
||||
device.UserId.ToString(), device.Identifier, device.Type, organizationIdsString,
|
||||
_globalSettings.Installation.Id);
|
||||
}
|
||||
|
||||
public async Task ClearTokenAsync(Device device)
|
||||
|
@ -93,40 +93,45 @@ public static class HubHelpers
|
||||
var orgStatusNotification =
|
||||
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
|
||||
notificationJson, _deserializerOptions);
|
||||
await hubContext.Clients.Group($"Organization_{orgStatusNotification.Payload.OrganizationId}")
|
||||
.SendAsync("ReceiveMessage", orgStatusNotification, cancellationToken);
|
||||
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(orgStatusNotification.Payload.OrganizationId))
|
||||
.SendAsync(_receiveMessageMethod, orgStatusNotification, cancellationToken);
|
||||
break;
|
||||
case PushType.SyncOrganizationCollectionSettingChanged:
|
||||
var organizationCollectionSettingsChangedNotification =
|
||||
JsonSerializer.Deserialize<PushNotificationData<OrganizationStatusPushNotification>>(
|
||||
notificationJson, _deserializerOptions);
|
||||
await hubContext.Clients.Group($"Organization_{organizationCollectionSettingsChangedNotification.Payload.OrganizationId}")
|
||||
.SendAsync("ReceiveMessage", organizationCollectionSettingsChangedNotification, cancellationToken);
|
||||
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(organizationCollectionSettingsChangedNotification.Payload.OrganizationId))
|
||||
.SendAsync(_receiveMessageMethod, organizationCollectionSettingsChangedNotification, cancellationToken);
|
||||
break;
|
||||
case PushType.SyncNotification:
|
||||
case PushType.SyncNotificationStatus:
|
||||
var syncNotification =
|
||||
JsonSerializer.Deserialize<PushNotificationData<NotificationPushNotification>>(
|
||||
notificationJson, _deserializerOptions);
|
||||
if (syncNotification.Payload.UserId.HasValue)
|
||||
case PushType.Notification:
|
||||
case PushType.NotificationStatus:
|
||||
var notificationData = JsonSerializer.Deserialize<PushNotificationData<NotificationPushNotification>>(
|
||||
notificationJson, _deserializerOptions);
|
||||
if (notificationData.Payload.InstallationId.HasValue)
|
||||
{
|
||||
if (syncNotification.Payload.ClientType == ClientType.All)
|
||||
await hubContext.Clients.Group(NotificationsHub.GetInstallationGroup(
|
||||
notificationData.Payload.InstallationId.Value, notificationData.Payload.ClientType))
|
||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||
}
|
||||
else if (notificationData.Payload.UserId.HasValue)
|
||||
{
|
||||
if (notificationData.Payload.ClientType == ClientType.All)
|
||||
{
|
||||
await hubContext.Clients.User(syncNotification.Payload.UserId.ToString())
|
||||
.SendAsync(_receiveMessageMethod, syncNotification, cancellationToken);
|
||||
await hubContext.Clients.User(notificationData.Payload.UserId.ToString())
|
||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
await hubContext.Clients.Group(NotificationsHub.GetUserGroup(
|
||||
syncNotification.Payload.UserId.Value, syncNotification.Payload.ClientType))
|
||||
.SendAsync(_receiveMessageMethod, syncNotification, cancellationToken);
|
||||
notificationData.Payload.UserId.Value, notificationData.Payload.ClientType))
|
||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||
}
|
||||
}
|
||||
else if (syncNotification.Payload.OrganizationId.HasValue)
|
||||
else if (notificationData.Payload.OrganizationId.HasValue)
|
||||
{
|
||||
await hubContext.Clients.Group(NotificationsHub.GetOrganizationGroup(
|
||||
syncNotification.Payload.OrganizationId.Value, syncNotification.Payload.ClientType))
|
||||
.SendAsync(_receiveMessageMethod, syncNotification, cancellationToken);
|
||||
notificationData.Payload.OrganizationId.Value, notificationData.Payload.ClientType))
|
||||
.SendAsync(_receiveMessageMethod, notificationData, cancellationToken);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -29,6 +29,16 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, GetUserGroup(currentContext.UserId.Value, clientType));
|
||||
}
|
||||
|
||||
if (_globalSettings.Installation.Id != Guid.Empty)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, GetInstallationGroup(_globalSettings.Installation.Id));
|
||||
if (clientType != ClientType.All)
|
||||
{
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId,
|
||||
GetInstallationGroup(_globalSettings.Installation.Id, clientType));
|
||||
}
|
||||
}
|
||||
|
||||
if (currentContext.Organizations != null)
|
||||
{
|
||||
foreach (var org in currentContext.Organizations)
|
||||
@ -57,6 +67,17 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
GetUserGroup(currentContext.UserId.Value, clientType));
|
||||
}
|
||||
|
||||
if (_globalSettings.Installation.Id != Guid.Empty)
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId,
|
||||
GetInstallationGroup(_globalSettings.Installation.Id));
|
||||
if (clientType != ClientType.All)
|
||||
{
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId,
|
||||
GetInstallationGroup(_globalSettings.Installation.Id, clientType));
|
||||
}
|
||||
}
|
||||
|
||||
if (currentContext.Organizations != null)
|
||||
{
|
||||
foreach (var org in currentContext.Organizations)
|
||||
@ -73,6 +94,13 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
public static string GetInstallationGroup(Guid installationId, ClientType? clientType = null)
|
||||
{
|
||||
return clientType is null or ClientType.All
|
||||
? $"Installation_{installationId}"
|
||||
: $"Installation_ClientType_{installationId}_{clientType}";
|
||||
}
|
||||
|
||||
public static string GetUserGroup(Guid userId, ClientType clientType)
|
||||
{
|
||||
return $"UserClientType_{userId}_{clientType}";
|
||||
|
@ -282,9 +282,13 @@ public static class ServiceCollectionExtensions
|
||||
services.AddSingleton<IPushNotificationService, MultiServicePushNotificationService>();
|
||||
if (globalSettings.SelfHosted)
|
||||
{
|
||||
if (globalSettings.Installation.Id == Guid.Empty)
|
||||
{
|
||||
throw new InvalidOperationException("Installation Id must be set for self-hosted installations.");
|
||||
}
|
||||
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.PushRelayBaseUri) &&
|
||||
globalSettings.Installation?.Id != null &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.Installation?.Key))
|
||||
CoreHelpers.SettingHasValue(globalSettings.Installation.Key))
|
||||
{
|
||||
services.AddKeyedSingleton<IPushNotificationService, RelayPushNotificationService>("implementation");
|
||||
services.AddSingleton<IPushRegistrationService, RelayPushRegistrationService>();
|
||||
@ -300,7 +304,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddKeyedSingleton<IPushNotificationService, NotificationsApiPushNotificationService>("implementation");
|
||||
}
|
||||
}
|
||||
else if (!globalSettings.SelfHosted)
|
||||
else
|
||||
{
|
||||
services.AddSingleton<INotificationHubPool, NotificationHubPool>();
|
||||
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
|
||||
|
Reference in New Issue
Block a user