Files
server/src/Core/Services/Implementations/NotificationHubPushRegistrationService.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

278 lines
10 KiB
C#
Raw Normal View History

using Bit.Core.Enums;
2019-03-19 00:39:03 -04:00
using Bit.Core.Models.Data;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Microsoft.Azure.NotificationHubs;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Bit.Core.Services;
2022-08-29 16:06:55 -04:00
public class NotificationHubPushRegistrationService : IPushRegistrationService
{
private readonly IInstallationDeviceRepository _installationDeviceRepository;
private readonly GlobalSettings _globalSettings;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<NotificationHubPushRegistrationService> _logger;
private Dictionary<NotificationHubType, NotificationHubClient> _clients = [];
2017-05-26 22:52:50 -04:00
public NotificationHubPushRegistrationService(
2019-03-19 00:39:03 -04:00
IInstallationDeviceRepository installationDeviceRepository,
GlobalSettings globalSettings,
IServiceProvider serviceProvider,
ILogger<NotificationHubPushRegistrationService> logger)
2022-08-29 16:06:55 -04:00
{
2017-05-26 22:52:50 -04:00
_installationDeviceRepository = installationDeviceRepository;
_globalSettings = globalSettings;
_serviceProvider = serviceProvider;
_logger = logger;
// Is this dirty to do in the ctor?
void addHub(NotificationHubType type)
{
var hubRegistration = globalSettings.NotificationHubs.FirstOrDefault(
h => h.HubType == type && h.EnableRegistration);
if (hubRegistration != null)
{
var client = NotificationHubClient.CreateClientFromConnectionString(
hubRegistration.ConnectionString,
hubRegistration.HubName,
hubRegistration.EnableSendTracing);
_clients.Add(type, client);
}
}
addHub(NotificationHubType.General);
addHub(NotificationHubType.iOS);
addHub(NotificationHubType.Android);
2022-08-29 16:06:55 -04:00
}
2017-05-26 22:52:50 -04:00
public async Task CreateOrUpdateRegistrationAsync(string pushToken, string deviceId, string userId,
2019-03-19 00:39:03 -04:00
string identifier, DeviceType type)
2022-08-29 16:06:55 -04:00
{
2019-03-19 00:39:03 -04:00
if (string.IsNullOrWhiteSpace(pushToken))
{
return;
}
var installation = new Installation
{
2017-08-11 08:57:31 -04:00
InstallationId = deviceId,
PushChannel = pushToken,
Templates = new Dictionary<string, InstallationTemplate>()
};
2017-08-11 08:57:31 -04:00
installation.Tags = new List<string>
{
2017-08-11 08:57:31 -04:00
$"userId:{userId}"
};
2017-05-26 22:52:50 -04:00
if (!string.IsNullOrWhiteSpace(identifier))
{
installation.Tags.Add("deviceIdentifier:" + identifier);
}
string payloadTemplate = null, messageTemplate = null, badgeMessageTemplate = null;
switch (type)
{
2017-08-11 08:57:31 -04:00
case DeviceType.Android:
var featureService = _serviceProvider.GetRequiredService<IFeatureService>();
if (featureService.IsEnabled(FeatureFlagKeys.AnhFcmv1Migration))
{
payloadTemplate = "{\"message\":{\"data\":{\"type\":\"$(type)\",\"payload\":\"$(payload)\"}}}";
messageTemplate = "{\"message\":{\"data\":{\"type\":\"$(type)\"}," +
"\"notification\":{\"title\":\"$(title)\",\"body\":\"$(message)\"}}}";
installation.Platform = NotificationPlatform.FcmV1;
}
else
{
payloadTemplate = "{\"data\":{\"data\":{\"type\":\"#(type)\",\"payload\":\"$(payload)\"}}}";
messageTemplate = "{\"data\":{\"data\":{\"type\":\"#(type)\"}," +
"\"notification\":{\"title\":\"$(title)\",\"body\":\"$(message)\"}}}";
installation.Platform = NotificationPlatform.Fcm;
}
break;
2017-08-11 08:57:31 -04:00
case DeviceType.iOS:
2017-05-26 22:52:50 -04:00
payloadTemplate = "{\"data\":{\"type\":\"#(type)\",\"payload\":\"$(payload)\"}," +
"\"aps\":{\"content-available\":1}}";
2017-05-26 22:52:50 -04:00
messageTemplate = "{\"data\":{\"type\":\"#(type)\"}," +
"\"aps\":{\"alert\":\"$(message)\",\"badge\":null,\"content-available\":1}}";
badgeMessageTemplate = "{\"data\":{\"type\":\"#(type)\"}," +
"\"aps\":{\"alert\":\"$(message)\",\"badge\":\"#(badge)\",\"content-available\":1}}";
2022-08-29 16:06:55 -04:00
installation.Platform = NotificationPlatform.Apns;
break;
case DeviceType.AndroidAmazon:
2017-05-26 22:52:50 -04:00
payloadTemplate = "{\"data\":{\"type\":\"#(type)\",\"payload\":\"$(payload)\"}}";
messageTemplate = "{\"data\":{\"type\":\"#(type)\",\"message\":\"$(message)\"}}";
2022-08-29 16:06:55 -04:00
installation.Platform = NotificationPlatform.Adm;
break;
default:
break;
}
2017-05-26 22:52:50 -04:00
BuildInstallationTemplate(installation, "payload", payloadTemplate, userId, identifier);
2019-03-19 00:39:03 -04:00
BuildInstallationTemplate(installation, "message", messageTemplate, userId, identifier);
BuildInstallationTemplate(installation, "badgeMessage", badgeMessageTemplate ?? messageTemplate,
userId, identifier);
await GetClient(type).CreateOrUpdateInstallationAsync(installation);
2017-08-11 08:57:31 -04:00
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
{
2017-08-11 08:57:31 -04:00
await _installationDeviceRepository.UpsertAsync(new InstallationDeviceEntity(deviceId));
2017-05-26 22:52:50 -04:00
}
2022-08-29 16:06:55 -04:00
}
2017-05-26 22:52:50 -04:00
private void BuildInstallationTemplate(Installation installation, string templateId, string templateBody,
2017-08-11 08:57:31 -04:00
string userId, string identifier)
2022-08-29 16:06:55 -04:00
{
2017-05-26 22:52:50 -04:00
if (templateBody == null)
{
return;
2017-05-26 22:52:50 -04:00
}
2019-03-19 00:39:03 -04:00
var fullTemplateId = $"template:{templateId}";
2022-08-29 14:53:16 -04:00
var template = new InstallationTemplate
{
2017-08-11 08:57:31 -04:00
Body = templateBody,
2019-03-19 00:39:03 -04:00
Tags = new List<string>
{
2019-03-19 00:39:03 -04:00
fullTemplateId,
$"{fullTemplateId}_userId:{userId}"
}
2022-08-29 16:06:55 -04:00
};
2017-08-11 08:57:31 -04:00
if (!string.IsNullOrWhiteSpace(identifier))
2022-08-29 16:06:55 -04:00
{
2017-08-11 08:57:31 -04:00
template.Tags.Add($"{fullTemplateId}_deviceIdentifier:{identifier}");
}
2017-05-26 22:52:50 -04:00
2019-03-19 00:39:03 -04:00
installation.Templates.Add(fullTemplateId, template);
2022-08-29 16:06:55 -04:00
}
public async Task DeleteRegistrationAsync(string deviceId, DeviceType deviceType)
2022-08-29 16:06:55 -04:00
{
try
2017-05-26 22:52:50 -04:00
{
await GetClient(deviceType).DeleteInstallationAsync(deviceId);
2019-03-19 00:39:03 -04:00
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
{
2019-03-19 00:39:03 -04:00
await _installationDeviceRepository.DeleteAsync(new InstallationDeviceEntity(deviceId));
}
2017-05-26 22:52:50 -04:00
}
2019-03-19 00:39:03 -04:00
catch (Exception e) when (e.InnerException == null || !e.InnerException.Message.Contains("(404) Not Found"))
2022-08-29 16:06:55 -04:00
{
throw;
}
}
2017-05-26 22:52:50 -04:00
public async Task AddUserRegistrationOrganizationAsync(IEnumerable<KeyValuePair<string, DeviceType>> devices, string organizationId)
2022-08-29 16:06:55 -04:00
{
await PatchTagsForUserDevicesAsync(devices, UpdateOperationType.Add, $"organizationId:{organizationId}");
if (devices.Any() && InstallationDeviceEntity.IsInstallationDeviceId(devices.First().Key))
2017-05-26 22:52:50 -04:00
{
var entities = devices.Select(e => new InstallationDeviceEntity(e.Key));
2019-03-19 00:39:03 -04:00
await _installationDeviceRepository.UpsertManyAsync(entities.ToList());
2017-05-26 22:52:50 -04:00
}
2022-08-29 16:06:55 -04:00
}
2017-05-26 22:52:50 -04:00
public async Task DeleteUserRegistrationOrganizationAsync(IEnumerable<KeyValuePair<string, DeviceType>> devices, string organizationId)
2022-08-29 16:06:55 -04:00
{
await PatchTagsForUserDevicesAsync(devices, UpdateOperationType.Remove,
2017-08-11 08:57:31 -04:00
$"organizationId:{organizationId}");
if (devices.Any() && InstallationDeviceEntity.IsInstallationDeviceId(devices.First().Key))
2017-05-26 22:52:50 -04:00
{
var entities = devices.Select(e => new InstallationDeviceEntity(e.Key));
2017-08-11 08:57:31 -04:00
await _installationDeviceRepository.UpsertManyAsync(entities.ToList());
}
2022-08-29 16:06:55 -04:00
}
2017-08-11 08:57:31 -04:00
private async Task PatchTagsForUserDevicesAsync(IEnumerable<KeyValuePair<string, DeviceType>> devices, UpdateOperationType op,
string tag)
2022-08-29 16:06:55 -04:00
{
if (!devices.Any())
2017-05-26 22:52:50 -04:00
{
return;
}
var operation = new PartialUpdateOperation
2017-11-14 09:35:48 -05:00
{
Operation = op,
2017-11-14 09:35:48 -05:00
Path = "/tags"
2022-08-29 14:53:16 -04:00
};
if (op == UpdateOperationType.Add)
2017-11-14 09:35:48 -05:00
{
operation.Value = tag;
}
else if (op == UpdateOperationType.Remove)
2022-08-29 16:06:55 -04:00
{
2017-11-14 09:35:48 -05:00
operation.Path += $"/{tag}";
2022-08-29 16:06:55 -04:00
}
foreach (var device in devices)
2022-08-29 16:06:55 -04:00
{
try
2017-05-26 22:52:50 -04:00
{
await GetClient(device.Value).PatchInstallationAsync(device.Key, new List<PartialUpdateOperation> { operation });
}
catch (Exception e) when (e.InnerException == null || !e.InnerException.Message.Contains("(404) Not Found"))
{
throw;
2017-05-26 22:52:50 -04:00
}
}
}
private NotificationHubClient GetClient(DeviceType deviceType)
{
var hubType = NotificationHubType.General;
switch (deviceType)
{
case DeviceType.Android:
hubType = NotificationHubType.Android;
break;
case DeviceType.iOS:
hubType = NotificationHubType.iOS;
break;
case DeviceType.ChromeExtension:
case DeviceType.FirefoxExtension:
case DeviceType.OperaExtension:
case DeviceType.EdgeExtension:
case DeviceType.VivaldiExtension:
case DeviceType.SafariExtension:
hubType = NotificationHubType.GeneralBrowserExtension;
break;
case DeviceType.WindowsDesktop:
case DeviceType.MacOsDesktop:
case DeviceType.LinuxDesktop:
hubType = NotificationHubType.GeneralDesktop;
break;
case DeviceType.ChromeBrowser:
case DeviceType.FirefoxBrowser:
case DeviceType.OperaBrowser:
case DeviceType.EdgeBrowser:
case DeviceType.IEBrowser:
case DeviceType.UnknownBrowser:
case DeviceType.SafariBrowser:
case DeviceType.VivaldiBrowser:
hubType = NotificationHubType.GeneralWeb;
break;
default:
break;
}
if (!_clients.ContainsKey(hubType))
{
_logger.LogWarning("No hub client for '{0}'. Using general hub instead.", hubType);
hubType = NotificationHubType.General;
if (!_clients.ContainsKey(hubType))
{
throw new Exception("No general hub client found.");
}
}
return _clients[hubType];
}
}