mirror of
https://github.com/bitwarden/server.git
synced 2025-04-25 23:02:17 -05:00
[PM-18555] Notifications service tests (#5473)
* Add RelayPush Notifications Tests * Nullable Test Fixup * Azure Queue Notifications Tests * NotificationsHub Push Tests * Make common base for API based notifications * Register TimeProvider just in case * Format * React to TaskId * Remove completed TODO
This commit is contained in:
parent
c986cbb208
commit
4d6e4d35f2
@ -32,20 +32,22 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
private readonly INotificationHubPool _notificationHubPool;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public NotificationHubPushNotificationService(
|
||||
IInstallationDeviceRepository installationDeviceRepository,
|
||||
INotificationHubPool notificationHubPool,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<NotificationHubPushNotificationService> logger,
|
||||
IGlobalSettings globalSettings)
|
||||
IGlobalSettings globalSettings,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_installationDeviceRepository = installationDeviceRepository;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_notificationHubPool = notificationHubPool;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings;
|
||||
|
||||
_timeProvider = timeProvider;
|
||||
if (globalSettings.Installation.Id == Guid.Empty)
|
||||
{
|
||||
logger.LogWarning("Installation ID is not set. Push notifications for installations will not work.");
|
||||
@ -152,7 +154,7 @@ public class NotificationHubPushNotificationService : IPushNotificationService
|
||||
|
||||
private async Task PushUserAsync(Guid userId, PushType type, bool excludeCurrentContext = false)
|
||||
{
|
||||
var message = new UserPushNotification { UserId = userId, Date = DateTime.UtcNow };
|
||||
var message = new UserPushNotification { UserId = userId, Date = _timeProvider.GetUtcNow().UtcDateTime };
|
||||
|
||||
await SendPayloadToUserAsync(userId, type, message, excludeCurrentContext);
|
||||
}
|
||||
|
@ -22,17 +22,19 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
private readonly QueueClient _queueClient;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public AzureQueuePushNotificationService(
|
||||
[FromKeyedServices("notifications")] QueueClient queueClient,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IGlobalSettings globalSettings,
|
||||
ILogger<AzureQueuePushNotificationService> logger)
|
||||
ILogger<AzureQueuePushNotificationService> logger,
|
||||
TimeProvider timeProvider)
|
||||
{
|
||||
_queueClient = queueClient;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_globalSettings = globalSettings;
|
||||
|
||||
_timeProvider = timeProvider;
|
||||
if (globalSettings.Installation.Id == Guid.Empty)
|
||||
{
|
||||
logger.LogWarning("Installation ID is not set. Push notifications for installations will not work.");
|
||||
@ -140,7 +142,7 @@ public class AzureQueuePushNotificationService : IPushNotificationService
|
||||
|
||||
private async Task PushUserAsync(Guid userId, PushType type, bool excludeCurrentContext = false)
|
||||
{
|
||||
var message = new UserPushNotification { UserId = userId, Date = DateTime.UtcNow };
|
||||
var message = new UserPushNotification { UserId = userId, Date = _timeProvider.GetUtcNow().UtcDateTime };
|
||||
|
||||
await SendMessageAsync(type, message, excludeCurrentContext);
|
||||
}
|
||||
|
@ -24,12 +24,14 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
{
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public NotificationsApiPushNotificationService(
|
||||
IHttpClientFactory httpFactory,
|
||||
GlobalSettings globalSettings,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<NotificationsApiPushNotificationService> logger)
|
||||
ILogger<NotificationsApiPushNotificationService> logger,
|
||||
TimeProvider timeProvider)
|
||||
: base(
|
||||
httpFactory,
|
||||
globalSettings.BaseServiceUri.InternalNotifications,
|
||||
@ -41,6 +43,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
@ -148,7 +151,7 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
|
||||
var message = new UserPushNotification
|
||||
{
|
||||
UserId = userId,
|
||||
Date = DateTime.UtcNow
|
||||
Date = _timeProvider.GetUtcNow().UtcDateTime,
|
||||
};
|
||||
|
||||
await SendMessageAsync(type, message, excludeCurrentContext);
|
||||
|
@ -27,13 +27,15 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly IGlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
|
||||
public RelayPushNotificationService(
|
||||
IHttpClientFactory httpFactory,
|
||||
IDeviceRepository deviceRepository,
|
||||
GlobalSettings globalSettings,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<RelayPushNotificationService> logger)
|
||||
ILogger<RelayPushNotificationService> logger,
|
||||
TimeProvider timeProvider)
|
||||
: base(
|
||||
httpFactory,
|
||||
globalSettings.PushRelayBaseUri,
|
||||
@ -46,6 +48,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
_deviceRepository = deviceRepository;
|
||||
_globalSettings = globalSettings;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_timeProvider = timeProvider;
|
||||
}
|
||||
|
||||
public async Task PushSyncCipherCreateAsync(Cipher cipher, IEnumerable<Guid> collectionIds)
|
||||
@ -147,7 +150,7 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
|
||||
|
||||
private async Task PushUserAsync(Guid userId, PushType type, bool excludeCurrentContext = false)
|
||||
{
|
||||
var message = new UserPushNotification { UserId = userId, Date = DateTime.UtcNow };
|
||||
var message = new UserPushNotification { UserId = userId, Date = _timeProvider.GetUtcNow().UtcDateTime };
|
||||
|
||||
await SendPayloadToUserAsync(userId, type, message, excludeCurrentContext);
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ using Microsoft.Extensions.Caching.Cosmos;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
@ -279,6 +280,8 @@ public static class ServiceCollectionExtensions
|
||||
services.AddSingleton<IMailDeliveryService, NoopMailDeliveryService>();
|
||||
}
|
||||
|
||||
services.TryAddSingleton(TimeProvider.System);
|
||||
|
||||
services.AddSingleton<IPushNotificationService, MultiServicePushNotificationService>();
|
||||
if (globalSettings.SelfHosted)
|
||||
{
|
||||
|
@ -1,15 +1,25 @@
|
||||
#nullable enable
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.NotificationCenter.Enums;
|
||||
using Bit.Core.NotificationHub;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
@ -19,6 +29,10 @@ namespace Bit.Core.Test.NotificationHub;
|
||||
[NotificationStatusCustomize]
|
||||
public class NotificationHubPushNotificationServiceTests
|
||||
{
|
||||
private static readonly string _deviceIdentifier = "test_device_identifier";
|
||||
private static readonly DateTime _now = DateTime.UtcNow;
|
||||
private static readonly Guid _installationId = Guid.Parse("da73177b-513f-4444-b582-595c890e1022");
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
[NotificationCustomize]
|
||||
@ -496,6 +510,630 @@ public class NotificationHubPushNotificationServiceTests
|
||||
.UpsertAsync(Arg.Any<InstallationDeviceEntity>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCipherCreateAsync_SendExpectedData()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = cipher.OrganizationId,
|
||||
["CollectionIds"] = new JsonArray(collectionId),
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherCreateAsync(cipher, [collectionId]),
|
||||
PushType.SyncCipherCreate,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCipherUpdateAsync_SendExpectedData()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = cipher.OrganizationId,
|
||||
["CollectionIds"] = new JsonArray(collectionId),
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherUpdateAsync(cipher, [collectionId]),
|
||||
PushType.SyncCipherUpdate,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCipherDeleteAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = cipher.OrganizationId,
|
||||
["CollectionIds"] = null,
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherDeleteAsync(cipher),
|
||||
PushType.SyncLoginDelete,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderCreateAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderCreateAsync(folder),
|
||||
PushType.SyncFolderCreate,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderUpdateAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderUpdateAsync(folder),
|
||||
PushType.SyncFolderUpdate,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendCreateAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendCreateAsync(send),
|
||||
PushType.SyncSendCreate,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushAuthRequestAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushAuthRequestAsync(authRequest),
|
||||
PushType.AuthRequest,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushAuthRequestResponseAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushAuthRequestResponseAsync(authRequest),
|
||||
PushType.AuthRequestResponse,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendUpdateAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendUpdateAsync(send),
|
||||
PushType.SyncSendUpdate,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendDeleteAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendDeleteAsync(send),
|
||||
PushType.SyncSendDelete,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCiphersAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _now,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCiphersAsync(userId),
|
||||
PushType.SyncCiphers,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncVaultAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _now,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncVaultAsync(userId),
|
||||
PushType.SyncVault,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrganizationsAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _now,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrganizationsAsync(userId),
|
||||
PushType.SyncOrganizations,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrgKeysAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _now,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrgKeysAsync(userId),
|
||||
PushType.SyncOrgKeys,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId})"
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSettingsAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _now,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSettingsAsync(userId),
|
||||
PushType.SyncSettings,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId})"
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task PushLogOutAsync_SendExpectedData(bool excludeCurrentContext)
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _now,
|
||||
};
|
||||
|
||||
var expectedTag = excludeCurrentContext
|
||||
? $"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
: $"(template:payload_userId:{userId})";
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushLogOutAsync(userId, excludeCurrentContext),
|
||||
PushType.LogOut,
|
||||
expectedPayload,
|
||||
expectedTag
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderDeleteAsync_SendExpectedData()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderDeleteAsync(folder),
|
||||
PushType.SyncFolderDelete,
|
||||
expectedPayload,
|
||||
$"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})"
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, null, null)]
|
||||
[InlineData(false, "e8e08ce8-8a26-4a65-913a-ba1d8c478b2f", null)]
|
||||
[InlineData(false, null, "2f53ee32-edf9-4169-b276-760fe92e03bf")]
|
||||
public async Task PushNotificationAsync_SendExpectedData(bool global, string? userId, string? organizationId)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Priority = Priority.High,
|
||||
Global = global,
|
||||
ClientType = ClientType.All,
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
TaskId = Guid.NewGuid(),
|
||||
Title = "My Title",
|
||||
Body = "My Body",
|
||||
CreationDate = DateTime.UtcNow.AddDays(-1),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
JsonNode? installationId = global ? _installationId : null;
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["TaskId"] = notification.TaskId,
|
||||
["InstallationId"] = installationId,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
["ReadDate"] = null,
|
||||
["DeletedDate"] = null,
|
||||
};
|
||||
|
||||
string expectedTag;
|
||||
|
||||
if (global)
|
||||
{
|
||||
expectedTag = $"(template:payload && installationId:{_installationId} && !deviceIdentifier:{_deviceIdentifier})";
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
expectedTag = "(template:payload && organizationId:2f53ee32-edf9-4169-b276-760fe92e03bf && !deviceIdentifier:test_device_identifier)";
|
||||
}
|
||||
else
|
||||
{
|
||||
expectedTag = $"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})";
|
||||
}
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushNotificationAsync(notification),
|
||||
PushType.Notification,
|
||||
expectedPayload,
|
||||
expectedTag
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, null, null)]
|
||||
[InlineData(false, "e8e08ce8-8a26-4a65-913a-ba1d8c478b2f", null)]
|
||||
[InlineData(false, null, "2f53ee32-edf9-4169-b276-760fe92e03bf")]
|
||||
public async Task PushNotificationStatusAsync_SendExpectedData(bool global, string? userId, string? organizationId)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Priority = Priority.High,
|
||||
Global = global,
|
||||
ClientType = ClientType.All,
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
Title = "My Title",
|
||||
Body = "My Body",
|
||||
CreationDate = DateTime.UtcNow.AddDays(-1),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var notificationStatus = new NotificationStatus
|
||||
{
|
||||
ReadDate = DateTime.UtcNow.AddDays(-1),
|
||||
DeletedDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
JsonNode? installationId = global ? _installationId : null;
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["TaskId"] = notification.TaskId,
|
||||
["InstallationId"] = installationId,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
["ReadDate"] = notificationStatus.ReadDate,
|
||||
["DeletedDate"] = notificationStatus.DeletedDate,
|
||||
};
|
||||
|
||||
string expectedTag;
|
||||
|
||||
if (global)
|
||||
{
|
||||
expectedTag = $"(template:payload && installationId:{_installationId} && !deviceIdentifier:{_deviceIdentifier})";
|
||||
}
|
||||
else if (notification.OrganizationId.HasValue)
|
||||
{
|
||||
expectedTag = "(template:payload && organizationId:2f53ee32-edf9-4169-b276-760fe92e03bf && !deviceIdentifier:test_device_identifier)";
|
||||
}
|
||||
else
|
||||
{
|
||||
expectedTag = $"(template:payload_userId:{userId} && !deviceIdentifier:{_deviceIdentifier})";
|
||||
}
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushNotificationStatusAsync(notification, notificationStatus),
|
||||
PushType.NotificationStatus,
|
||||
expectedPayload,
|
||||
expectedTag
|
||||
);
|
||||
}
|
||||
|
||||
private async Task VerifyNotificationAsync(Func<NotificationHubPushNotificationService, Task> test,
|
||||
PushType type, JsonNode expectedPayload, string tag)
|
||||
{
|
||||
var installationDeviceRepository = Substitute.For<IInstallationDeviceRepository>();
|
||||
|
||||
var notificationHubPool = Substitute.For<INotificationHubPool>();
|
||||
|
||||
var notificationHubProxy = Substitute.For<INotificationHubProxy>();
|
||||
|
||||
notificationHubPool.AllClients
|
||||
.Returns(notificationHubProxy);
|
||||
|
||||
var httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.DeviceIdentifier = _deviceIdentifier;
|
||||
serviceCollection.AddSingleton(currentContext);
|
||||
|
||||
httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
||||
|
||||
httpContextAccessor.HttpContext
|
||||
.Returns(httpContext);
|
||||
|
||||
var globalSettings = new Core.Settings.GlobalSettings();
|
||||
globalSettings.Installation.Id = _installationId;
|
||||
|
||||
var fakeTimeProvider = new FakeTimeProvider();
|
||||
|
||||
fakeTimeProvider.SetUtcNow(_now);
|
||||
|
||||
var sut = new NotificationHubPushNotificationService(
|
||||
installationDeviceRepository,
|
||||
notificationHubPool,
|
||||
httpContextAccessor,
|
||||
NullLogger<NotificationHubPushNotificationService>.Instance,
|
||||
globalSettings,
|
||||
fakeTimeProvider
|
||||
);
|
||||
|
||||
// Act
|
||||
await test(sut);
|
||||
|
||||
// Assert
|
||||
var calls = notificationHubProxy.ReceivedCalls();
|
||||
var methodInfo = typeof(INotificationHubProxy).GetMethod(nameof(INotificationHubProxy.SendTemplateNotificationAsync));
|
||||
var call = Assert.Single(calls, c => c.GetMethodInfo() == methodInfo);
|
||||
|
||||
var arguments = call.GetArguments();
|
||||
|
||||
var dictionaryArg = (Dictionary<string, string>)arguments[0]!;
|
||||
var tagArg = (string)arguments[1]!;
|
||||
|
||||
Assert.Equal(2, dictionaryArg.Count);
|
||||
Assert.True(dictionaryArg.TryGetValue("type", out var typeString));
|
||||
Assert.True(byte.TryParse(typeString, out var typeByte));
|
||||
Assert.Equal(type, (PushType)typeByte);
|
||||
|
||||
Assert.True(dictionaryArg.TryGetValue("payload", out var payloadString));
|
||||
var actualPayloadNode = JsonNode.Parse(payloadString);
|
||||
|
||||
Assert.True(JsonNode.DeepEquals(expectedPayload, actualPayloadNode));
|
||||
|
||||
Assert.Equal(tag, tagArg);
|
||||
}
|
||||
|
||||
private static NotificationPushNotification ToNotificationPushNotification(Notification notification,
|
||||
NotificationStatus? notificationStatus, Guid? installationId) =>
|
||||
new()
|
||||
|
@ -1,18 +1,27 @@
|
||||
#nullable enable
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Azure.Storage.Queues;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.NotificationCenter.Enums;
|
||||
using Bit.Core.Platform.Push.Internal;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Test.AutoFixture;
|
||||
using Bit.Core.Test.AutoFixture.CurrentContextFixtures;
|
||||
using Bit.Core.Test.NotificationCenter.AutoFixture;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
@ -22,6 +31,17 @@ namespace Bit.Core.Test.Platform.Push.Services;
|
||||
[SutProviderCustomize]
|
||||
public class AzureQueuePushNotificationServiceTests
|
||||
{
|
||||
private static readonly Guid _deviceId = Guid.Parse("c4730f80-caaa-4772-97bd-5c0d23a2baa3");
|
||||
private static readonly string _deviceIdentifier = "test_device_identifier";
|
||||
private readonly FakeTimeProvider _fakeTimeProvider;
|
||||
private readonly Core.Settings.GlobalSettings _globalSettings = new();
|
||||
|
||||
public AzureQueuePushNotificationServiceTests()
|
||||
{
|
||||
_fakeTimeProvider = new();
|
||||
_fakeTimeProvider.SetUtcNow(DateTime.UtcNow);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData]
|
||||
[NotificationCustomize]
|
||||
@ -112,6 +132,761 @@ public class AzureQueuePushNotificationServiceTests
|
||||
deviceIdentifier.ToString())));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("6a5bbe1b-cf16-49a6-965f-5c2eac56a531", null)]
|
||||
[InlineData(null, "b9a3fcb4-2447-45c1-aad2-24de43c88c44")]
|
||||
public async Task PushSyncCipherCreateAsync_SendsExpectedResponse(string? userId, string? organizationId)
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 1,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = cipher.OrganizationId,
|
||||
["CollectionIds"] = new JsonArray(collectionId),
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
if (!cipher.UserId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("UserId");
|
||||
}
|
||||
|
||||
if (!cipher.OrganizationId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("OrganizationId");
|
||||
expectedPayload["Payload"]!.AsObject().Remove("CollectionIds");
|
||||
}
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherCreateAsync(cipher, [collectionId]),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("6a5bbe1b-cf16-49a6-965f-5c2eac56a531", null)]
|
||||
[InlineData(null, "b9a3fcb4-2447-45c1-aad2-24de43c88c44")]
|
||||
public async Task PushSyncCipherUpdateAsync_SendsExpectedResponse(string? userId, string? organizationId)
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 0,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = cipher.OrganizationId,
|
||||
["CollectionIds"] = new JsonArray(collectionId),
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
if (!cipher.UserId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("UserId");
|
||||
}
|
||||
|
||||
if (!cipher.OrganizationId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("OrganizationId");
|
||||
expectedPayload["Payload"]!.AsObject().Remove("CollectionIds");
|
||||
}
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherUpdateAsync(cipher, [collectionId]),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCipherDeleteAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
OrganizationId = null,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 2,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherDeleteAsync(cipher),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderCreateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 7,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderCreateAsync(folder),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderUpdateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 8,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderUpdateAsync(folder),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderDeleteAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 3,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderDeleteAsync(folder),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCiphersAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 4,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _fakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCiphersAsync(userId),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncVaultAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 5,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _fakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncVaultAsync(userId),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrganizationsAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 17,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _fakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrganizationsAsync(userId),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrgKeysAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 6,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _fakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrgKeysAsync(userId),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSettingsAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 10,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _fakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSettingsAsync(userId),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task PushLogOutAsync_SendsExpectedResponse(bool excludeCurrentContext)
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 11,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _fakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
};
|
||||
|
||||
if (excludeCurrentContext)
|
||||
{
|
||||
expectedPayload["ContextId"] = _deviceIdentifier;
|
||||
}
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushLogOutAsync(userId, excludeCurrentContext),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendCreateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 12,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendCreateAsync(send),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendUpdateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 13,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendUpdateAsync(send),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendDeleteAsync_SendsExpectedResponse()
|
||||
{
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 14,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendDeleteAsync(send),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushAuthRequestAsync_SendsExpectedResponse()
|
||||
{
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 15,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushAuthRequestAsync(authRequest),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushAuthRequestResponseAsync_SendsExpectedResponse()
|
||||
{
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 16,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushAuthRequestResponseAsync(authRequest),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, null, null)]
|
||||
[InlineData(false, "e8e08ce8-8a26-4a65-913a-ba1d8c478b2f", null)]
|
||||
[InlineData(false, null, "2f53ee32-edf9-4169-b276-760fe92e03bf")]
|
||||
public async Task PushNotificationAsync_SendsExpectedResponse(bool global, string? userId, string? organizationId)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Priority = Priority.High,
|
||||
Global = global,
|
||||
ClientType = ClientType.All,
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
Title = "My Title",
|
||||
Body = "My Body",
|
||||
CreationDate = DateTime.UtcNow.AddDays(-1),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 20,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["InstallationId"] = _globalSettings.Installation.Id,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
if (!global)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("InstallationId");
|
||||
}
|
||||
|
||||
if (!notification.UserId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("UserId");
|
||||
}
|
||||
|
||||
if (!notification.OrganizationId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("OrganizationId");
|
||||
}
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushNotificationAsync(notification),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, null, null)]
|
||||
[InlineData(false, "e8e08ce8-8a26-4a65-913a-ba1d8c478b2f", null)]
|
||||
[InlineData(false, null, "2f53ee32-edf9-4169-b276-760fe92e03bf")]
|
||||
public async Task PushNotificationStatusAsync_SendsExpectedResponse(bool global, string? userId, string? organizationId)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Priority = Priority.High,
|
||||
Global = global,
|
||||
ClientType = ClientType.All,
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
Title = "My Title",
|
||||
Body = "My Body",
|
||||
CreationDate = DateTime.UtcNow.AddDays(-1),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var notificationStatus = new NotificationStatus
|
||||
{
|
||||
ReadDate = DateTime.UtcNow,
|
||||
DeletedDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 21,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["InstallationId"] = _globalSettings.Installation.Id,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
["ReadDate"] = notificationStatus.ReadDate,
|
||||
["DeletedDate"] = notificationStatus.DeletedDate,
|
||||
},
|
||||
["ContextId"] = _deviceIdentifier,
|
||||
};
|
||||
|
||||
if (!global)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("InstallationId");
|
||||
}
|
||||
|
||||
if (!notification.UserId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("UserId");
|
||||
}
|
||||
|
||||
if (!notification.OrganizationId.HasValue)
|
||||
{
|
||||
expectedPayload["Payload"]!.AsObject().Remove("OrganizationId");
|
||||
}
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushNotificationStatusAsync(notification, notificationStatus),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrganizationStatusAsync_SendsExpectedResponse()
|
||||
{
|
||||
var organization = new Organization
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Enabled = true,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 18,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["OrganizationId"] = organization.Id,
|
||||
["Enabled"] = organization.Enabled,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrganizationStatusAsync(organization),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrganizationCollectionManagementSettingsAsync_SendsExpectedResponse()
|
||||
{
|
||||
var organization = new Organization
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Enabled = true,
|
||||
LimitCollectionCreation = true,
|
||||
LimitCollectionDeletion = true,
|
||||
LimitItemDeletion = true,
|
||||
};
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 19,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["OrganizationId"] = organization.Id,
|
||||
["LimitCollectionCreation"] = organization.LimitCollectionCreation,
|
||||
["LimitCollectionDeletion"] = organization.LimitCollectionDeletion,
|
||||
["LimitItemDeletion"] = organization.LimitItemDeletion,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrganizationCollectionManagementSettingsAsync(organization),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushPendingSecurityTasksAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
var expectedPayload = new JsonObject
|
||||
{
|
||||
["Type"] = 22,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = _fakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushPendingSecurityTasksAsync(userId),
|
||||
expectedPayload
|
||||
);
|
||||
}
|
||||
|
||||
// [Fact]
|
||||
// public async Task SendPayloadToInstallationAsync_ThrowsNotImplementedException()
|
||||
// {
|
||||
// await Assert.ThrowsAsync<NotImplementedException>(
|
||||
// async () => await sut.SendPayloadToInstallationAsync("installation_id", PushType.AuthRequest, new {}, null)
|
||||
// );
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task SendPayloadToUserAsync_ThrowsNotImplementedException()
|
||||
// {
|
||||
// await Assert.ThrowsAsync<NotImplementedException>(
|
||||
// async () => await _sut.SendPayloadToUserAsync("user_id", PushType.AuthRequest, new {}, null)
|
||||
// );
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task SendPayloadToOrganizationAsync_ThrowsNotImplementedException()
|
||||
// {
|
||||
// await Assert.ThrowsAsync<NotImplementedException>(
|
||||
// async () => await _sut.SendPayloadToOrganizationAsync("organization_id", PushType.AuthRequest, new {}, null)
|
||||
// );
|
||||
// }
|
||||
|
||||
private async Task VerifyNotificationAsync(Func<AzureQueuePushNotificationService, Task> test, JsonNode expectedMessage)
|
||||
{
|
||||
var queueClient = Substitute.For<QueueClient>();
|
||||
|
||||
var httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.DeviceIdentifier = _deviceIdentifier;
|
||||
serviceCollection.AddSingleton(currentContext);
|
||||
|
||||
httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
||||
|
||||
httpContextAccessor.HttpContext
|
||||
.Returns(httpContext);
|
||||
|
||||
var globalSettings = new Core.Settings.GlobalSettings();
|
||||
|
||||
var sut = new AzureQueuePushNotificationService(
|
||||
queueClient,
|
||||
httpContextAccessor,
|
||||
globalSettings,
|
||||
NullLogger<AzureQueuePushNotificationService>.Instance,
|
||||
_fakeTimeProvider
|
||||
);
|
||||
|
||||
await test(sut);
|
||||
|
||||
// Hoist equality checker outside the expression so that we
|
||||
// can more easily place a breakpoint
|
||||
var checkEquality = (string actual) =>
|
||||
{
|
||||
var actualNode = JsonNode.Parse(actual);
|
||||
return JsonNode.DeepEquals(actualNode, expectedMessage);
|
||||
};
|
||||
|
||||
await queueClient
|
||||
.Received(1)
|
||||
.SendMessageAsync(Arg.Is<string>((actual) => checkEquality(actual)));
|
||||
}
|
||||
|
||||
private static bool MatchMessage<T>(PushType pushType, string message, IEquatable<T> expectedPayloadEquatable,
|
||||
string contextId)
|
||||
{
|
||||
|
@ -1,41 +1,385 @@
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
using System.Text.Json.Nodes;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace Bit.Core.Test.Platform.Push.Services;
|
||||
|
||||
public class NotificationsApiPushNotificationServiceTests
|
||||
public class NotificationsApiPushNotificationServiceTests : PushTestBase
|
||||
{
|
||||
private readonly NotificationsApiPushNotificationService _sut;
|
||||
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<NotificationsApiPushNotificationService> _logger;
|
||||
|
||||
public NotificationsApiPushNotificationServiceTests()
|
||||
{
|
||||
_httpFactory = Substitute.For<IHttpClientFactory>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
_logger = Substitute.For<ILogger<NotificationsApiPushNotificationService>>();
|
||||
GlobalSettings.BaseServiceUri.InternalNotifications = "https://localhost:7777";
|
||||
GlobalSettings.BaseServiceUri.InternalIdentity = "https://localhost:8888";
|
||||
}
|
||||
|
||||
_sut = new NotificationsApiPushNotificationService(
|
||||
_httpFactory,
|
||||
_globalSettings,
|
||||
_httpContextAccessor,
|
||||
_logger
|
||||
protected override string ExpectedClientUrl() => "https://localhost:7777/send";
|
||||
|
||||
protected override IPushNotificationService CreateService()
|
||||
{
|
||||
return new NotificationsApiPushNotificationService(
|
||||
HttpClientFactory,
|
||||
GlobalSettings,
|
||||
HttpContextAccessor,
|
||||
NullLogger<NotificationsApiPushNotificationService>.Instance,
|
||||
FakeTimeProvider
|
||||
);
|
||||
}
|
||||
|
||||
// Remove this test when we add actual tests. It only proves that
|
||||
// we've properly constructed the system under test.
|
||||
[Fact(Skip = "Needs additional work")]
|
||||
public void ServiceExists()
|
||||
protected override JsonNode GetPushSyncCipherCreatePayload(Cipher cipher, Guid collectionId)
|
||||
{
|
||||
Assert.NotNull(_sut);
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 1,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["CollectionIds"] = new JsonArray(collectionId),
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushSyncCipherUpdatePayload(Cipher cipher, Guid collectionId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 0,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["CollectionIds"] = new JsonArray(collectionId),
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushSyncCipherDeletePayload(Cipher cipher)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 2,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["CollectionIds"] = null,
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncFolderCreatePayload(Folder folder)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 7,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncFolderUpdatePayload(Folder folder)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 8,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncFolderDeletePayload(Folder folder)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 3,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncCiphersPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 4,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncVaultPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 5,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncOrganizationsPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 17,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncOrgKeysPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 6,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncSettingsPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 10,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushLogOutPayload(Guid userId, bool excludeCurrentContext)
|
||||
{
|
||||
JsonNode? contextId = excludeCurrentContext ? DeviceIdentifier : null;
|
||||
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 11,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ContextId"] = contextId,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSendCreatePayload(Send send)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 12,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSendUpdatePayload(Send send)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 13,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSendDeletePayload(Send send)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 14,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushAuthRequestPayload(AuthRequest authRequest)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 15,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushAuthRequestResponsePayload(AuthRequest authRequest)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 16,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushNotificationResponsePayload(Notification notification, Guid? userId, Guid? organizationId)
|
||||
{
|
||||
JsonNode? installationId = notification.Global ? GlobalSettings.Installation.Id : null;
|
||||
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 20,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = notification.Global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["TaskId"] = notification.TaskId,
|
||||
["InstallationId"] = installationId,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
["ReadDate"] = null,
|
||||
["DeletedDate"] = null,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushNotificationStatusResponsePayload(Notification notification, NotificationStatus notificationStatus, Guid? userId, Guid? organizationId)
|
||||
{
|
||||
JsonNode? installationId = notification.Global ? GlobalSettings.Installation.Id : null;
|
||||
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 21,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = notification.Global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["InstallationId"] = installationId,
|
||||
["TaskId"] = notification.TaskId,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
["ReadDate"] = notificationStatus.ReadDate,
|
||||
["DeletedDate"] = notificationStatus.DeletedDate,
|
||||
},
|
||||
["ContextId"] = DeviceIdentifier,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncOrganizationStatusResponsePayload(Organization organization)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 18,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["OrganizationId"] = organization.Id,
|
||||
["Enabled"] = organization.Enabled,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncOrganizationCollectionManagementSettingsResponsePayload(Organization organization)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 19,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["OrganizationId"] = organization.Id,
|
||||
["LimitCollectionCreation"] = organization.LimitCollectionCreation,
|
||||
["LimitCollectionDeletion"] = organization.LimitCollectionDeletion,
|
||||
["LimitItemDeletion"] = organization.LimitItemDeletion,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushPendingSecurityTasksResponsePayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["Type"] = 22,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ContextId"] = null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
498
test/Core.Test/Platform/Push/Services/PushTestBase.cs
Normal file
498
test/Core.Test/Platform/Push/Services/PushTestBase.cs
Normal file
@ -0,0 +1,498 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.NotificationCenter.Enums;
|
||||
using Bit.Core.Platform.Push;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using NSubstitute;
|
||||
using RichardSzalay.MockHttp;
|
||||
using Xunit;
|
||||
|
||||
public abstract class PushTestBase
|
||||
{
|
||||
protected static readonly string DeviceIdentifier = "test_device_identifier";
|
||||
|
||||
protected readonly MockHttpMessageHandler MockClient = new();
|
||||
protected readonly MockHttpMessageHandler MockIdentityClient = new();
|
||||
|
||||
protected readonly IHttpClientFactory HttpClientFactory;
|
||||
protected readonly GlobalSettings GlobalSettings;
|
||||
protected readonly IHttpContextAccessor HttpContextAccessor;
|
||||
protected readonly FakeTimeProvider FakeTimeProvider;
|
||||
|
||||
public PushTestBase()
|
||||
{
|
||||
HttpClientFactory = Substitute.For<IHttpClientFactory>();
|
||||
|
||||
// Mock HttpClient
|
||||
HttpClientFactory.CreateClient("client")
|
||||
.Returns(new HttpClient(MockClient));
|
||||
|
||||
HttpClientFactory.CreateClient("identity")
|
||||
.Returns(new HttpClient(MockIdentityClient));
|
||||
|
||||
GlobalSettings = new GlobalSettings();
|
||||
HttpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
|
||||
FakeTimeProvider = new FakeTimeProvider();
|
||||
|
||||
FakeTimeProvider.SetUtcNow(DateTimeOffset.UtcNow);
|
||||
}
|
||||
|
||||
protected abstract IPushNotificationService CreateService();
|
||||
|
||||
protected abstract string ExpectedClientUrl();
|
||||
|
||||
protected abstract JsonNode GetPushSyncCipherCreatePayload(Cipher cipher, Guid collectionId);
|
||||
protected abstract JsonNode GetPushSyncCipherUpdatePayload(Cipher cipher, Guid collectionId);
|
||||
protected abstract JsonNode GetPushSyncCipherDeletePayload(Cipher cipher);
|
||||
protected abstract JsonNode GetPushSyncFolderCreatePayload(Folder folder);
|
||||
protected abstract JsonNode GetPushSyncFolderUpdatePayload(Folder folder);
|
||||
protected abstract JsonNode GetPushSyncFolderDeletePayload(Folder folder);
|
||||
protected abstract JsonNode GetPushSyncCiphersPayload(Guid userId);
|
||||
protected abstract JsonNode GetPushSyncVaultPayload(Guid userId);
|
||||
protected abstract JsonNode GetPushSyncOrganizationsPayload(Guid userId);
|
||||
protected abstract JsonNode GetPushSyncOrgKeysPayload(Guid userId);
|
||||
protected abstract JsonNode GetPushSyncSettingsPayload(Guid userId);
|
||||
protected abstract JsonNode GetPushLogOutPayload(Guid userId, bool excludeCurrentContext);
|
||||
protected abstract JsonNode GetPushSendCreatePayload(Send send);
|
||||
protected abstract JsonNode GetPushSendUpdatePayload(Send send);
|
||||
protected abstract JsonNode GetPushSendDeletePayload(Send send);
|
||||
protected abstract JsonNode GetPushAuthRequestPayload(AuthRequest authRequest);
|
||||
protected abstract JsonNode GetPushAuthRequestResponsePayload(AuthRequest authRequest);
|
||||
protected abstract JsonNode GetPushNotificationResponsePayload(Notification notification, Guid? userId, Guid? organizationId);
|
||||
protected abstract JsonNode GetPushNotificationStatusResponsePayload(Notification notification, NotificationStatus notificationStatus, Guid? userId, Guid? organizationId);
|
||||
protected abstract JsonNode GetPushSyncOrganizationStatusResponsePayload(Organization organization);
|
||||
protected abstract JsonNode GetPushSyncOrganizationCollectionManagementSettingsResponsePayload(Organization organization);
|
||||
protected abstract JsonNode GetPushPendingSecurityTasksResponsePayload(Guid userId);
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCipherCreateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
OrganizationId = null,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherCreateAsync(cipher, [collectionId]),
|
||||
GetPushSyncCipherCreatePayload(cipher, collectionId)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCipherUpdateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
OrganizationId = null,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherUpdateAsync(cipher, [collectionId]),
|
||||
GetPushSyncCipherUpdatePayload(cipher, collectionId)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCipherDeleteAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var cipher = new Cipher
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
OrganizationId = null,
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCipherDeleteAsync(cipher),
|
||||
GetPushSyncCipherDeletePayload(cipher)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderCreateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderCreateAsync(folder),
|
||||
GetPushSyncFolderCreatePayload(folder)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderUpdateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderUpdateAsync(folder),
|
||||
GetPushSyncFolderUpdatePayload(folder)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncFolderDeleteAsync_SendsExpectedResponse()
|
||||
{
|
||||
var collectionId = Guid.NewGuid();
|
||||
|
||||
var folder = new Folder
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncFolderDeleteAsync(folder),
|
||||
GetPushSyncFolderDeletePayload(folder)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncCiphersAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncCiphersAsync(userId),
|
||||
GetPushSyncCiphersPayload(userId)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncVaultAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncVaultAsync(userId),
|
||||
GetPushSyncVaultPayload(userId)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrganizationsAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrganizationsAsync(userId),
|
||||
GetPushSyncOrganizationsPayload(userId)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrgKeysAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrgKeysAsync(userId),
|
||||
GetPushSyncOrgKeysPayload(userId)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSettingsAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSettingsAsync(userId),
|
||||
GetPushSyncSettingsPayload(userId)
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task PushLogOutAsync_SendsExpectedResponse(bool excludeCurrentContext)
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushLogOutAsync(userId, excludeCurrentContext),
|
||||
GetPushLogOutPayload(userId, excludeCurrentContext)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendCreateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendCreateAsync(send),
|
||||
GetPushSendCreatePayload(send)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendUpdateAsync_SendsExpectedResponse()
|
||||
{
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendUpdateAsync(send),
|
||||
GetPushSendUpdatePayload(send)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncSendDeleteAsync_SendsExpectedResponse()
|
||||
{
|
||||
var send = new Send
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncSendDeleteAsync(send),
|
||||
GetPushSendDeletePayload(send)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushAuthRequestAsync_SendsExpectedResponse()
|
||||
{
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushAuthRequestAsync(authRequest),
|
||||
GetPushAuthRequestPayload(authRequest)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushAuthRequestResponseAsync_SendsExpectedResponse()
|
||||
{
|
||||
var authRequest = new AuthRequest
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
UserId = Guid.NewGuid(),
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushAuthRequestResponseAsync(authRequest),
|
||||
GetPushAuthRequestResponsePayload(authRequest)
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, null, null)]
|
||||
[InlineData(false, "e8e08ce8-8a26-4a65-913a-ba1d8c478b2f", null)]
|
||||
[InlineData(false, null, "2f53ee32-edf9-4169-b276-760fe92e03bf")]
|
||||
public async Task PushNotificationAsync_SendsExpectedResponse(bool global, string? userId, string? organizationId)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Priority = Priority.High,
|
||||
Global = global,
|
||||
ClientType = ClientType.All,
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
TaskId = Guid.NewGuid(),
|
||||
Title = "My Title",
|
||||
Body = "My Body",
|
||||
CreationDate = DateTime.UtcNow.AddDays(-1),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushNotificationAsync(notification),
|
||||
GetPushNotificationResponsePayload(notification, notification.UserId, notification.OrganizationId)
|
||||
);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, null, null)]
|
||||
[InlineData(false, "e8e08ce8-8a26-4a65-913a-ba1d8c478b2f", null)]
|
||||
[InlineData(false, null, "2f53ee32-edf9-4169-b276-760fe92e03bf")]
|
||||
public async Task PushNotificationStatusAsync_SendsExpectedResponse(bool global, string? userId, string? organizationId)
|
||||
{
|
||||
var notification = new Notification
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Priority = Priority.High,
|
||||
Global = global,
|
||||
ClientType = ClientType.All,
|
||||
UserId = userId != null ? Guid.Parse(userId) : null,
|
||||
OrganizationId = organizationId != null ? Guid.Parse(organizationId) : null,
|
||||
TaskId = Guid.NewGuid(),
|
||||
Title = "My Title",
|
||||
Body = "My Body",
|
||||
CreationDate = DateTime.UtcNow.AddDays(-1),
|
||||
RevisionDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
var notificationStatus = new NotificationStatus
|
||||
{
|
||||
ReadDate = DateTime.UtcNow,
|
||||
DeletedDate = DateTime.UtcNow,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushNotificationStatusAsync(notification, notificationStatus),
|
||||
GetPushNotificationStatusResponsePayload(notification, notificationStatus, notification.UserId, notification.OrganizationId)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrganizationStatusAsync_SendsExpectedResponse()
|
||||
{
|
||||
var organization = new Organization
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Enabled = true,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrganizationStatusAsync(organization),
|
||||
GetPushSyncOrganizationStatusResponsePayload(organization)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushSyncOrganizationCollectionManagementSettingsAsync_SendsExpectedResponse()
|
||||
{
|
||||
var organization = new Organization
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Enabled = true,
|
||||
LimitCollectionCreation = true,
|
||||
LimitCollectionDeletion = true,
|
||||
LimitItemDeletion = true,
|
||||
};
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushSyncOrganizationCollectionManagementSettingsAsync(organization),
|
||||
GetPushSyncOrganizationCollectionManagementSettingsResponsePayload(organization)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PushPendingSecurityTasksAsync_SendsExpectedResponse()
|
||||
{
|
||||
var userId = Guid.NewGuid();
|
||||
|
||||
await VerifyNotificationAsync(
|
||||
async sut => await sut.PushPendingSecurityTasksAsync(userId),
|
||||
GetPushPendingSecurityTasksResponsePayload(userId)
|
||||
);
|
||||
}
|
||||
|
||||
private async Task VerifyNotificationAsync(
|
||||
Func<IPushNotificationService, Task> test,
|
||||
JsonNode expectedRequestBody
|
||||
)
|
||||
{
|
||||
var httpContext = new DefaultHttpContext();
|
||||
|
||||
var serviceCollection = new ServiceCollection();
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.DeviceIdentifier = DeviceIdentifier;
|
||||
serviceCollection.AddSingleton(currentContext);
|
||||
|
||||
httpContext.RequestServices = serviceCollection.BuildServiceProvider();
|
||||
|
||||
HttpContextAccessor.HttpContext
|
||||
.Returns(httpContext);
|
||||
|
||||
var connectTokenRequest = MockIdentityClient
|
||||
.Expect(HttpMethod.Post, "https://localhost:8888/connect/token")
|
||||
.Respond(HttpStatusCode.OK, JsonContent.Create(new
|
||||
{
|
||||
access_token = CreateAccessToken(DateTime.UtcNow.AddDays(1)),
|
||||
}));
|
||||
|
||||
JsonNode actualNode = null;
|
||||
|
||||
var clientRequest = MockClient
|
||||
.Expect(HttpMethod.Post, ExpectedClientUrl())
|
||||
.With(request =>
|
||||
{
|
||||
if (request.Content is not JsonContent jsonContent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: What options?
|
||||
var actualString = JsonSerializer.Serialize(jsonContent.Value);
|
||||
actualNode = JsonNode.Parse(actualString);
|
||||
|
||||
return JsonNode.DeepEquals(actualNode, expectedRequestBody);
|
||||
})
|
||||
.Respond(HttpStatusCode.OK);
|
||||
|
||||
await test(CreateService());
|
||||
|
||||
Assert.NotNull(actualNode);
|
||||
|
||||
Assert.Equal(expectedRequestBody, actualNode, EqualityComparer<JsonNode>.Create(JsonNode.DeepEquals));
|
||||
|
||||
Assert.Equal(1, MockClient.GetMatchCount(clientRequest));
|
||||
}
|
||||
|
||||
protected static string CreateAccessToken(DateTime expirationTime)
|
||||
{
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var token = new JwtSecurityToken(expires: expirationTime);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
}
|
@ -1,45 +1,541 @@
|
||||
using Bit.Core.Platform.Push.Internal;
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json.Nodes;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.NotificationCenter.Entities;
|
||||
using Bit.Core.Platform.Push.Internal;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Settings;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Bit.Core.Tools.Entities;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Platform.Push.Services;
|
||||
|
||||
public class RelayPushNotificationServiceTests
|
||||
public class RelayPushNotificationServiceTests : PushTestBase
|
||||
{
|
||||
private readonly RelayPushNotificationService _sut;
|
||||
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
private static readonly Guid _deviceId = Guid.Parse("c4730f80-caaa-4772-97bd-5c0d23a2baa3");
|
||||
private readonly IDeviceRepository _deviceRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<RelayPushNotificationService> _logger;
|
||||
|
||||
public RelayPushNotificationServiceTests()
|
||||
{
|
||||
_httpFactory = Substitute.For<IHttpClientFactory>();
|
||||
_deviceRepository = Substitute.For<IDeviceRepository>();
|
||||
_globalSettings = new GlobalSettings();
|
||||
_httpContextAccessor = Substitute.For<IHttpContextAccessor>();
|
||||
_logger = Substitute.For<ILogger<RelayPushNotificationService>>();
|
||||
|
||||
_sut = new RelayPushNotificationService(
|
||||
_httpFactory,
|
||||
_deviceRepository.GetByIdentifierAsync(DeviceIdentifier)
|
||||
.Returns(new Device
|
||||
{
|
||||
Id = _deviceId,
|
||||
});
|
||||
|
||||
GlobalSettings.PushRelayBaseUri = "https://localhost:7777";
|
||||
GlobalSettings.Installation.Id = Guid.Parse("478c608a-99fd-452a-94f0-af271654e6ee");
|
||||
GlobalSettings.Installation.IdentityUri = "https://localhost:8888";
|
||||
}
|
||||
|
||||
protected override RelayPushNotificationService CreateService()
|
||||
{
|
||||
return new RelayPushNotificationService(
|
||||
HttpClientFactory,
|
||||
_deviceRepository,
|
||||
_globalSettings,
|
||||
_httpContextAccessor,
|
||||
_logger
|
||||
GlobalSettings,
|
||||
HttpContextAccessor,
|
||||
NullLogger<RelayPushNotificationService>.Instance,
|
||||
FakeTimeProvider
|
||||
);
|
||||
}
|
||||
|
||||
// Remove this test when we add actual tests. It only proves that
|
||||
// we've properly constructed the system under test.
|
||||
[Fact(Skip = "Needs additional work")]
|
||||
public void ServiceExists()
|
||||
protected override string ExpectedClientUrl() => "https://localhost:7777/push/send";
|
||||
|
||||
[Fact]
|
||||
public async Task SendPayloadToInstallationAsync_ThrowsNotImplementedException()
|
||||
{
|
||||
Assert.NotNull(_sut);
|
||||
var sut = CreateService();
|
||||
await Assert.ThrowsAsync<NotImplementedException>(
|
||||
async () => await sut.SendPayloadToInstallationAsync("installation_id", PushType.AuthRequest, new { }, null)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendPayloadToUserAsync_ThrowsNotImplementedException()
|
||||
{
|
||||
var sut = CreateService();
|
||||
await Assert.ThrowsAsync<NotImplementedException>(
|
||||
async () => await sut.SendPayloadToUserAsync("user_id", PushType.AuthRequest, new { }, null)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendPayloadToOrganizationAsync_ThrowsNotImplementedException()
|
||||
{
|
||||
var sut = CreateService();
|
||||
await Assert.ThrowsAsync<NotImplementedException>(
|
||||
async () => await sut.SendPayloadToOrganizationAsync("organization_id", PushType.AuthRequest, new { }, null)
|
||||
);
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncCipherCreatePayload(Cipher cipher, Guid collectionIds)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 1,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
// Currently CollectionIds are not passed along from the method signature
|
||||
// to the request body.
|
||||
["CollectionIds"] = null,
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncCipherUpdatePayload(Cipher cipher, Guid collectionIds)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 0,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
// Currently CollectionIds are not passed along from the method signature
|
||||
// to the request body.
|
||||
["CollectionIds"] = null,
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncCipherDeletePayload(Cipher cipher)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 2,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = cipher.Id,
|
||||
["UserId"] = cipher.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["CollectionIds"] = null,
|
||||
["RevisionDate"] = cipher.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncFolderCreatePayload(Folder folder)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = folder.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 7,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncFolderUpdatePayload(Folder folder)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = folder.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 8,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncFolderDeletePayload(Folder folder)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = folder.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 3,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = folder.Id,
|
||||
["UserId"] = folder.UserId,
|
||||
["RevisionDate"] = folder.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncCiphersPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 4,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncVaultPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 5,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncOrganizationsPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 17,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncOrgKeysPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 6,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushSyncSettingsPayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 10,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushLogOutPayload(Guid userId, bool excludeCurrentContext)
|
||||
{
|
||||
JsonNode? identifier = excludeCurrentContext ? DeviceIdentifier : null;
|
||||
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = identifier,
|
||||
["Type"] = 11,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushSendCreatePayload(Send send)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = send.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 12,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushSendUpdatePayload(Send send)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = send.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 13,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushSendDeletePayload(Send send)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = send.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 14,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = send.Id,
|
||||
["UserId"] = send.UserId,
|
||||
["RevisionDate"] = send.RevisionDate,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushAuthRequestPayload(AuthRequest authRequest)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = authRequest.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 15,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushAuthRequestResponsePayload(AuthRequest authRequest)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = authRequest.UserId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 16,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = authRequest.Id,
|
||||
["UserId"] = authRequest.UserId,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushNotificationResponsePayload(Notification notification, Guid? userId, Guid? organizationId)
|
||||
{
|
||||
JsonNode? installationId = notification.Global ? GlobalSettings.Installation.Id : null;
|
||||
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 20,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = notification.Global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = organizationId,
|
||||
["TaskId"] = notification.TaskId,
|
||||
["InstallationId"] = installationId,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
["ReadDate"] = null,
|
||||
["DeletedDate"] = null,
|
||||
},
|
||||
["ClientType"] = 0,
|
||||
["InstallationId"] = installationId?.DeepClone(),
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushNotificationStatusResponsePayload(Notification notification, NotificationStatus notificationStatus, Guid? userId, Guid? organizationId)
|
||||
{
|
||||
JsonNode? installationId = notification.Global ? GlobalSettings.Installation.Id : null;
|
||||
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = DeviceIdentifier,
|
||||
["Type"] = 21,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["Id"] = notification.Id,
|
||||
["Priority"] = 3,
|
||||
["Global"] = notification.Global,
|
||||
["ClientType"] = 0,
|
||||
["UserId"] = notification.UserId,
|
||||
["OrganizationId"] = notification.OrganizationId,
|
||||
["InstallationId"] = installationId,
|
||||
["TaskId"] = notification.TaskId,
|
||||
["Title"] = notification.Title,
|
||||
["Body"] = notification.Body,
|
||||
["CreationDate"] = notification.CreationDate,
|
||||
["RevisionDate"] = notification.RevisionDate,
|
||||
["ReadDate"] = notificationStatus.ReadDate,
|
||||
["DeletedDate"] = notificationStatus.DeletedDate,
|
||||
},
|
||||
["ClientType"] = 0,
|
||||
["InstallationId"] = installationId?.DeepClone(),
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushSyncOrganizationStatusResponsePayload(Organization organization)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = null,
|
||||
["OrganizationId"] = organization.Id,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 18,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["OrganizationId"] = organization.Id,
|
||||
["Enabled"] = organization.Enabled,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
protected override JsonNode GetPushSyncOrganizationCollectionManagementSettingsResponsePayload(Organization organization)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = null,
|
||||
["OrganizationId"] = organization.Id,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 19,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["OrganizationId"] = organization.Id,
|
||||
["LimitCollectionCreation"] = organization.LimitCollectionCreation,
|
||||
["LimitCollectionDeletion"] = organization.LimitCollectionDeletion,
|
||||
["LimitItemDeletion"] = organization.LimitItemDeletion,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
|
||||
protected override JsonNode GetPushPendingSecurityTasksResponsePayload(Guid userId)
|
||||
{
|
||||
return new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["OrganizationId"] = null,
|
||||
["DeviceId"] = _deviceId,
|
||||
["Identifier"] = null,
|
||||
["Type"] = 22,
|
||||
["Payload"] = new JsonObject
|
||||
{
|
||||
["UserId"] = userId,
|
||||
["Date"] = FakeTimeProvider.GetUtcNow().UtcDateTime,
|
||||
},
|
||||
["ClientType"] = null,
|
||||
["InstallationId"] = null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user