mirror of
https://github.com/bitwarden/server.git
synced 2025-04-08 14:38:15 -05:00
notification hub push registration service
This commit is contained in:
parent
e3cba6204b
commit
c95d39f563
@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Authorization;
|
|||||||
using Bit.Core.Models.Api;
|
using Bit.Core.Models.Api;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Microsoft.AspNetCore.Identity;
|
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Api.Controllers
|
namespace Bit.Api.Controllers
|
||||||
@ -109,7 +108,13 @@ namespace Bit.Api.Controllers
|
|||||||
[HttpPost("identifier/{identifier}/clear-token")]
|
[HttpPost("identifier/{identifier}/clear-token")]
|
||||||
public async Task PutClearToken(string identifier)
|
public async Task PutClearToken(string identifier)
|
||||||
{
|
{
|
||||||
await _deviceRepository.ClearPushTokenByIdentifierAsync(identifier);
|
var device = await _deviceRepository.GetByIdentifierAsync(identifier, _userService.GetProperUserId(User).Value);
|
||||||
|
if(device == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _deviceService.ClearTokenAsync(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
@ -122,7 +127,7 @@ namespace Bit.Api.Controllers
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _deviceRepository.DeleteAsync(device);
|
await _deviceService.DeleteAsync(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,10 @@
|
|||||||
"documentDb": {
|
"documentDb": {
|
||||||
"uri": "SECRET",
|
"uri": "SECRET",
|
||||||
"key": "SECRET"
|
"key": "SECRET"
|
||||||
|
},
|
||||||
|
"notificationHub": {
|
||||||
|
"connectionString": "SECRET",
|
||||||
|
"hubName": "SECRET"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"IpRateLimitOptions": {
|
"IpRateLimitOptions": {
|
||||||
|
@ -30,6 +30,10 @@
|
|||||||
"documentDb": {
|
"documentDb": {
|
||||||
"uri": "SECRET",
|
"uri": "SECRET",
|
||||||
"key": "SECRET"
|
"key": "SECRET"
|
||||||
|
},
|
||||||
|
"notificationHub": {
|
||||||
|
"connectionString": "SECRET",
|
||||||
|
"hubName": "SECRET"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billingSettings": {
|
"billingSettings": {
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="1.1.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="1.1.3" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="1.1.3" />
|
||||||
<PackageReference Include="Dapper" Version="1.50.2" />
|
<PackageReference Include="Dapper" Version="1.50.2" />
|
||||||
|
<PackageReference Include="Microsoft.Azure.NotificationHubs" Version="1.0.8" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
OperaBrowser = 11,
|
OperaBrowser = 11,
|
||||||
EdgeBrowser = 12,
|
EdgeBrowser = 12,
|
||||||
IEBrowser = 13,
|
IEBrowser = 13,
|
||||||
UnknownBrowser = 14
|
UnknownBrowser = 14,
|
||||||
|
AndroidAmazon = 15
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
public virtual IdentityServerSettings IdentityServer { get; set; } = new IdentityServerSettings();
|
public virtual IdentityServerSettings IdentityServer { get; set; } = new IdentityServerSettings();
|
||||||
public virtual DataProtectionSettings DataProtection { get; set; } = new DataProtectionSettings();
|
public virtual DataProtectionSettings DataProtection { get; set; } = new DataProtectionSettings();
|
||||||
public virtual DocumentDbSettings DocumentDb { get; set; } = new DocumentDbSettings();
|
public virtual DocumentDbSettings DocumentDb { get; set; } = new DocumentDbSettings();
|
||||||
|
public virtual NotificationHubSettings NotificationHub { get; set; } = new NotificationHubSettings();
|
||||||
|
|
||||||
public class SqlServerSettings
|
public class SqlServerSettings
|
||||||
{
|
{
|
||||||
@ -54,5 +55,11 @@
|
|||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class NotificationHubSettings
|
||||||
|
{
|
||||||
|
public string ConnectionString { get; set; }
|
||||||
|
public string HubName { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,14 @@ using Bit.Core.Models.Table;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Core.Identity
|
namespace Bit.Core.Identity
|
||||||
{
|
{
|
||||||
public class JwtBearerSignInManager
|
public class JwtBearerSignInManager
|
||||||
{
|
{
|
||||||
private readonly IDeviceRepository _deviceRepository;
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
|
|
||||||
public JwtBearerSignInManager(
|
public JwtBearerSignInManager(
|
||||||
UserManager<User> userManager,
|
UserManager<User> userManager,
|
||||||
@ -25,7 +27,8 @@ namespace Bit.Core.Identity
|
|||||||
IOptions<JwtBearerIdentityOptions> jwtIdentityOptionsAccessor,
|
IOptions<JwtBearerIdentityOptions> jwtIdentityOptionsAccessor,
|
||||||
IOptions<JwtBearerOptions> jwtOptionsAccessor,
|
IOptions<JwtBearerOptions> jwtOptionsAccessor,
|
||||||
ILogger<JwtBearerSignInManager> logger,
|
ILogger<JwtBearerSignInManager> logger,
|
||||||
IDeviceRepository deviceRepository)
|
IDeviceRepository deviceRepository,
|
||||||
|
IDeviceService deviceService)
|
||||||
{
|
{
|
||||||
UserManager = userManager;
|
UserManager = userManager;
|
||||||
Context = contextAccessor.HttpContext;
|
Context = contextAccessor.HttpContext;
|
||||||
@ -34,6 +37,7 @@ namespace Bit.Core.Identity
|
|||||||
JwtIdentityOptions = jwtIdentityOptionsAccessor?.Value ?? new JwtBearerIdentityOptions();
|
JwtIdentityOptions = jwtIdentityOptionsAccessor?.Value ?? new JwtBearerIdentityOptions();
|
||||||
JwtBearerOptions = jwtOptionsAccessor?.Value ?? new JwtBearerOptions();
|
JwtBearerOptions = jwtOptionsAccessor?.Value ?? new JwtBearerOptions();
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
|
_deviceService = deviceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal UserManager<User> UserManager { get; set; }
|
internal UserManager<User> UserManager { get; set; }
|
||||||
@ -75,7 +79,7 @@ namespace Bit.Core.Identity
|
|||||||
if(existingDevice == null)
|
if(existingDevice == null)
|
||||||
{
|
{
|
||||||
device.UserId = user.Id;
|
device.UserId = user.Id;
|
||||||
await _deviceRepository.CreateAsync(device);
|
await _deviceService.SaveAsync(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +121,7 @@ namespace Bit.Core.Identity
|
|||||||
if(existingDevice == null)
|
if(existingDevice == null)
|
||||||
{
|
{
|
||||||
device.UserId = user.Id;
|
device.UserId = user.Id;
|
||||||
await _deviceRepository.CreateAsync(device);
|
await _deviceService.SaveAsync(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer
|
namespace Bit.Core.IdentityServer
|
||||||
{
|
{
|
||||||
@ -26,18 +27,21 @@ namespace Bit.Core.IdentityServer
|
|||||||
private JwtBearerOptions _jwtBearerOptions;
|
private JwtBearerOptions _jwtBearerOptions;
|
||||||
private JwtBearerIdentityOptions _jwtBearerIdentityOptions;
|
private JwtBearerIdentityOptions _jwtBearerIdentityOptions;
|
||||||
private readonly IDeviceRepository _deviceRepository;
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
|
private readonly IDeviceService _deviceService;
|
||||||
|
|
||||||
public ResourceOwnerPasswordValidator(
|
public ResourceOwnerPasswordValidator(
|
||||||
UserManager<User> userManager,
|
UserManager<User> userManager,
|
||||||
IOptions<IdentityOptions> identityOptionsAccessor,
|
IOptions<IdentityOptions> identityOptionsAccessor,
|
||||||
IOptions<JwtBearerIdentityOptions> jwtIdentityOptionsAccessor,
|
IOptions<JwtBearerIdentityOptions> jwtIdentityOptionsAccessor,
|
||||||
IDeviceRepository deviceRepository)
|
IDeviceRepository deviceRepository,
|
||||||
|
IDeviceService deviceService)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_identityOptions = identityOptionsAccessor?.Value ?? new IdentityOptions();
|
_identityOptions = identityOptionsAccessor?.Value ?? new IdentityOptions();
|
||||||
_jwtBearerIdentityOptions = jwtIdentityOptionsAccessor?.Value;
|
_jwtBearerIdentityOptions = jwtIdentityOptionsAccessor?.Value;
|
||||||
_jwtBearerOptions = Core.Identity.JwtBearerAppBuilderExtensions.BuildJwtBearerOptions(_jwtBearerIdentityOptions);
|
_jwtBearerOptions = Identity.JwtBearerAppBuilderExtensions.BuildJwtBearerOptions(_jwtBearerIdentityOptions);
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
|
_deviceService = deviceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
|
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
|
||||||
@ -222,7 +226,7 @@ namespace Bit.Core.IdentityServer
|
|||||||
if(existingDevice == null)
|
if(existingDevice == null)
|
||||||
{
|
{
|
||||||
device.UserId = user.Id;
|
device.UserId = user.Id;
|
||||||
await _deviceRepository.CreateAsync(device);
|
await _deviceService.SaveAsync(device);
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,5 +6,7 @@ namespace Bit.Core.Services
|
|||||||
public interface IDeviceService
|
public interface IDeviceService
|
||||||
{
|
{
|
||||||
Task SaveAsync(Device device);
|
Task SaveAsync(Device device);
|
||||||
|
Task ClearTokenAsync(Device device);
|
||||||
|
Task DeleteAsync(Device device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/Core/Services/IPushRegistrationService.cs
Normal file
12
src/Core/Services/IPushRegistrationService.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public interface IPushRegistrationService
|
||||||
|
{
|
||||||
|
Task CreateOrUpdateRegistrationAsync(Device device);
|
||||||
|
Task DeleteRegistrationAsync(Guid deviceId);
|
||||||
|
}
|
||||||
|
}
|
@ -8,11 +8,14 @@ namespace Bit.Core.Services
|
|||||||
public class DeviceService : IDeviceService
|
public class DeviceService : IDeviceService
|
||||||
{
|
{
|
||||||
private readonly IDeviceRepository _deviceRepository;
|
private readonly IDeviceRepository _deviceRepository;
|
||||||
|
private readonly IPushRegistrationService _pushRegistrationService;
|
||||||
|
|
||||||
public DeviceService(
|
public DeviceService(
|
||||||
IDeviceRepository deviceRepository)
|
IDeviceRepository deviceRepository,
|
||||||
|
IPushRegistrationService pushRegistrationService)
|
||||||
{
|
{
|
||||||
_deviceRepository = deviceRepository;
|
_deviceRepository = deviceRepository;
|
||||||
|
_pushRegistrationService = pushRegistrationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(Device device)
|
public async Task SaveAsync(Device device)
|
||||||
@ -26,6 +29,20 @@ namespace Bit.Core.Services
|
|||||||
device.RevisionDate = DateTime.UtcNow;
|
device.RevisionDate = DateTime.UtcNow;
|
||||||
await _deviceRepository.ReplaceAsync(device);
|
await _deviceRepository.ReplaceAsync(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _pushRegistrationService.CreateOrUpdateRegistrationAsync(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ClearTokenAsync(Device device)
|
||||||
|
{
|
||||||
|
await _deviceRepository.ClearPushTokenByIdentifierAsync(device.Identifier);
|
||||||
|
await _pushRegistrationService.DeleteRegistrationAsync(device.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteAsync(Device device)
|
||||||
|
{
|
||||||
|
await _deviceRepository.DeleteAsync(device);
|
||||||
|
await _pushRegistrationService.DeleteRegistrationAsync(device.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Azure.NotificationHubs;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public class NotificationHubPushRegistrationService : IPushRegistrationService
|
||||||
|
{
|
||||||
|
private readonly NotificationHubClient _client;
|
||||||
|
|
||||||
|
public NotificationHubPushRegistrationService(GlobalSettings globalSettings)
|
||||||
|
{
|
||||||
|
_client = NotificationHubClient.CreateClientFromConnectionString(globalSettings.NotificationHub.ConnectionString,
|
||||||
|
globalSettings.NotificationHub.HubName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateOrUpdateRegistrationAsync(Device device)
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(device.PushToken))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var installation = new Installation
|
||||||
|
{
|
||||||
|
InstallationId = device.Id.ToString(),
|
||||||
|
PushChannel = device.PushToken
|
||||||
|
};
|
||||||
|
|
||||||
|
installation.Tags = new List<string>
|
||||||
|
{
|
||||||
|
"userId:" + device.UserId.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(device.Identifier))
|
||||||
|
{
|
||||||
|
installation.Tags.Add("identifier:" + device.Identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(device.Type)
|
||||||
|
{
|
||||||
|
case Enums.DeviceType.Android:
|
||||||
|
installation.Platform = NotificationPlatform.Gcm;
|
||||||
|
break;
|
||||||
|
case Enums.DeviceType.iOS:
|
||||||
|
installation.Platform = NotificationPlatform.Apns;
|
||||||
|
break;
|
||||||
|
case Enums.DeviceType.AndroidAmazon:
|
||||||
|
installation.Platform = NotificationPlatform.Adm;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _client.CreateOrUpdateInstallationAsync(installation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteRegistrationAsync(Guid deviceId)
|
||||||
|
{
|
||||||
|
await _client.DeleteInstallationAsync(deviceId.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public class NoopPushRegistrationService : IPushRegistrationService
|
||||||
|
{
|
||||||
|
public Task CreateOrUpdateRegistrationAsync(Device device)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task DeleteRegistrationAsync(Guid deviceId)
|
||||||
|
{
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,7 @@ namespace Bit.Core.Utilities
|
|||||||
services.AddSingleton<IMailService, SendGridMailService>();
|
services.AddSingleton<IMailService, SendGridMailService>();
|
||||||
services.AddSingleton<IPushService, PushSharpPushService>();
|
services.AddSingleton<IPushService, PushSharpPushService>();
|
||||||
services.AddSingleton<IBlockIpService, AzureQueueBlockIpService>();
|
services.AddSingleton<IBlockIpService, AzureQueueBlockIpService>();
|
||||||
|
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddNoopServices(this IServiceCollection services)
|
public static void AddNoopServices(this IServiceCollection services)
|
||||||
@ -59,6 +60,7 @@ namespace Bit.Core.Utilities
|
|||||||
services.AddSingleton<IMailService, NoopMailService>();
|
services.AddSingleton<IMailService, NoopMailService>();
|
||||||
services.AddSingleton<IPushService, NoopPushService>();
|
services.AddSingleton<IPushService, NoopPushService>();
|
||||||
services.AddSingleton<IBlockIpService, NoopBlockIpService>();
|
services.AddSingleton<IBlockIpService, NoopBlockIpService>();
|
||||||
|
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IdentityBuilder AddCustomIdentityServices(
|
public static IdentityBuilder AddCustomIdentityServices(
|
||||||
|
@ -30,6 +30,10 @@
|
|||||||
"documentDb": {
|
"documentDb": {
|
||||||
"uri": "SECRET",
|
"uri": "SECRET",
|
||||||
"key": "SECRET"
|
"key": "SECRET"
|
||||||
|
},
|
||||||
|
"notificationHub": {
|
||||||
|
"connectionString": "SECRET",
|
||||||
|
"hubName": "SECRET"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user