2022-06-29 19:46:41 -04:00
|
|
|
|
using Bit.Core.Enums;
|
2019-03-19 00:39:03 -04:00
|
|
|
|
using Bit.Core.Models.Data;
|
|
|
|
|
|
using Bit.Core.Repositories;
|
2024-10-22 09:20:57 -07:00
|
|
|
|
using Bit.Core.Services;
|
2021-02-22 15:35:16 -06:00
|
|
|
|
using Bit.Core.Settings;
|
2017-05-26 00:50:27 -04:00
|
|
|
|
using Microsoft.Azure.NotificationHubs;
|
2024-05-02 16:37:06 -04:00
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2024-04-08 15:39:44 -04:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2017-05-26 00:50:27 -04:00
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
namespace Bit.Core.NotificationHub;
|
2022-08-29 16:06:55 -04:00
|
|
|
|
|
2017-05-26 00:50:27 -04:00
|
|
|
|
public class NotificationHubPushRegistrationService : IPushRegistrationService
|
|
|
|
|
|
{
|
2018-08-06 09:04:31 -04:00
|
|
|
|
private readonly IInstallationDeviceRepository _installationDeviceRepository;
|
|
|
|
|
|
private readonly GlobalSettings _globalSettings;
|
2024-10-22 09:20:57 -07:00
|
|
|
|
private readonly INotificationHubPool _notificationHubPool;
|
2024-05-02 16:37:06 -04:00
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
2024-04-08 15:39:44 -04:00
|
|
|
|
private readonly ILogger<NotificationHubPushRegistrationService> _logger;
|
2017-05-26 00:50:27 -04:00
|
|
|
|
|
2017-05-26 22:52:50 -04:00
|
|
|
|
public NotificationHubPushRegistrationService(
|
2019-03-19 00:39:03 -04:00
|
|
|
|
IInstallationDeviceRepository installationDeviceRepository,
|
2024-04-08 15:39:44 -04:00
|
|
|
|
GlobalSettings globalSettings,
|
2024-10-22 09:20:57 -07:00
|
|
|
|
INotificationHubPool notificationHubPool,
|
2024-05-02 16:37:06 -04:00
|
|
|
|
IServiceProvider serviceProvider,
|
2024-04-08 15:39:44 -04:00
|
|
|
|
ILogger<NotificationHubPushRegistrationService> logger)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2017-05-26 22:52:50 -04:00
|
|
|
|
_installationDeviceRepository = installationDeviceRepository;
|
2018-08-06 09:04:31 -04:00
|
|
|
|
_globalSettings = globalSettings;
|
2024-10-22 09:20:57 -07:00
|
|
|
|
_notificationHubPool = notificationHubPool;
|
2024-05-02 16:37:06 -04:00
|
|
|
|
_serviceProvider = serviceProvider;
|
2024-04-08 15:39:44 -04:00
|
|
|
|
_logger = logger;
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
2022-08-29 15:53:48 -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))
|
2017-05-26 00:50:27 -04:00
|
|
|
|
{
|
2018-08-06 09:04:31 -04:00
|
|
|
|
return;
|
2017-05-26 00:50:27 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-11-14 08:51:24 -05:00
|
|
|
|
var installation = new Installation
|
2017-05-26 00:50:27 -04:00
|
|
|
|
{
|
2017-08-11 08:57:31 -04:00
|
|
|
|
InstallationId = deviceId,
|
|
|
|
|
|
PushChannel = pushToken,
|
2017-05-26 00:50:27 -04:00
|
|
|
|
Templates = new Dictionary<string, InstallationTemplate>()
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2017-08-11 08:57:31 -04:00
|
|
|
|
installation.Tags = new List<string>
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2017-08-11 08:57:31 -04:00
|
|
|
|
$"userId:{userId}"
|
2017-05-26 00:50:27 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
2017-05-26 22:52:50 -04:00
|
|
|
|
if (!string.IsNullOrWhiteSpace(identifier))
|
2017-05-26 00:50:27 -04:00
|
|
|
|
{
|
|
|
|
|
|
installation.Tags.Add("deviceIdentifier:" + identifier);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-08-06 09:04:31 -04:00
|
|
|
|
string payloadTemplate = null, messageTemplate = null, badgeMessageTemplate = null;
|
2020-03-27 14:36:37 -04:00
|
|
|
|
switch (type)
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2017-08-11 08:57:31 -04:00
|
|
|
|
case DeviceType.Android:
|
2024-05-02 16:37:06 -04:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2022-08-29 15:53:48 -04:00
|
|
|
|
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)\"}," +
|
2022-01-14 10:52:50 -03:00
|
|
|
|
"\"aps\":{\"content-available\":1}}";
|
2017-05-26 22:52:50 -04:00
|
|
|
|
messageTemplate = "{\"data\":{\"type\":\"#(type)\"}," +
|
2018-08-06 09:04:31 -04:00
|
|
|
|
"\"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
|
|
|
|
|
2017-05-26 00:50:27 -04:00
|
|
|
|
installation.Platform = NotificationPlatform.Apns;
|
2022-08-29 15:53:48 -04:00
|
|
|
|
break;
|
2018-08-06 09:04:31 -04:00
|
|
|
|
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
|
|
|
|
|
2018-08-06 09:04:31 -04:00
|
|
|
|
installation.Platform = NotificationPlatform.Adm;
|
2022-08-29 15:53:48 -04:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-05-26 22:52:50 -04:00
|
|
|
|
|
2019-02-26 08:15:56 -05: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);
|
2017-05-26 00:50:27 -04:00
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
await ClientFor(GetComb(deviceId)).CreateOrUpdateInstallationAsync(installation);
|
2017-08-11 08:57:31 -04:00
|
|
|
|
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
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)
|
|
|
|
|
|
{
|
2022-08-29 15:53:48 -04:00
|
|
|
|
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
|
|
|
|
|
2020-03-27 14:36:37 -04:00
|
|
|
|
var template = new InstallationTemplate
|
2017-11-14 08:51:24 -05:00
|
|
|
|
{
|
2017-08-11 08:57:31 -04:00
|
|
|
|
Body = templateBody,
|
2019-03-19 00:39:03 -04:00
|
|
|
|
Tags = new List<string>
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2019-03-19 00:39:03 -04:00
|
|
|
|
fullTemplateId,
|
|
|
|
|
|
$"{fullTemplateId}_userId:{userId}"
|
2017-11-14 08:51:24 -05:00
|
|
|
|
}
|
2022-08-29 16:06:55 -04:00
|
|
|
|
};
|
2022-08-29 15:53:48 -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 00:50:27 -04:00
|
|
|
|
}
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
public async Task DeleteRegistrationAsync(string deviceId)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
|
|
|
|
|
try
|
2017-05-26 22:52:50 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
await ClientFor(GetComb(deviceId)).DeleteInstallationAsync(deviceId);
|
2019-03-19 00:39:03 -04:00
|
|
|
|
if (InstallationDeviceEntity.IsInstallationDeviceId(deviceId))
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2019-03-19 00:39:03 -04:00
|
|
|
|
await _installationDeviceRepository.DeleteAsync(new InstallationDeviceEntity(deviceId));
|
2022-08-29 15:53:48 -04:00
|
|
|
|
}
|
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
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
public async Task AddUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
await PatchTagsForUserDevicesAsync(deviceIds, UpdateOperationType.Add, $"organizationId:{organizationId}");
|
|
|
|
|
|
if (deviceIds.Any() && InstallationDeviceEntity.IsInstallationDeviceId(deviceIds.First()))
|
2017-05-26 22:52:50 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
var entities = deviceIds.Select(e => new InstallationDeviceEntity(e));
|
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
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
public async Task DeleteUserRegistrationOrganizationAsync(IEnumerable<string> deviceIds, string organizationId)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
await PatchTagsForUserDevicesAsync(deviceIds, UpdateOperationType.Remove,
|
2017-08-11 08:57:31 -04:00
|
|
|
|
$"organizationId:{organizationId}");
|
2024-10-22 09:20:57 -07:00
|
|
|
|
if (deviceIds.Any() && InstallationDeviceEntity.IsInstallationDeviceId(deviceIds.First()))
|
2017-05-26 22:52:50 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
var entities = deviceIds.Select(e => new InstallationDeviceEntity(e));
|
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
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
private async Task PatchTagsForUserDevicesAsync(IEnumerable<string> deviceIds, UpdateOperationType op,
|
2018-08-06 09:04:31 -04:00
|
|
|
|
string tag)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
if (!deviceIds.Any())
|
2017-05-26 22:52:50 -04:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-27 14:36:37 -04:00
|
|
|
|
var operation = new PartialUpdateOperation
|
2017-11-14 09:35:48 -05:00
|
|
|
|
{
|
2020-03-27 14:36:37 -04:00
|
|
|
|
Operation = op,
|
2017-11-14 09:35:48 -05:00
|
|
|
|
Path = "/tags"
|
2022-08-29 14:53:16 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
2020-03-27 14:36:37 -04:00
|
|
|
|
if (op == UpdateOperationType.Add)
|
2017-11-14 09:35:48 -05:00
|
|
|
|
{
|
|
|
|
|
|
operation.Value = tag;
|
|
|
|
|
|
}
|
2020-03-27 14:36:37 -04:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
foreach (var deviceId in deviceIds)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
|
|
|
|
|
try
|
2017-05-26 22:52:50 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
await ClientFor(GetComb(deviceId)).PatchInstallationAsync(deviceId, new List<PartialUpdateOperation> { operation });
|
2022-08-29 15:53:48 -04:00
|
|
|
|
}
|
2020-03-27 14:36:37 -04:00
|
|
|
|
catch (Exception e) when (e.InnerException == null || !e.InnerException.Message.Contains("(404) Not Found"))
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2021-12-16 15:35:07 -05:00
|
|
|
|
throw;
|
2017-05-26 22:52:50 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-05-26 00:50:27 -04:00
|
|
|
|
}
|
2024-04-08 15:39:44 -04:00
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
private NotificationHubClient ClientFor(Guid deviceId)
|
2024-04-08 15:39:44 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
return _notificationHubPool.ClientFor(deviceId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Guid GetComb(string deviceId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var deviceIdString = deviceId;
|
|
|
|
|
|
InstallationDeviceEntity installationDeviceEntity;
|
|
|
|
|
|
Guid deviceIdGuid;
|
|
|
|
|
|
if (InstallationDeviceEntity.TryParse(deviceIdString, out installationDeviceEntity))
|
2024-04-08 15:39:44 -04:00
|
|
|
|
{
|
2024-10-22 09:20:57 -07:00
|
|
|
|
// Strip off the installation id (PartitionId). RowKey is the ID in the Installation's table.
|
|
|
|
|
|
deviceIdString = installationDeviceEntity.RowKey;
|
2024-04-08 15:39:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-22 09:20:57 -07:00
|
|
|
|
if (Guid.TryParse(deviceIdString, out deviceIdGuid))
|
2024-04-08 15:39:44 -04:00
|
|
|
|
{
|
|
|
|
|
|
}
|
2024-10-22 09:20:57 -07:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($"Invalid device id {deviceId}.");
|
|
|
|
|
|
}
|
|
|
|
|
|
return deviceIdGuid;
|
2024-04-08 15:39:44 -04:00
|
|
|
|
}
|
2017-05-26 00:50:27 -04:00
|
|
|
|
}
|