1
0
mirror of https://github.com/bitwarden/server synced 2025-12-16 08:13:33 +00:00
Files
server/src/Core/Services/Implementations/DeviceService.cs
Justin Baur e5159a3ba2 [PM-19659] Clean up Notifications code (#6244)
* Move PushType to Platform Folder

- Move the PushType next to the rest of push notification code
- Specifically exclude it from needing Platform code review
- Add tests establishing rules Platform has for usage of this enum, making it safe to have no owner

* Move NotificationHub code into Platform/Push directory

* Update NotificationHub namespace imports

* Add attribute for storing push type metadata

* Rename Push Engines to have PushEngine suffix

* Move Push Registration items to their own directory

* Push code move

* Add expected usage comment

* Add Push feature registration method

- Make method able to be called multipes times with no ill effects

* Add Push Registration service entrypoint and tests

* Use new service entrypoints

* Test changes
2025-08-26 13:30:37 -04:00

153 lines
5.3 KiB
C#

using Bit.Core.Auth.Models.Api.Request;
using Bit.Core.Auth.Utilities;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Platform.Push;
using Bit.Core.Platform.PushRegistration;
using Bit.Core.Repositories;
using Bit.Core.Settings;
namespace Bit.Core.Services;
public class DeviceService : IDeviceService
{
private readonly IDeviceRepository _deviceRepository;
private readonly IPushRegistrationService _pushRegistrationService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IGlobalSettings _globalSettings;
public DeviceService(
IDeviceRepository deviceRepository,
IPushRegistrationService pushRegistrationService,
IOrganizationUserRepository organizationUserRepository,
IGlobalSettings globalSettings)
{
_deviceRepository = deviceRepository;
_pushRegistrationService = pushRegistrationService;
_organizationUserRepository = organizationUserRepository;
_globalSettings = globalSettings;
}
public async Task SaveAsync(WebPushRegistrationData webPush, Device device, IEnumerable<string> organizationIds)
{
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(
new PushRegistrationData(webPush.Endpoint, webPush.P256dh, webPush.Auth),
device.Id.ToString(),
device.UserId.ToString(),
device.Identifier,
device.Type,
organizationIds,
_globalSettings.Installation.Id
);
}
public async Task SaveAsync(Device device)
{
await SaveAsync(new PushRegistrationData(device.PushToken), device);
}
private async Task SaveAsync(PushRegistrationData data, Device device)
{
if (device.Id == default)
{
await _deviceRepository.CreateAsync(device);
}
else
{
device.RevisionDate = DateTime.UtcNow;
await _deviceRepository.ReplaceAsync(device);
}
var organizationIdsString =
(await _organizationUserRepository.GetManyDetailsByUserAsync(device.UserId,
OrganizationUserStatusType.Confirmed))
.Select(ou => ou.OrganizationId.ToString());
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(data, device.Id.ToString(),
device.UserId.ToString(), device.Identifier, device.Type, organizationIdsString, _globalSettings.Installation.Id);
}
public async Task ClearTokenAsync(Device device)
{
await _deviceRepository.ClearPushTokenAsync(device.Id);
await _pushRegistrationService.DeleteRegistrationAsync(device.Id.ToString());
}
public async Task DeactivateAsync(Device device)
{
// already deactivated
if (!device.Active)
{
return;
}
device.Active = false;
device.RevisionDate = DateTime.UtcNow;
device.EncryptedPrivateKey = null;
device.EncryptedPublicKey = null;
device.EncryptedUserKey = null;
await _deviceRepository.UpsertAsync(device);
await _pushRegistrationService.DeleteRegistrationAsync(device.Id.ToString());
}
public async Task UpdateDevicesTrustAsync(string currentDeviceIdentifier,
Guid currentUserId,
DeviceKeysUpdateRequestModel currentDeviceUpdate,
IEnumerable<OtherDeviceKeysUpdateRequestModel> alteredDevices)
{
var existingDevices = await _deviceRepository.GetManyByUserIdAsync(currentUserId);
var currentDevice = existingDevices.FirstOrDefault(d => d.Identifier == currentDeviceIdentifier);
if (currentDevice == null)
{
throw new NotFoundException();
}
existingDevices.Remove(currentDevice);
var alterDeviceKeysDict = alteredDevices.ToDictionary(d => d.DeviceId);
if (alterDeviceKeysDict.ContainsKey(currentDevice.Id))
{
throw new BadRequestException("Current device can not be an optional rotation.");
}
currentDevice.EncryptedPublicKey = currentDeviceUpdate.EncryptedPublicKey;
currentDevice.EncryptedUserKey = currentDeviceUpdate.EncryptedUserKey;
await _deviceRepository.UpsertAsync(currentDevice);
foreach (var device in existingDevices)
{
if (!device.IsTrusted())
{
// You can't update the trust of a device that isn't trusted to begin with
// should we throw and consider this a BadRequest? If we want to consider it a invalid request
// we need to check that information before we enter this foreach, we don't want to partially complete
// this process.
continue;
}
if (alterDeviceKeysDict.TryGetValue(device.Id, out var updateRequest))
{
// An update to this device was requested
device.EncryptedPublicKey = updateRequest.EncryptedPublicKey;
device.EncryptedUserKey = updateRequest.EncryptedUserKey;
}
else
{
// No update to this device requested, just untrust it
device.EncryptedUserKey = null;
device.EncryptedPublicKey = null;
device.EncryptedPrivateKey = null;
}
await _deviceRepository.UpsertAsync(device);
}
}
}