Browse Source

[SG-167] Implement Passwordless Authentication via Notifications (#2276)

* [SG-549] Commit Initial AuthRequest Repository (#2174)

* Model Passwordless

* Scaffold database for Passwordless

* Implement SQL Repository

* [SG-167] Base Passwordless API (#2185)

* Implement Passwordless notifications

* Implement Controller

* Add documentation to BaseRequestValidator

* Register AuthRequestRepo

* Remove ExpirationDate from the AuthRequest table

* [SG-407] Create job to delete expired requests (#2187)

* chore: init

* remove exp date

* fix: log name

* [SG-167] Added fingerprint phrase to response model. (#2233)

* Remove FailedLoginAttempt logic

* Block unknown devices

* Add EF Support for passwordless

* Got SignalR working for responses

* Added delete job method to EF repo

* Implement a GetMany API endpoint for AuthRequests

* Ran dotnet format

* Fix a merge issues

* Redated migration scripts

* tried sorting sqlproj

* Remove FailedLoginAttempts from SQL

* Groom Postgres script

* Remove extra commas from migration script

* Correct isSpent()

* [SG-167] Adde identity validation for passwordless requests. Registered IAuthRepository.

* [SG-167] Added origin of the request to response model

* Use display name for device identifier in response

* Add datetime conversions back to postgres migration script

* [SG-655] Add anonymous endpoint for checking if a device & user combo match

* [review] Consolidate error conditions

Co-authored-by: Brandon Maharaj <107377945+BrandonM-Bitwarden@users.noreply.github.com>
Co-authored-by: André Filipe da Silva Bispo <andrefsbispo@hotmail.com>
Co-authored-by: André Bispo <abispo@bitwarden.com>
pull/2302/head
Addison Beck 3 years ago committed by GitHub
parent
commit
02bea3c48d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      src/Admin/Jobs/DeleteAuthRequestsJob.cs
  2. 9
      src/Admin/Jobs/JobsHostedService.cs
  3. 145
      src/Api/Controllers/AuthRequestsController.cs
  4. 24
      src/Api/Controllers/DevicesController.cs
  5. 32
      src/Api/Models/Request/AuthRequestRequestModel.cs
  6. 43
      src/Api/Models/Response/AuthRequestResponseModel.cs
  7. 41
      src/Core/Entities/AuthRequest.cs
  8. 7
      src/Core/Enums/AuthRequestType.cs
  9. 3
      src/Core/Enums/PushType.cs
  10. 28
      src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs
  11. 6
      src/Core/Models/PushNotification.cs
  12. 9
      src/Core/Repositories/IAuthRequestRepository.cs
  13. 2
      src/Core/Services/IPushNotificationService.cs
  14. 21
      src/Core/Services/Implementations/AzureQueuePushNotificationService.cs
  15. 12
      src/Core/Services/Implementations/MultiServicePushNotificationService.cs
  16. 21
      src/Core/Services/Implementations/NotificationHubPushNotificationService.cs
  17. 21
      src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs
  18. 21
      src/Core/Services/Implementations/RelayPushNotificationService.cs
  19. 10
      src/Core/Services/NoopImplementations/NoopPushNotificationService.cs
  20. 6
      src/Core/Settings/GlobalSettings.cs
  21. 1
      src/Core/Settings/IGlobalSettings.cs
  22. 6
      src/Core/Settings/IPasswordlessAuthSettings.cs
  23. 1
      src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs
  24. 43
      src/Infrastructure.Dapper/Repositories/AuthRequestRepository.cs
  25. 1
      src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs
  26. 17
      src/Infrastructure.EntityFramework/Models/AuthRequest.cs
  27. 35
      src/Infrastructure.EntityFramework/Repositories/AuthRequestRepository.cs
  28. 4
      src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs
  29. 19
      src/Notifications/AnonymousNotificationsHub.cs
  30. 5
      src/Notifications/AzureQueueHostedService.cs
  31. 2
      src/Notifications/Controllers/SendController.cs
  32. 22
      src/Notifications/HubHelpers.cs
  33. 7
      src/Notifications/INotificationHub.cs
  34. 7
      src/Notifications/Startup.cs
  35. 8
      src/Sql/Sql.sqlproj
  36. 57
      src/Sql/dbo/Stored Procedures/AuthRequest_Create.sql
  37. 12
      src/Sql/dbo/Stored Procedures/AuthRequest_DeleteById.sql
  38. 6
      src/Sql/dbo/Stored Procedures/AuthRequest_DeleteIfExpired.sql
  39. 13
      src/Sql/dbo/Stored Procedures/AuthRequest_ReadById.sql
  40. 13
      src/Sql/dbo/Stored Procedures/AuthRequest_ReadByUserId.sql
  41. 22
      src/Sql/dbo/Stored Procedures/AuthRequest_Update.sql
  42. 23
      src/Sql/dbo/Tables/AuthRequest.sql
  43. 6
      src/Sql/dbo/Views/AuthRequestView.sql
  44. 61
      test/Infrastructure.EFIntegration.Test/AutoFixture/AuthRequestFixtures.cs
  45. 1
      test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs
  46. 50
      test/Infrastructure.EFIntegration.Test/Repositories/AuthRequestRepositoryTests.cs
  47. 23
      test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/AuthRequestCompare.cs
  48. 218
      util/Migrator/DbScripts/2022-09-12_00_AuthRequestInit.sql
  49. 1672
      util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.Designer.cs
  50. 71
      util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.cs
  51. 76
      util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs
  52. 31
      util/MySqlMigrations/Scripts/2022-09-12_00_PasswordlessAuth.sql
  53. 1680
      util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.Designer.cs
  54. 905
      util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.cs
  55. 175
      util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs
  56. 133
      util/PostgresMigrations/Scripts/2022-09-12_00_PasswordlessAuthRequests.psql

27
src/Admin/Jobs/DeleteAuthRequestsJob.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
using Bit.Core;
using Bit.Core.Jobs;
using Bit.Core.Repositories;
using Quartz;
namespace Bit.Admin.Jobs;
public class DeleteAuthRequestsJob : BaseJob
{
private readonly IAuthRequestRepository _authRepo;
public DeleteAuthRequestsJob(
IAuthRequestRepository authrepo,
ILogger<DeleteAuthRequestsJob> logger)
: base(logger)
{
_authRepo = authrepo;
}
protected async override Task ExecuteJobAsync(IJobExecutionContext context)
{
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteAuthRequestsJob: Start");
var count = await _authRepo.DeleteExpiredAsync();
_logger.LogInformation(Constants.BypassFiltersEventId, $"{count} records deleted from AuthRequests.");
_logger.LogInformation(Constants.BypassFiltersEventId, "Execute job task: DeleteAuthRequestsJob: End");
}
}

9
src/Admin/Jobs/JobsHostedService.cs

@ -59,6 +59,11 @@ public class JobsHostedService : BaseJobsHostedService @@ -59,6 +59,11 @@ public class JobsHostedService : BaseJobsHostedService
.StartNow()
.WithCronSchedule("0 0 0 * * ?")
.Build();
var everyFifteenMinutesTrigger = TriggerBuilder.Create()
.WithIdentity("everyFifteenMinutesTrigger")
.StartNow()
.WithCronSchedule("0 */15 * ? * *")
.Build();
var jobs = new List<Tuple<Type, ITrigger>>
{
@ -67,7 +72,8 @@ public class JobsHostedService : BaseJobsHostedService @@ -67,7 +72,8 @@ public class JobsHostedService : BaseJobsHostedService
new Tuple<Type, ITrigger>(typeof(DatabaseUpdateStatisticsJob), everySaturdayAtMidnightTrigger),
new Tuple<Type, ITrigger>(typeof(DatabaseRebuildlIndexesJob), everySundayAtMidnightTrigger),
new Tuple<Type, ITrigger>(typeof(DeleteCiphersJob), everyDayAtMidnightUtc),
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger)
new Tuple<Type, ITrigger>(typeof(DatabaseExpiredSponsorshipsJob), everyMondayAtMidnightTrigger),
new Tuple<Type, ITrigger>(typeof(DeleteAuthRequestsJob), everyFifteenMinutesTrigger),
};
if (!_globalSettings.SelfHosted)
@ -91,5 +97,6 @@ public class JobsHostedService : BaseJobsHostedService @@ -91,5 +97,6 @@ public class JobsHostedService : BaseJobsHostedService
services.AddTransient<DatabaseExpiredSponsorshipsJob>();
services.AddTransient<DeleteSendsJob>();
services.AddTransient<DeleteCiphersJob>();
services.AddTransient<DeleteAuthRequestsJob>();
}
}

145
src/Api/Controllers/AuthRequestsController.cs

@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
using Bit.Api.Models.Request;
using Bit.Api.Models.Response;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.Controllers;
[Route("auth-requests")]
[Authorize("Application")]
public class AuthRequestsController : Controller
{
private readonly IUserRepository _userRepository;
private readonly IDeviceRepository _deviceRepository;
private readonly IUserService _userService;
private readonly IAuthRequestRepository _authRequestRepository;
private readonly ICurrentContext _currentContext;
private readonly IPushNotificationService _pushNotificationService;
private readonly IGlobalSettings _globalSettings;
public AuthRequestsController(
IUserRepository userRepository,
IDeviceRepository deviceRepository,
IUserService userService,
IAuthRequestRepository authRequestRepository,
ICurrentContext currentContext,
IPushNotificationService pushNotificationService,
IGlobalSettings globalSettings)
{
_userRepository = userRepository;
_deviceRepository = deviceRepository;
_userService = userService;
_authRequestRepository = authRequestRepository;
_currentContext = currentContext;
_pushNotificationService = pushNotificationService;
_globalSettings = globalSettings;
}
[HttpGet("")]
public async Task<ListResponseModel<AuthRequestResponseModel>> Get()
{
var userId = _userService.GetProperUserId(User).Value;
var authRequests = await _authRequestRepository.GetManyByUserIdAsync(userId);
var responses = authRequests.Select(a => new AuthRequestResponseModel(a, _globalSettings.SelfHosted)).ToList();
return new ListResponseModel<AuthRequestResponseModel>(responses);
}
[HttpGet("{id}")]
public async Task<AuthRequestResponseModel> Get(string id)
{
var userId = _userService.GetProperUserId(User).Value;
var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id));
if (authRequest == null || authRequest.UserId != userId)
{
throw new NotFoundException();
}
return new AuthRequestResponseModel(authRequest, _globalSettings.SelfHosted);
}
[HttpGet("{id}/response")]
[AllowAnonymous]
public async Task<AuthRequestResponseModel> GetResponse(string id, [FromQuery] string code)
{
var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id));
if (authRequest == null || code != authRequest.AccessCode || authRequest.GetExpirationDate() < DateTime.UtcNow)
{
throw new NotFoundException();
}
return new AuthRequestResponseModel(authRequest, _globalSettings.SelfHosted);
}
[HttpPost("")]
[AllowAnonymous]
public async Task<AuthRequestResponseModel> Post([FromBody] AuthRequestCreateRequestModel model)
{
var user = await _userRepository.GetByEmailAsync(model.Email);
if (user == null)
{
throw new NotFoundException();
}
if (!_currentContext.DeviceType.HasValue)
{
throw new BadRequestException("Device type not provided.");
}
if (!_globalSettings.PasswordlessAuth.KnownDevicesOnly)
{
var d = await _deviceRepository.GetByIdentifierAsync(_currentContext.DeviceIdentifier);
if (d == null || d.UserId != user.Id)
{
throw new NotFoundException();
}
}
var authRequest = new AuthRequest
{
RequestDeviceIdentifier = model.DeviceIdentifier,
RequestDeviceType = _currentContext.DeviceType.Value,
RequestIpAddress = _currentContext.IpAddress,
AccessCode = model.AccessCode,
PublicKey = model.PublicKey,
UserId = user.Id,
Type = model.Type.Value,
RequestFingerprint = model.FingerprintPhrase
};
authRequest = await _authRequestRepository.CreateAsync(authRequest);
await _pushNotificationService.PushAuthRequestAsync(authRequest);
return new AuthRequestResponseModel(authRequest, _globalSettings.SelfHosted);
}
[HttpPut("{id}")]
public async Task<AuthRequestResponseModel> Put(string id, [FromBody] AuthRequestUpdateRequestModel model)
{
var userId = _userService.GetProperUserId(User).Value;
var authRequest = await _authRequestRepository.GetByIdAsync(new Guid(id));
if (authRequest == null || authRequest.UserId != userId || authRequest.GetExpirationDate() < DateTime.UtcNow)
{
throw new NotFoundException();
}
var device = await _deviceRepository.GetByIdentifierAsync(model.DeviceIdentifier);
if (device == null)
{
throw new BadRequestException("Invalid device.");
}
if (model.RequestApproved)
{
authRequest.Key = model.Key;
authRequest.MasterPasswordHash = model.MasterPasswordHash;
authRequest.ResponseDeviceId = device.Id;
authRequest.ResponseDate = DateTime.UtcNow;
await _authRequestRepository.ReplaceAsync(authRequest);
}
await _pushNotificationService.PushAuthRequestResponseAsync(authRequest);
return new AuthRequestResponseModel(authRequest, _globalSettings.SelfHosted);
}
}

24
src/Api/Controllers/DevicesController.cs

@ -16,15 +16,18 @@ public class DevicesController : Controller @@ -16,15 +16,18 @@ public class DevicesController : Controller
private readonly IDeviceRepository _deviceRepository;
private readonly IDeviceService _deviceService;
private readonly IUserService _userService;
private readonly IUserRepository _userRepository;
public DevicesController(
IDeviceRepository deviceRepository,
IDeviceService deviceService,
IUserService userService)
IUserService userService,
IUserRepository userRepository)
{
_deviceRepository = deviceRepository;
_deviceService = deviceService;
_userService = userService;
_userRepository = userRepository;
}
[HttpGet("{id}")]
@ -126,4 +129,23 @@ public class DevicesController : Controller @@ -126,4 +129,23 @@ public class DevicesController : Controller
await _deviceService.DeleteAsync(device);
}
[AllowAnonymous]
[HttpGet("knowndevice/{email}/{identifier}")]
public async Task<bool> GetByIdentifier(string email, string identifier)
{
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(identifier))
{
throw new BadRequestException("Please provide an email and device identifier");
}
var user = await _userRepository.GetByEmailAsync(email);
if (user == null)
{
return false;
}
var device = await _deviceRepository.GetByIdentifierAsync(identifier, user.Id);
return device != null;
}
}

32
src/Api/Models/Request/AuthRequestRequestModel.cs

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using Newtonsoft.Json;
namespace Bit.Api.Models.Request;
public class AuthRequestCreateRequestModel
{
[Required]
public string Email { get; set; }
[Required]
public string PublicKey { get; set; }
[Required]
public string DeviceIdentifier { get; set; }
[Required]
[StringLength(25)]
public string AccessCode { get; set; }
[Required]
public AuthRequestType? Type { get; set; }
[Required]
public string FingerprintPhrase { get; set; }
}
public class AuthRequestUpdateRequestModel
{
public string Key { get; set; }
public string MasterPasswordHash { get; set; }
[Required]
public string DeviceIdentifier { get; set; }
[Required]
public bool RequestApproved { get; set; }
}

43
src/Api/Models/Response/AuthRequestResponseModel.cs

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Models.Api;
namespace Bit.Api.Models.Response;
public class AuthRequestResponseModel : ResponseModel
{
public AuthRequestResponseModel(AuthRequest authRequest, bool isSelfHosted, string obj = "auth-request")
: base(obj)
{
if (authRequest == null)
{
throw new ArgumentNullException(nameof(authRequest));
}
Id = authRequest.Id.ToString();
PublicKey = authRequest.PublicKey;
RequestDeviceType = authRequest.RequestDeviceType.GetType().GetMember(authRequest.RequestDeviceType.ToString())
.FirstOrDefault()?.GetCustomAttribute<DisplayAttribute>()?.GetName();
RequestIpAddress = authRequest.RequestIpAddress;
RequestFingerprint = authRequest.RequestFingerprint;
Key = authRequest.Key;
MasterPasswordHash = authRequest.MasterPasswordHash;
CreationDate = authRequest.CreationDate;
RequestApproved = !string.IsNullOrWhiteSpace(Key) &&
(authRequest.Type == AuthRequestType.Unlock || !string.IsNullOrWhiteSpace(MasterPasswordHash));
Origin = Origin = isSelfHosted ? "SelfHosted" : "bitwarden.com";
}
public string Id { get; set; }
public string PublicKey { get; set; }
public string RequestDeviceType { get; set; }
public string RequestIpAddress { get; set; }
public string RequestFingerprint { get; set; }
public string Key { get; set; }
public string MasterPasswordHash { get; set; }
public DateTime CreationDate { get; set; }
public bool RequestApproved { get; set; }
public string Origin { get; set; }
}

41
src/Core/Entities/AuthRequest.cs

@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities;
namespace Bit.Core.Entities;
public class AuthRequest : ITableObject<Guid>
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public Enums.AuthRequestType Type { get; set; }
[MaxLength(50)]
public string RequestDeviceIdentifier { get; set; }
public Enums.DeviceType RequestDeviceType { get; set; }
[MaxLength(50)]
public string RequestIpAddress { get; set; }
public string RequestFingerprint { get; set; }
public Guid? ResponseDeviceId { get; set; }
[MaxLength(25)]
public string AccessCode { get; set; }
public string PublicKey { get; set; }
public string Key { get; set; }
public string MasterPasswordHash { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
public DateTime? ResponseDate { get; set; }
public DateTime? AuthenticationDate { get; set; }
public void SetNewId()
{
Id = CoreHelpers.GenerateComb();
}
public bool IsSpent()
{
return ResponseDate.HasValue || AuthenticationDate.HasValue || GetExpirationDate() < DateTime.UtcNow;
}
public DateTime GetExpirationDate()
{
return CreationDate.AddMinutes(15);
}
}

7
src/Core/Enums/AuthRequestType.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace Bit.Core.Enums;
public enum AuthRequestType : byte
{
AuthenticateAndUnlock = 0,
Unlock = 1
}

3
src/Core/Enums/PushType.cs

@ -20,4 +20,7 @@ public enum PushType : byte @@ -20,4 +20,7 @@ public enum PushType : byte
SyncSendCreate = 12,
SyncSendUpdate = 13,
SyncSendDelete = 14,
AuthRequest = 15,
AuthRequestResponse = 16,
}

28
src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs

@ -20,6 +20,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner @@ -20,6 +20,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly ICaptchaValidationService _captchaValidationService;
private readonly IAuthRequestRepository _authRequestRepository;
public ResourceOwnerPasswordValidator(
UserManager<User> userManager,
IDeviceRepository deviceRepository,
@ -36,6 +37,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner @@ -36,6 +37,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
GlobalSettings globalSettings,
IPolicyRepository policyRepository,
ICaptchaValidationService captchaValidationService,
IAuthRequestRepository authRequestRepository,
IUserRepository userRepository)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
@ -46,6 +48,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner @@ -46,6 +48,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
_userService = userService;
_currentContext = currentContext;
_captchaValidationService = captchaValidationService;
_authRequestRepository = authRequestRepository;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
@ -104,12 +107,31 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner @@ -104,12 +107,31 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
return false;
}
if (!await _userService.CheckPasswordAsync(validatorContext.User, context.Password))
var authRequestId = context.Request.Raw["AuthRequest"]?.ToString()?.ToLowerInvariant();
if (!string.IsNullOrWhiteSpace(authRequestId) && Guid.TryParse(authRequestId, out var authRequestGuid))
{
var authRequest = await _authRequestRepository.GetByIdAsync(authRequestGuid);
if (authRequest != null)
{
var requestAge = DateTime.UtcNow - authRequest.CreationDate;
if (requestAge < TimeSpan.FromHours(1) && !authRequest.AuthenticationDate.HasValue &&
CoreHelpers.FixedTimeEquals(authRequest.AccessCode, context.Password))
{
authRequest.AuthenticationDate = DateTime.UtcNow;
await _authRequestRepository.ReplaceAsync(authRequest);
return true;
}
}
return false;
}
return true;
else
{
if (!await _userService.CheckPasswordAsync(validatorContext.User, context.Password))
{
return false;
}
return true;
}
}
protected override Task SetSuccessResult(ResourceOwnerPasswordValidationContext context, User user,

6
src/Core/Models/PushNotification.cs

@ -44,3 +44,9 @@ public class SyncSendPushNotification @@ -44,3 +44,9 @@ public class SyncSendPushNotification
public Guid UserId { get; set; }
public DateTime RevisionDate { get; set; }
}
public class AuthRequestPushNotification
{
public Guid UserId { get; set; }
public Guid Id { get; set; }
}

9
src/Core/Repositories/IAuthRequestRepository.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
using Bit.Core.Entities;
namespace Bit.Core.Repositories;
public interface IAuthRequestRepository : IRepository<AuthRequest, Guid>
{
Task<int> DeleteExpiredAsync();
Task<ICollection<AuthRequest>> GetManyByUserIdAsync(Guid userId);
}

2
src/Core/Services/IPushNotificationService.cs

@ -19,6 +19,8 @@ public interface IPushNotificationService @@ -19,6 +19,8 @@ public interface IPushNotificationService
Task PushSyncSendCreateAsync(Send send);
Task PushSyncSendUpdateAsync(Send send);
Task PushSyncSendDeleteAsync(Send send);
Task PushAuthRequestAsync(AuthRequest authRequest);
Task PushAuthRequestResponseAsync(AuthRequest authRequest);
Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier, string deviceId = null);
Task SendPayloadToOrganizationAsync(string orgId, PushType type, object payload, string identifier,
string deviceId = null);

21
src/Core/Services/Implementations/AzureQueuePushNotificationService.cs

@ -130,6 +130,27 @@ public class AzureQueuePushNotificationService : IPushNotificationService @@ -130,6 +130,27 @@ public class AzureQueuePushNotificationService : IPushNotificationService
await SendMessageAsync(type, message, false);
}
public async Task PushAuthRequestAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequest);
}
public async Task PushAuthRequestResponseAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse);
}
private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type)
{
var message = new AuthRequestPushNotification
{
Id = authRequest.Id,
UserId = authRequest.UserId
};
await SendMessageAsync(type, message, true);
}
public async Task PushSyncSendCreateAsync(Send send)
{
await PushSendAsync(send, PushType.SyncSendCreate);

12
src/Core/Services/Implementations/MultiServicePushNotificationService.cs

@ -133,6 +133,18 @@ public class MultiServicePushNotificationService : IPushNotificationService @@ -133,6 +133,18 @@ public class MultiServicePushNotificationService : IPushNotificationService
return Task.FromResult(0);
}
public Task PushAuthRequestAsync(AuthRequest authRequest)
{
PushToServices((s) => s.PushAuthRequestAsync(authRequest));
return Task.FromResult(0);
}
public Task PushAuthRequestResponseAsync(AuthRequest authRequest)
{
PushToServices((s) => s.PushAuthRequestResponseAsync(authRequest));
return Task.FromResult(0);
}
public Task PushSyncSendDeleteAsync(Send send)
{
PushToServices((s) => s.PushSyncSendDeleteAsync(send));

21
src/Core/Services/Implementations/NotificationHubPushNotificationService.cs

@ -167,6 +167,27 @@ public class NotificationHubPushNotificationService : IPushNotificationService @@ -167,6 +167,27 @@ public class NotificationHubPushNotificationService : IPushNotificationService
}
}
public async Task PushAuthRequestAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequest);
}
public async Task PushAuthRequestResponseAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse);
}
private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type)
{
var message = new AuthRequestPushNotification
{
Id = authRequest.Id,
UserId = authRequest.UserId
};
await SendPayloadToUserAsync(authRequest.UserId, type, message, true);
}
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext)
{
await SendPayloadToUserAsync(userId.ToString(), type, payload, GetContextIdentifier(excludeCurrentContext));

21
src/Core/Services/Implementations/NotificationsApiPushNotificationService.cs

@ -137,6 +137,27 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService @@ -137,6 +137,27 @@ public class NotificationsApiPushNotificationService : BaseIdentityClientService
await SendMessageAsync(type, message, false);
}
public async Task PushAuthRequestAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequest);
}
public async Task PushAuthRequestResponseAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse);
}
private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type)
{
var message = new AuthRequestPushNotification
{
Id = authRequest.Id,
UserId = authRequest.UserId
};
await SendMessageAsync(type, message, true);
}
public async Task PushSyncSendCreateAsync(Send send)
{
await PushSendAsync(send, PushType.SyncSendCreate);

21
src/Core/Services/Implementations/RelayPushNotificationService.cs

@ -167,6 +167,27 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti @@ -167,6 +167,27 @@ public class RelayPushNotificationService : BaseIdentityClientService, IPushNoti
}
}
public async Task PushAuthRequestAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequest);
}
public async Task PushAuthRequestResponseAsync(AuthRequest authRequest)
{
await PushAuthRequestAsync(authRequest, PushType.AuthRequestResponse);
}
private async Task PushAuthRequestAsync(AuthRequest authRequest, PushType type)
{
var message = new AuthRequestPushNotification
{
Id = authRequest.Id,
UserId = authRequest.UserId
};
await SendPayloadToUserAsync(authRequest.UserId, type, message, true);
}
private async Task SendPayloadToUserAsync(Guid userId, PushType type, object payload, bool excludeCurrentContext)
{
var request = new PushSendRequestModel

10
src/Core/Services/NoopImplementations/NoopPushNotificationService.cs

@ -81,6 +81,16 @@ public class NoopPushNotificationService : IPushNotificationService @@ -81,6 +81,16 @@ public class NoopPushNotificationService : IPushNotificationService
return Task.FromResult(0);
}
public Task PushAuthRequestAsync(AuthRequest authRequest)
{
return Task.FromResult(0);
}
public Task PushAuthRequestResponseAsync(AuthRequest authRequest)
{
return Task.FromResult(0);
}
public Task SendPayloadToUserAsync(string userId, PushType type, object payload, string identifier,
string deviceId = null)
{

6
src/Core/Settings/GlobalSettings.cs

@ -71,6 +71,7 @@ public class GlobalSettings : IGlobalSettings @@ -71,6 +71,7 @@ public class GlobalSettings : IGlobalSettings
public virtual ITwoFactorAuthSettings TwoFactorAuth { get; set; } = new TwoFactorAuthSettings();
public virtual DistributedIpRateLimitingSettings DistributedIpRateLimiting { get; set; } =
new DistributedIpRateLimitingSettings();
public virtual IPasswordlessAuthSettings PasswordlessAuth { get; set; } = new PasswordlessAuthSettings();
public string BuildExternalUri(string explicitValue, string name)
{
@ -453,6 +454,7 @@ public class GlobalSettings : IGlobalSettings @@ -453,6 +454,7 @@ public class GlobalSettings : IGlobalSettings
get => string.IsNullOrWhiteSpace(_apiUri) ? "https://api.bitwarden.com" : _apiUri;
set => _apiUri = value;
}
}
public class AmazonSettings
@ -519,4 +521,8 @@ public class GlobalSettings : IGlobalSettings @@ -519,4 +521,8 @@ public class GlobalSettings : IGlobalSettings
public int SlidingWindowSeconds { get; set; } = 120;
}
public class PasswordlessAuthSettings : IPasswordlessAuthSettings
{
public bool KnownDevicesOnly { get; set; } = true;
}
}

1
src/Core/Settings/IGlobalSettings.cs

@ -15,4 +15,5 @@ public interface IGlobalSettings @@ -15,4 +15,5 @@ public interface IGlobalSettings
IBaseServiceUriSettings BaseServiceUri { get; set; }
ITwoFactorAuthSettings TwoFactorAuth { get; set; }
ISsoSettings Sso { get; set; }
IPasswordlessAuthSettings PasswordlessAuth { get; set; }
}

6
src/Core/Settings/IPasswordlessAuthSettings.cs

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
namespace Bit.Core.Settings;
public interface IPasswordlessAuthSettings
{
bool KnownDevicesOnly { get; set; }
}

1
src/Infrastructure.Dapper/DapperServiceCollectionExtensions.cs

@ -34,6 +34,7 @@ public static class DapperServiceCollectionExtensions @@ -34,6 +34,7 @@ public static class DapperServiceCollectionExtensions
services.AddSingleton<IUserRepository, UserRepository>();
services.AddSingleton<IOrganizationApiKeyRepository, OrganizationApiKeyRepository>();
services.AddSingleton<IOrganizationConnectionRepository, OrganizationConnectionRepository>();
services.AddSingleton<IAuthRequestRepository, AuthRequestRepository>();
if (selfHosted)
{

43
src/Infrastructure.Dapper/Repositories/AuthRequestRepository.cs

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
using System.Data;
using System.Data.SqlClient;
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Bit.Core.Settings;
using Dapper;
namespace Bit.Infrastructure.Dapper.Repositories;
public class AuthRequestRepository : Repository<AuthRequest, Guid>, IAuthRequestRepository
{
public AuthRequestRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{ }
public AuthRequestRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task<int> DeleteExpiredAsync()
{
using (var connection = new SqlConnection(ConnectionString))
{
return await connection.ExecuteAsync(
$"[{Schema}].[AuthRequest_DeleteIfExpired]",
null,
commandType: CommandType.StoredProcedure);
}
}
public async Task<ICollection<AuthRequest>> GetManyByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync<AuthRequest>(
"[{Schema}].[AuthRequest_ReadByUserId]",
new { UserId = userId },
commandType: CommandType.StoredProcedure);
return results.ToList();
}
}
}

1
src/Infrastructure.EntityFramework/EntityFrameworkServiceCollectionExtensions.cs

@ -56,6 +56,7 @@ public static class EntityFrameworkServiceCollectionExtensions @@ -56,6 +56,7 @@ public static class EntityFrameworkServiceCollectionExtensions
services.AddSingleton<IProviderRepository, ProviderRepository>();
services.AddSingleton<IProviderUserRepository, ProviderUserRepository>();
services.AddSingleton<IProviderOrganizationRepository, ProviderOrganizationRepository>();
services.AddSingleton<IAuthRequestRepository, AuthRequestRepository>();
if (selfHosted)
{

17
src/Infrastructure.EntityFramework/Models/AuthRequest.cs

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
using AutoMapper;
namespace Bit.Infrastructure.EntityFramework.Models;
public class AuthRequest : Core.Entities.AuthRequest
{
public virtual User User { get; set; }
public virtual Device ResponseDevice { get; set; }
}
public class AuthRequestMapperProfile : Profile
{
public AuthRequestMapperProfile()
{
CreateMap<Core.Entities.AuthRequest, AuthRequest>().ReverseMap();
}
}

35
src/Infrastructure.EntityFramework/Repositories/AuthRequestRepository.cs

@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
using AutoMapper;
using Bit.Core.Repositories;
using Bit.Infrastructure.EntityFramework.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Infrastructure.EntityFramework.Repositories;
public class AuthRequestRepository : Repository<Core.Entities.AuthRequest, AuthRequest, Guid>, IAuthRequestRepository
{
public AuthRequestRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.AuthRequests)
{ }
public async Task<int> DeleteExpiredAsync()
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var expiredRequests = await dbContext.AuthRequests.Where(a => a.CreationDate < DateTime.Now.AddMinutes(-15)).ToListAsync();
dbContext.AuthRequests.RemoveRange(expiredRequests);
await dbContext.SaveChangesAsync();
return 1;
}
}
public async Task<ICollection<Core.Entities.AuthRequest>> GetManyByUserIdAsync(Guid userId)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
var dbContext = GetDatabaseContext(scope);
var userAuthRequests = await dbContext.AuthRequests.Where(a => a.UserId.Equals(userId)).ToListAsync();
return Mapper.Map<List<Core.Entities.AuthRequest>>(userAuthRequests);
}
}
}

4
src/Infrastructure.EntityFramework/Repositories/DatabaseContext.cs

@ -39,6 +39,7 @@ public class DatabaseContext : DbContext @@ -39,6 +39,7 @@ public class DatabaseContext : DbContext
public DbSet<TaxRate> TaxRates { get; set; }
public DbSet<Transaction> Transactions { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<AuthRequest> AuthRequests { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
@ -70,6 +71,7 @@ public class DatabaseContext : DbContext @@ -70,6 +71,7 @@ public class DatabaseContext : DbContext
var eUser = builder.Entity<User>();
var eOrganizationApiKey = builder.Entity<OrganizationApiKey>();
var eOrganizationConnection = builder.Entity<OrganizationConnection>();
var eAuthRequest = builder.Entity<AuthRequest>();
eCipher.Property(c => c.Id).ValueGeneratedNever();
eCollection.Property(c => c.Id).ValueGeneratedNever();
@ -90,6 +92,7 @@ public class DatabaseContext : DbContext @@ -90,6 +92,7 @@ public class DatabaseContext : DbContext
eUser.Property(c => c.Id).ValueGeneratedNever();
eOrganizationApiKey.Property(c => c.Id).ValueGeneratedNever();
eOrganizationConnection.Property(c => c.Id).ValueGeneratedNever();
eAuthRequest.Property(ar => ar.Id).ValueGeneratedNever();
eCollectionCipher.HasKey(cc => new { cc.CollectionId, cc.CipherId });
eCollectionUser.HasKey(cu => new { cu.CollectionId, cu.OrganizationUserId });
@ -135,5 +138,6 @@ public class DatabaseContext : DbContext @@ -135,5 +138,6 @@ public class DatabaseContext : DbContext
eUser.ToTable(nameof(User));
eOrganizationApiKey.ToTable(nameof(OrganizationApiKey));
eOrganizationConnection.ToTable(nameof(OrganizationConnection));
eAuthRequest.ToTable(nameof(AuthRequest));
}
}

19
src/Notifications/AnonymousNotificationsHub.cs

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
namespace Bit.Notifications;
[AllowAnonymous]
public class AnonymousNotificationsHub : Microsoft.AspNetCore.SignalR.Hub, INotificationHub
{
public override async Task OnConnectedAsync()
{
var httpContext = Context.GetHttpContext();
var token = httpContext.Request.Query["Token"].FirstOrDefault();
if (!string.IsNullOrWhiteSpace(token))
{
await Groups.AddToGroupAsync(Context.ConnectionId, token);
}
await base.OnConnectedAsync();
}
}

5
src/Notifications/AzureQueueHostedService.cs

@ -9,6 +9,7 @@ public class AzureQueueHostedService : IHostedService, IDisposable @@ -9,6 +9,7 @@ public class AzureQueueHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private readonly IHubContext<NotificationsHub> _hubContext;
private readonly IHubContext<AnonymousNotificationsHub> _anonymousHubContext;
private readonly GlobalSettings _globalSettings;
private Task _executingTask;
@ -18,11 +19,13 @@ public class AzureQueueHostedService : IHostedService, IDisposable @@ -18,11 +19,13 @@ public class AzureQueueHostedService : IHostedService, IDisposable
public AzureQueueHostedService(
ILogger<AzureQueueHostedService> logger,
IHubContext<NotificationsHub> hubContext,
IHubContext<AnonymousNotificationsHub> anonymousHubContext,
GlobalSettings globalSettings)
{
_logger = logger;
_hubContext = hubContext;
_globalSettings = globalSettings;
_anonymousHubContext = anonymousHubContext;
}
public Task StartAsync(CancellationToken cancellationToken)
@ -62,7 +65,7 @@ public class AzureQueueHostedService : IHostedService, IDisposable @@ -62,7 +65,7 @@ public class AzureQueueHostedService : IHostedService, IDisposable
try
{
await HubHelpers.SendNotificationToHubAsync(
message.DecodeMessageText(), _hubContext, cancellationToken);
message.DecodeMessageText(), _hubContext, _anonymousHubContext, cancellationToken);
await _queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
}
catch (Exception e)

2
src/Notifications/Controllers/SendController.cs

@ -25,7 +25,7 @@ public class SendController : Controller @@ -25,7 +25,7 @@ public class SendController : Controller
var notificationJson = await reader.ReadToEndAsync();
if (!string.IsNullOrWhiteSpace(notificationJson))
{
await HubHelpers.SendNotificationToHubAsync(notificationJson, _hubContext);
await HubHelpers.SendNotificationToHubAsync(notificationJson, _hubContext, null);
}
}
}

22
src/Notifications/HubHelpers.cs

@ -7,8 +7,12 @@ namespace Bit.Notifications; @@ -7,8 +7,12 @@ namespace Bit.Notifications;
public static class HubHelpers
{
public static async Task SendNotificationToHubAsync(string notificationJson,
IHubContext<NotificationsHub> hubContext, CancellationToken cancellationToken = default(CancellationToken))
public static async Task SendNotificationToHubAsync(
string notificationJson,
IHubContext<NotificationsHub> hubContext,
IHubContext<AnonymousNotificationsHub> anonymousHubContext,
CancellationToken cancellationToken = default(CancellationToken)
)
{
var notification = JsonSerializer.Deserialize<PushNotificationData<object>>(notificationJson);
switch (notification.Type)
@ -61,6 +65,20 @@ public static class HubHelpers @@ -61,6 +65,20 @@ public static class HubHelpers
await hubContext.Clients.User(sendNotification.Payload.UserId.ToString())
.SendAsync("ReceiveMessage", sendNotification, cancellationToken);
break;
case PushType.AuthRequestResponse:
var authRequestResponseNotification =
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
notificationJson);
await anonymousHubContext.Clients.Group(authRequestResponseNotification.Payload.Id.ToString())
.SendAsync("AuthRequestResponseRecieved", authRequestResponseNotification, cancellationToken);
break;
case PushType.AuthRequest:
var authRequestNotification =
JsonSerializer.Deserialize<PushNotificationData<AuthRequestPushNotification>>(
notificationJson);
await hubContext.Clients.User(authRequestNotification.Payload.UserId.ToString())
.SendAsync("ReceiveMessage", authRequestNotification, cancellationToken);
break;
default:
break;
}

7
src/Notifications/INotificationHub.cs

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
namespace Bit.Notifications;
public interface INotificationHub
{
Task OnConnectedAsync();
Task OnDisconnectedAsync(Exception exception);
}

7
src/Notifications/Startup.cs

@ -110,7 +110,12 @@ public class Startup @@ -110,7 +110,12 @@ public class Startup
{
endpoints.MapHub<NotificationsHub>("/hub", options =>
{
options.ApplicationMaxBufferSize = 2048; // client => server messages are not even used
options.ApplicationMaxBufferSize = 2048;
options.TransportMaxBufferSize = 4096;
});
endpoints.MapHub<AnonymousNotificationsHub>("/anonymousHub", options =>
{
options.ApplicationMaxBufferSize = 2048;
options.TransportMaxBufferSize = 4096;
});
endpoints.MapDefaultControllerRoute();

8
src/Sql/Sql.sqlproj

@ -73,6 +73,12 @@ @@ -73,6 +73,12 @@
<Build Include="dbo\Functions\PolicyApplicableToUser.sql" />
<Build Include="dbo\Functions\UserCipherDetails.sql" />
<Build Include="dbo\Functions\UserCollectionDetails.sql" />
<Build Include="dbo\Stored Procedures\AuthRequest_Create.sql" />
<Build Include="dbo\Stored Procedures\AuthRequest_DeleteById.sql" />
<Build Include="dbo\Stored Procedures\AuthRequest_DeleteIfExpired.sql" />
<Build Include="dbo\Stored Procedures\AuthRequest_ReadById.sql" />
<Build Include="dbo\Stored Procedures\AuthRequest_ReadByUserId.sql" />
<Build Include="dbo\Stored Procedures\AuthRequest_Update.sql" />
<Build Include="dbo\Stored Procedures\AzureSQLMaintenance.sql" />
<Build Include="dbo\Stored Procedures\CipherDetails_Create.sql" />
<Build Include="dbo\Stored Procedures\CipherDetails_CreateWithCollections.sql" />
@ -344,6 +350,7 @@ @@ -344,6 +350,7 @@
<Build Include="dbo\Stored Procedures\User_UpdateKeys.sql" />
<Build Include="dbo\Stored Procedures\User_UpdateRenewalReminderDate.sql" />
<Build Include="dbo\Stored Procedures\User_UpdateStorage.sql" />
<Build Include="dbo\Tables\AuthRequest.sql" />
<Build Include="dbo\Tables\Cipher.sql" />
<Build Include="dbo\Tables\Collection.sql" />
<Build Include="dbo\Tables\CollectionCipher.sql" />
@ -378,6 +385,7 @@ @@ -378,6 +385,7 @@
<Build Include="dbo\User Defined Types\OrganizationUserType.sql" />
<Build Include="dbo\User Defined Types\SelectionReadOnlyArray.sql" />
<Build Include="dbo\User Defined Types\TwoGuidIdArray.sql" />
<Build Include="dbo\Views\AuthRequestView.sql" />
<Build Include="dbo\Views\CipherView.sql" />
<Build Include="dbo\Views\CollectionView.sql" />
<Build Include="dbo\Views\DeviceView.sql" />

57
src/Sql/dbo/Stored Procedures/AuthRequest_Create.sql

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
CREATE PROCEDURE [dbo].[AuthRequest_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@UserId UNIQUEIDENTIFIER,
@Type TINYINT,
@RequestDeviceIdentifier NVARCHAR(50),
@RequestDeviceType TINYINT,
@RequestIpAddress VARCHAR(50),
@RequestFingerprint VARCHAR(MAX),
@ResponseDeviceId UNIQUEIDENTIFIER,
@AccessCode VARCHAR(25),
@PublicKey VARCHAR(MAX),
@Key VARCHAR(MAX),
@MasterPasswordHash VARCHAR(MAX),
@CreationDate DATETIME2(7),
@ResponseDate DATETIME2(7),
@AuthenticationDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[AuthRequest]
(
[Id],
[UserId],
[Type],
[RequestDeviceIdentifier],
[RequestDeviceType],
[RequestIpAddress],
[RequestFingerprint],
[ResponseDeviceId],
[AccessCode],
[PublicKey],
[Key],
[MasterPasswordHash],
[CreationDate],
[ResponseDate],
[AuthenticationDate]
)
VALUES
(
@Id,
@UserId,
@Type,
@RequestDeviceIdentifier,
@RequestDeviceType,
@RequestIpAddress,
@RequestFingerprint,
@ResponseDeviceId,
@AccessCode,
@PublicKey,
@Key,
@MasterPasswordHash,
@CreationDate,
@ResponseDate,
@AuthenticationDate
)
END

12
src/Sql/dbo/Stored Procedures/AuthRequest_DeleteById.sql

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
CREATE PROCEDURE [dbo].[AuthRequest_DeleteById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
DELETE
FROM
[dbo].[AuthRequest]
WHERE
[Id] = @Id
END

6
src/Sql/dbo/Stored Procedures/AuthRequest_DeleteIfExpired.sql

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
CREATE PROCEDURE [dbo].[AuthRequest_DeleteIfExpired]
AS
BEGIN
SET NOCOUNT OFF
DELETE FROM [dbo].[AuthRequest] WHERE [CreationDate] < DATEADD(minute, -15, GETUTCDATE());
END

13
src/Sql/dbo/Stored Procedures/AuthRequest_ReadById.sql

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
CREATE PROCEDURE [dbo].[AuthRequest_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[AuthRequestView]
WHERE
[Id] = @Id
END

13
src/Sql/dbo/Stored Procedures/AuthRequest_ReadByUserId.sql

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
CREATE PROCEDURE [dbo].[AuthRequest_ReadByUserId]
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[AuthRequestView]
WHERE
[UserId] = @UserId
END

22
src/Sql/dbo/Stored Procedures/AuthRequest_Update.sql

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
CREATE PROCEDURE [dbo].[AuthRequest_Update]
@Id UNIQUEIDENTIFIER OUTPUT,
@ResponseDeviceId UNIQUEIDENTIFIER,
@Key VARCHAR(MAX),
@MasterPasswordHash VARCHAR(MAX),
@ResponseDate DATETIME2(7),
@AuthenticationDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[AuthRequest]
SET
[ResponseDeviceId] = @ResponseDeviceId,
[Key] = @Key,
[MasterPasswordHash] = @MasterPasswordHash,
[ResponseDate] = @ResponseDate,
[AuthenticationDate] = @AuthenticationDate
WHERE
[Id] = @Id
END

23
src/Sql/dbo/Tables/AuthRequest.sql

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
CREATE TABLE [dbo].[AuthRequest] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[UserId] UNIQUEIDENTIFIER NOT NULL,
[Type] SMALLINT NOT NULL,
[RequestDeviceIdentifier] NVARCHAR(50) NOT NULL,
[RequestDeviceType] SMALLINT NOT NULL,
[RequestIpAddress] VARCHAR(50) NOT NULL,
[RequestFingerprint] VARCHAR(MAX) NOT NULL,
[ResponseDeviceId] UNIQUEIDENTIFIER NULL,
[AccessCode] VARCHAR(25) NOT NULL,
[PublicKey] VARCHAR(MAX) NOT NULL,
[Key] VARCHAR(MAX) NULL,
[MasterPasswordHash] VARCHAR(MAX) NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[ResponseDate] DATETIME2 (7) NULL,
[AuthenticationDate] DATETIME2 (7) NULL,
CONSTRAINT [PK_AuthRequest] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_AuthRequest_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
CONSTRAINT [FK_AuthRequest_ResponseDevice] FOREIGN KEY ([ResponseDeviceId]) REFERENCES [dbo].[Device] ([Id])
);
GO

6
src/Sql/dbo/Views/AuthRequestView.sql

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
CREATE VIEW [dbo].[AuthRequestView]
AS
SELECT
*
FROM
[dbo].[AuthRequest]

61
test/Infrastructure.EFIntegration.Test/AutoFixture/AuthRequestFixtures.cs

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
using AutoFixture;
using AutoFixture.Kernel;
using Bit.Core.Entities;
using Bit.Core.Test.AutoFixture.UserFixtures;
using Bit.Infrastructure.EFIntegration.Test.AutoFixture.Relays;
using Bit.Infrastructure.EntityFramework.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
namespace Bit.Infrastructure.EFIntegration.Test.AutoFixture;
internal class AuthRequestBuilder : ISpecimenBuilder
{
public object Create(object request, ISpecimenContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var type = request as Type;
if (type == null || type != typeof(AuthRequest))
{
return new NoSpecimen();
}
var fixture = new Fixture();
fixture.Customizations.Insert(0, new MaxLengthStringRelay());
var obj = fixture.WithAutoNSubstitutions().Create<AuthRequest>();
return obj;
}
}
internal class EfAuthRequest : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(new IgnoreVirtualMembersCustomization());
fixture.Customizations.Add(new GlobalSettingsBuilder());
fixture.Customizations.Add(new AuthRequestBuilder());
fixture.Customizations.Add(new DeviceBuilder());
fixture.Customizations.Add(new UserBuilder());
fixture.Customizations.Add(new EfRepositoryListBuilder<AuthRequestRepository>());
fixture.Customizations.Add(new EfRepositoryListBuilder<DeviceRepository>());
fixture.Customizations.Add(new EfRepositoryListBuilder<UserRepository>());
}
}
internal class EfAuthRequestAutoDataAttribute : CustomAutoDataAttribute
{
public EfAuthRequestAutoDataAttribute() : base(new SutProviderCustomization(), new EfAuthRequest())
{ }
}
internal class InlineEfAuthRequestAutoDataAttribute : InlineCustomAutoDataAttribute
{
public InlineEfAuthRequestAutoDataAttribute(params object[] values) : base(new[] { typeof(SutProviderCustomization),
typeof(EfAuthRequest) }, values)
{ }
}

1
test/Infrastructure.EFIntegration.Test/AutoFixture/EntityFrameworkRepositoryFixtures.cs

@ -63,6 +63,7 @@ public class EfRepositoryListBuilder<T> : ISpecimenBuilder where T : BaseEntityF @@ -63,6 +63,7 @@ public class EfRepositoryListBuilder<T> : ISpecimenBuilder where T : BaseEntityF
fixture.Customize<IMapper>(x => x.FromFactory(() =>
new MapperConfiguration(cfg =>
{
cfg.AddProfile<AuthRequestMapperProfile>();
cfg.AddProfile<CipherMapperProfile>();
cfg.AddProfile<CollectionCipherMapperProfile>();
cfg.AddProfile<CollectionMapperProfile>();

50
test/Infrastructure.EFIntegration.Test/Repositories/AuthRequestRepositoryTests.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
using Bit.Core.Entities;
using Bit.Core.Test.AutoFixture.Attributes;
using Bit.Infrastructure.EFIntegration.Test.AutoFixture;
using Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
using Xunit;
using EfRepo = Bit.Infrastructure.EntityFramework.Repositories;
using SqlRepo = Bit.Infrastructure.Dapper.Repositories;
namespace Bit.Infrastructure.EFIntegration.Test.Repositories;
public class AuthRequestRepositoryTests
{
[CiSkippedTheory, EfAuthRequestAutoData]
public async void CreateAsync_Works_DataMatches(
AuthRequest authRequest,
AuthRequestCompare equalityComparer,
List<EfRepo.AuthRequestRepository> suts,
SqlRepo.AuthRequestRepository sqlAuthRequestRepo,
User user,
List<EfRepo.UserRepository> efUserRepos,
SqlRepo.UserRepository sqlUserRepo
)
{
authRequest.ResponseDeviceId = null;
var savedAuthRequests = new List<AuthRequest>();
foreach (var sut in suts)
{
var i = suts.IndexOf(sut);
var efUser = await efUserRepos[i].CreateAsync(user);
sut.ClearChangeTracking();
authRequest.UserId = efUser.Id;
var postEfAuthRequest = await sut.CreateAsync(authRequest);
sut.ClearChangeTracking();
var savedAuthRequest = await sut.GetByIdAsync(postEfAuthRequest.Id);
savedAuthRequests.Add(savedAuthRequest);
}
var sqlUser = await sqlUserRepo.CreateAsync(user);
authRequest.UserId = sqlUser.Id;
var sqlAuthRequest = await sqlAuthRequestRepo.CreateAsync(authRequest);
var savedSqlAuthRequest = await sqlAuthRequestRepo.GetByIdAsync(sqlAuthRequest.Id);
savedAuthRequests.Add(savedSqlAuthRequest);
var distinctItems = savedAuthRequests.Distinct(equalityComparer);
Assert.True(!distinctItems.Skip(1).Any());
}
}

23
test/Infrastructure.EFIntegration.Test/Repositories/EqualityComparers/AuthRequestCompare.cs

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
using System.Diagnostics.CodeAnalysis;
using Bit.Core.Entities;
namespace Bit.Infrastructure.EFIntegration.Test.Repositories.EqualityComparers;
public class AuthRequestCompare : IEqualityComparer<AuthRequest>
{
public bool Equals(AuthRequest x, AuthRequest y)
{
return x.AccessCode == y.AccessCode &&
x.MasterPasswordHash == y.MasterPasswordHash &&
x.PublicKey == y.PublicKey &&
x.RequestDeviceIdentifier == y.RequestDeviceIdentifier &&
x.RequestDeviceType == y.RequestDeviceType &&
x.RequestIpAddress == y.RequestIpAddress &&
x.RequestFingerprint == y.RequestFingerprint;
}
public int GetHashCode([DisallowNull] AuthRequest obj)
{
return base.GetHashCode();
}
}

218
util/Migrator/DbScripts/2022-09-12_00_AuthRequestInit.sql

@ -0,0 +1,218 @@ @@ -0,0 +1,218 @@
-- Create Auth Request table
IF OBJECT_ID('[dbo].[AuthRequest]') IS NOT NULL
BEGIN
DROP TABLE [dbo].[AuthRequest]
END
IF OBJECT_ID('[dbo].[AuthRequest]') IS NULL
BEGIN
CREATE TABLE [dbo].[AuthRequest] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[UserId] UNIQUEIDENTIFIER NOT NULL,
[Type] SMALLINT NOT NULL,
[RequestDeviceIdentifier] NVARCHAR(50) NOT NULL,
[RequestDeviceType] SMALLINT NOT NULL,
[RequestIpAddress] VARCHAR(50) NOT NULL,
[RequestFingerprint] VARCHAR(MAX) NOT NULL,
[ResponseDeviceId] UNIQUEIDENTIFIER NULL,
[AccessCode] VARCHAR(25) NOT NULL,
[PublicKey] VARCHAR(MAX) NOT NULL,
[Key] VARCHAR(MAX) NULL,
[MasterPasswordHash] VARCHAR(MAX) NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[ResponseDate] DATETIME2 (7) NULL,
[AuthenticationDate] DATETIME2 (7) NULL,
CONSTRAINT [PK_AuthRequest] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_AuthRequest_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
CONSTRAINT [FK_AuthRequest_ResponseDevice] FOREIGN KEY ([ResponseDeviceId]) REFERENCES [dbo].[Device] ([Id])
);
END
GO
-- Create View
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'AuthRequestView')
BEGIN
DROP VIEW [dbo].[AuthRequestView]
END
GO
CREATE VIEW [dbo].[AuthRequestView]
AS
SELECT
*
FROM
[dbo].[AuthRequest]
GO
-- Auth Request CRUD sprocs
IF OBJECT_ID('[dbo].[AuthRequest_Create]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[AuthRequest_Create]
END
GO
CREATE PROCEDURE [dbo].[AuthRequest_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@UserId UNIQUEIDENTIFIER,
@Type TINYINT,
@RequestDeviceIdentifier NVARCHAR(50),
@RequestDeviceType TINYINT,
@RequestIpAddress VARCHAR(50),
@RequestFingerprint VARCHAR(MAX),
@ResponseDeviceId UNIQUEIDENTIFIER,
@AccessCode VARCHAR(25),
@PublicKey VARCHAR(MAX),
@Key VARCHAR(MAX),
@MasterPasswordHash VARCHAR(MAX),
@CreationDate DATETIME2(7),
@ResponseDate DATETIME2(7),
@AuthenticationDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[AuthRequest]
(
[Id],
[UserId],
[Type],
[RequestDeviceIdentifier],
[RequestDeviceType],
[RequestIpAddress],
[RequestFingerprint],
[ResponseDeviceId],
[AccessCode],
[PublicKey],
[Key],
[MasterPasswordHash],
[CreationDate],
[ResponseDate],
[AuthenticationDate]
)
VALUES
(
@Id,
@UserId,
@Type,
@RequestDeviceIdentifier,
@RequestDeviceType,
@RequestIpAddress,
@RequestFingerprint,
@ResponseDeviceId,
@AccessCode,
@PublicKey,
@Key,
@MasterPasswordHash,
@CreationDate,
@ResponseDate,
@AuthenticationDate
)
END
GO
IF OBJECT_ID('[dbo].[AuthRequest_Update]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[AuthRequest_Update]
END
GO
CREATE PROCEDURE [dbo].[AuthRequest_Update]
@Id UNIQUEIDENTIFIER OUTPUT,
@ResponseDeviceId UNIQUEIDENTIFIER,
@Key VARCHAR(MAX),
@MasterPasswordHash VARCHAR(MAX),
@ResponseDate DATETIME2(7),
@AuthenticationDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[AuthRequest]
SET
[ResponseDeviceId] = @ResponseDeviceId,
[Key] = @Key,
[MasterPasswordHash] = @MasterPasswordHash,
[ResponseDate] = @ResponseDate,
[AuthenticationDate] = @AuthenticationDate
WHERE
[Id] = @Id
END
GO
IF OBJECT_ID('[dbo].[AuthRequest_ReadById]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[AuthRequest_ReadById]
END
GO
CREATE PROCEDURE [dbo].[AuthRequest_ReadById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[AuthRequestView]
WHERE
[Id] = @Id
END
GO
IF OBJECT_ID('[dbo].[AuthRequest_DeleteById]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[AuthRequest_DeleteById]
END
GO
CREATE PROCEDURE [dbo].[AuthRequest_DeleteById]
@Id UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
DELETE
FROM
[dbo].[AuthRequest]
WHERE
[Id] = @Id
END
GO
IF OBJECT_ID('[dbo].[AuthRequest_DeleteIfExpired]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[AuthRequest_DeleteIfExpired]
END
GO
CREATE PROCEDURE [dbo].[AuthRequest_DeleteIfExpired]
AS
BEGIN
SET NOCOUNT OFF
DELETE FROM [dbo].[AuthRequest] WHERE [CreationDate] < DATEADD(minute, -15, GETUTCDATE());
END
GO
IF OBJECT_ID('[dbo].[AuthRequest_ReadByUserId]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[AuthRequest_ReadByUserId]
END
GO
CREATE PROCEDURE [dbo].[AuthRequest_ReadByUserId]
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
SELECT
*
FROM
[dbo].[AuthRequestView]
WHERE
[UserId] = @UserId
END
GO

1672
util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.Designer.cs generated

File diff suppressed because it is too large Load Diff

71
util/MySqlMigrations/Migrations/20220912144222_PasswordlessAuthRequests.cs

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.MySqlMigrations.Migrations;
public partial class PasswordlessAuthRequests : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AuthRequest",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
UserId = table.Column<Guid>(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
Type = table.Column<byte>(type: "tinyint unsigned", nullable: false),
RequestDeviceIdentifier = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
RequestDeviceType = table.Column<byte>(type: "tinyint unsigned", nullable: false),
RequestIpAddress = table.Column<string>(type: "varchar(50)", maxLength: 50, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
RequestFingerprint = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
ResponseDeviceId = table.Column<Guid>(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
AccessCode = table.Column<string>(type: "varchar(25)", maxLength: 25, nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
PublicKey = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
Key = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
MasterPasswordHash = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
CreationDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
ResponseDate = table.Column<DateTime>(type: "datetime(6)", nullable: true),
AuthenticationDate = table.Column<DateTime>(type: "datetime(6)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AuthRequest", x => x.Id);
table.ForeignKey(
name: "FK_AuthRequest_Device_ResponseDeviceId",
column: x => x.ResponseDeviceId,
principalTable: "Device",
principalColumn: "Id");
table.ForeignKey(
name: "FK_AuthRequest_User_UserId",
column: x => x.UserId,
principalTable: "User",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_AuthRequest_ResponseDeviceId",
table: "AuthRequest",
column: "ResponseDeviceId");
migrationBuilder.CreateIndex(
name: "IX_AuthRequest_UserId",
table: "AuthRequest",
column: "UserId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AuthRequest");
}
}

76
util/MySqlMigrations/Migrations/DatabaseContextModelSnapshot.cs

@ -19,6 +19,65 @@ namespace Bit.MySqlMigrations.Migrations @@ -19,6 +19,65 @@ namespace Bit.MySqlMigrations.Migrations
.HasAnnotation("ProductVersion", "6.0.4")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b =>
{
b.Property<Guid>("Id")
.HasColumnType("char(36)");
b.Property<string>("AccessCode")
.HasMaxLength(25)
.HasColumnType("varchar(25)");
b.Property<DateTime?>("AuthenticationDate")
.HasColumnType("datetime(6)");
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime(6)");
b.Property<string>("Key")
.HasColumnType("longtext");
b.Property<string>("MasterPasswordHash")
.HasColumnType("longtext");
b.Property<string>("PublicKey")
.HasColumnType("longtext");
b.Property<string>("RequestDeviceIdentifier")
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<byte>("RequestDeviceType")
.HasColumnType("tinyint unsigned");
b.Property<string>("RequestFingerprint")
.HasColumnType("longtext");
b.Property<string>("RequestIpAddress")
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<DateTime?>("ResponseDate")
.HasColumnType("datetime(6)");
b.Property<Guid?>("ResponseDeviceId")
.HasColumnType("char(36)");
b.Property<byte>("Type")
.HasColumnType("tinyint unsigned");
b.Property<Guid>("UserId")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("ResponseDeviceId");
b.HasIndex("UserId");
b.ToTable("AuthRequest", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b =>
{
b.Property<Guid>("Id")
@ -1208,6 +1267,23 @@ namespace Bit.MySqlMigrations.Migrations @@ -1208,6 +1267,23 @@ namespace Bit.MySqlMigrations.Migrations
b.ToTable("User", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice")
.WithMany()
.HasForeignKey("ResponseDeviceId");
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ResponseDevice");
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization")

31
util/MySqlMigrations/Scripts/2022-09-12_00_PasswordlessAuth.sql

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
START TRANSACTION;
CREATE TABLE `AuthRequest` (
`Id` char(36) COLLATE ascii_general_ci NOT NULL,
`UserId` char(36) COLLATE ascii_general_ci NOT NULL,
`Type` tinyint unsigned NOT NULL,
`RequestDeviceIdentifier` varchar(50) CHARACTER SET utf8mb4 NULL,
`RequestDeviceType` tinyint unsigned NOT NULL,
`RequestIpAddress` varchar(50) CHARACTER SET utf8mb4 NULL,
`RequestFingerprint` longtext CHARACTER SET utf8mb4 NULL,
`ResponseDeviceId` char(36) COLLATE ascii_general_ci NULL,
`AccessCode` varchar(25) CHARACTER SET utf8mb4 NULL,
`PublicKey` longtext CHARACTER SET utf8mb4 NULL,
`Key` longtext CHARACTER SET utf8mb4 NULL,
`MasterPasswordHash` longtext CHARACTER SET utf8mb4 NULL,
`CreationDate` datetime(6) NOT NULL,
`ResponseDate` datetime(6) NULL,
`AuthenticationDate` datetime(6) NULL,
CONSTRAINT `PK_AuthRequest` PRIMARY KEY (`Id`),
CONSTRAINT `FK_AuthRequest_Device_ResponseDeviceId` FOREIGN KEY (`ResponseDeviceId`) REFERENCES `Device` (`Id`),
CONSTRAINT `FK_AuthRequest_User_UserId` FOREIGN KEY (`UserId`) REFERENCES `User` (`Id`) ON DELETE CASCADE
) CHARACTER SET=utf8mb4;
CREATE INDEX `IX_AuthRequest_ResponseDeviceId` ON `AuthRequest` (`ResponseDeviceId`);
CREATE INDEX `IX_AuthRequest_UserId` ON `AuthRequest` (`UserId`);
INSERT INTO `__EFMigrationsHistory` (`MigrationId`, `ProductVersion`)
VALUES ('20220912144222_PasswordlessAuthRequests', '6.0.4');
COMMIT;

1680
util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.Designer.cs generated

File diff suppressed because it is too large Load Diff

905
util/PostgresMigrations/Migrations/20220830163921_PasswordlessAuthRequests.cs

@ -0,0 +1,905 @@ @@ -0,0 +1,905 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.PostgresMigrations.Migrations;
public partial class PasswordlessAuthRequests : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "User",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RenewalReminderDate",
table: "User",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "PremiumExpirationDate",
table: "User",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "LastFailedLoginDate",
table: "User",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "User",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AccountRevisionDate",
table: "User",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Transaction",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "SsoUser",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "SsoConfig",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "SsoConfig",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Send",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ExpirationDate",
table: "Send",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "DeletionDate",
table: "Send",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Send",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "ProviderUser",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "ProviderUser",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "ProviderOrganization",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "ProviderOrganization",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Provider",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Provider",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Policy",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Policy",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "OrganizationUser",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "OrganizationUser",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ValidUntil",
table: "OrganizationSponsorship",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "LastSyncDate",
table: "OrganizationSponsorship",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "OrganizationApiKey",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Organization",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "OwnersNotifiedOfAutoscaling",
table: "Organization",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "ExpirationDate",
table: "Organization",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Organization",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Installation",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Group",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Group",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ExpirationDate",
table: "Grant",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Grant",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ConsumedDate",
table: "Grant",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Folder",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Folder",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "Date",
table: "Event",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "EmergencyAccess",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RecoveryInitiatedDate",
table: "EmergencyAccess",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "LastNotificationDate",
table: "EmergencyAccess",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "EmergencyAccess",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Device",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Device",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Collection",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Collection",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Cipher",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "DeletedDate",
table: "Cipher",
type: "timestamp with time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Cipher",
type: "timestamp with time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp without time zone");
migrationBuilder.CreateTable(
name: "AuthRequest",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
Type = table.Column<byte>(type: "smallint", nullable: false),
RequestDeviceIdentifier = table.Column<string>(type: "text", nullable: true),
RequestDeviceType = table.Column<byte>(type: "smallint", nullable: false),
RequestIpAddress = table.Column<string>(type: "text", nullable: true),
RequestFingerprint = table.Column<string>(type: "text", nullable: true),
ResponseDeviceId = table.Column<Guid>(type: "uuid", nullable: true),
AccessCode = table.Column<string>(type: "text", nullable: true),
PublicKey = table.Column<string>(type: "text", nullable: true),
Key = table.Column<string>(type: "text", nullable: true),
MasterPasswordHash = table.Column<string>(type: "text", nullable: true),
CreationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
ResponseDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
AuthenticationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AuthRequest", x => x.Id);
table.ForeignKey(
name: "FK_AuthRequest_Device_ResponseDeviceId",
column: x => x.ResponseDeviceId,
principalTable: "Device",
principalColumn: "Id");
table.ForeignKey(
name: "FK_AuthRequest_User_UserId",
column: x => x.UserId,
principalTable: "User",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AuthRequest_ResponseDeviceId",
table: "AuthRequest",
column: "ResponseDeviceId");
migrationBuilder.CreateIndex(
name: "IX_AuthRequest_UserId",
table: "AuthRequest",
column: "UserId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AuthRequest");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "User",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RenewalReminderDate",
table: "User",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "PremiumExpirationDate",
table: "User",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "LastFailedLoginDate",
table: "User",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "User",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "AccountRevisionDate",
table: "User",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Transaction",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "SsoUser",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "SsoConfig",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "SsoConfig",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Send",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ExpirationDate",
table: "Send",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "DeletionDate",
table: "Send",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Send",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "ProviderUser",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "ProviderUser",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "ProviderOrganization",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "ProviderOrganization",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Provider",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Provider",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Policy",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Policy",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "OrganizationUser",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "OrganizationUser",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ValidUntil",
table: "OrganizationSponsorship",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "LastSyncDate",
table: "OrganizationSponsorship",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "OrganizationApiKey",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Organization",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "OwnersNotifiedOfAutoscaling",
table: "Organization",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "ExpirationDate",
table: "Organization",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Organization",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Installation",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Group",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Group",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ExpirationDate",
table: "Grant",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Grant",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "ConsumedDate",
table: "Grant",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Folder",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Folder",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "Date",
table: "Event",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "EmergencyAccess",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RecoveryInitiatedDate",
table: "EmergencyAccess",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "LastNotificationDate",
table: "EmergencyAccess",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "EmergencyAccess",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Device",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Device",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Collection",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Collection",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "RevisionDate",
table: "Cipher",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
migrationBuilder.AlterColumn<DateTime>(
name: "DeletedDate",
table: "Cipher",
type: "timestamp without time zone",
nullable: true,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone",
oldNullable: true);
migrationBuilder.AlterColumn<DateTime>(
name: "CreationDate",
table: "Cipher",
type: "timestamp without time zone",
nullable: false,
oldClrType: typeof(DateTime),
oldType: "timestamp with time zone");
}
}

175
util/PostgresMigrations/Migrations/DatabaseContextModelSnapshot.cs

@ -23,6 +23,62 @@ namespace Bit.PostgresMigrations.Migrations @@ -23,6 +23,62 @@ namespace Bit.PostgresMigrations.Migrations
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("AccessCode")
.HasColumnType("text");
b.Property<DateTime?>("AuthenticationDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Key")
.HasColumnType("text");
b.Property<string>("MasterPasswordHash")
.HasColumnType("text");
b.Property<string>("PublicKey")
.HasColumnType("text");
b.Property<string>("RequestDeviceIdentifier")
.HasColumnType("text");
b.Property<byte>("RequestDeviceType")
.HasColumnType("smallint");
b.Property<string>("RequestFingerprint")
.HasColumnType("text");
b.Property<string>("RequestIpAddress")
.HasColumnType("text");
b.Property<DateTime?>("ResponseDate")
.HasColumnType("timestamp with time zone");
b.Property<Guid?>("ResponseDeviceId")
.HasColumnType("uuid");
b.Property<byte>("Type")
.HasColumnType("smallint");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("ResponseDeviceId");
b.HasIndex("UserId");
b.ToTable("AuthRequest", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b =>
{
b.Property<Guid>("Id")
@ -32,13 +88,13 @@ namespace Bit.PostgresMigrations.Migrations @@ -32,13 +88,13 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Data")
.HasColumnType("text");
b.Property<DateTime?>("DeletedDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Favorites")
.HasColumnType("text");
@ -53,7 +109,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -53,7 +109,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("smallint");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Type")
.HasColumnType("smallint");
@ -76,7 +132,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -76,7 +132,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("ExternalId")
.HasMaxLength(300)
@ -89,7 +145,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -89,7 +145,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
@ -167,7 +223,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -167,7 +223,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Identifier")
.HasMaxLength(50)
@ -182,7 +238,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -182,7 +238,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(255)");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Type")
.HasColumnType("smallint");
@ -203,7 +259,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -203,7 +259,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
@ -219,13 +275,13 @@ namespace Bit.PostgresMigrations.Migrations @@ -219,13 +275,13 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime?>("LastNotificationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("RecoveryInitiatedDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Status")
.HasColumnType("smallint");
@ -260,7 +316,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -260,7 +316,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("Date")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte?>("DeviceType")
.HasColumnType("smallint");
@ -310,13 +366,13 @@ namespace Bit.PostgresMigrations.Migrations @@ -310,13 +366,13 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
@ -339,10 +395,10 @@ namespace Bit.PostgresMigrations.Migrations @@ -339,10 +395,10 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(200)");
b.Property<DateTime?>("ConsumedDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Data")
.HasColumnType("text");
@ -352,7 +408,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -352,7 +408,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(200)");
b.Property<DateTime?>("ExpirationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("SessionId")
.HasMaxLength(100)
@ -380,7 +436,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -380,7 +436,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("boolean");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("ExternalId")
.HasMaxLength(300)
@ -394,7 +450,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -394,7 +450,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
@ -429,7 +485,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -429,7 +485,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
@ -481,13 +537,13 @@ namespace Bit.PostgresMigrations.Migrations @@ -481,13 +537,13 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(30)");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<bool>("Enabled")
.HasColumnType("boolean");
b.Property<DateTime?>("ExpirationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte?>("Gateway")
.HasColumnType("smallint");
@ -523,7 +579,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -523,7 +579,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(50)");
b.Property<DateTime?>("OwnersNotifiedOfAutoscaling")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Plan")
.HasMaxLength(50)
@ -542,7 +598,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -542,7 +598,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<int?>("Seats")
.HasColumnType("integer");
@ -610,7 +666,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -610,7 +666,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Type")
.HasColumnType("smallint");
@ -656,7 +712,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -656,7 +712,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(256)");
b.Property<DateTime?>("LastSyncDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("OfferedToEmail")
.HasMaxLength(256)
@ -678,7 +734,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -678,7 +734,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("boolean");
b.Property<DateTime?>("ValidUntil")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
@ -698,7 +754,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -698,7 +754,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("boolean");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasMaxLength(256)
@ -721,7 +777,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -721,7 +777,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<short>("Status")
.HasColumnType("smallint");
@ -747,7 +803,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -747,7 +803,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Data")
.HasColumnType("text");
@ -759,7 +815,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -759,7 +815,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Type")
.HasColumnType("smallint");
@ -798,7 +854,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -798,7 +854,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<bool>("Enabled")
.HasColumnType("boolean");
@ -807,7 +863,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -807,7 +863,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Status")
.HasColumnType("smallint");
@ -826,7 +882,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -826,7 +882,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Key")
.HasColumnType("text");
@ -838,7 +894,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -838,7 +894,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Settings")
.HasColumnType("text");
@ -858,7 +914,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -858,7 +914,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Email")
.HasColumnType("text");
@ -873,7 +929,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -873,7 +929,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Status")
.HasColumnType("smallint");
@ -902,19 +958,19 @@ namespace Bit.PostgresMigrations.Migrations @@ -902,19 +958,19 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("integer");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Data")
.HasColumnType("text");
b.Property<DateTime>("DeletionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<bool>("Disabled")
.HasColumnType("boolean");
b.Property<DateTime?>("ExpirationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<bool?>("HideEmail")
.HasColumnType("boolean");
@ -933,7 +989,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -933,7 +989,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(300)");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<byte>("Type")
.HasColumnType("smallint");
@ -959,7 +1015,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -959,7 +1015,7 @@ namespace Bit.PostgresMigrations.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Data")
.HasColumnType("text");
@ -971,7 +1027,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -971,7 +1027,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
@ -989,7 +1045,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -989,7 +1045,7 @@ namespace Bit.PostgresMigrations.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("ExternalId")
.HasMaxLength(50)
@ -1049,7 +1105,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -1049,7 +1105,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("numeric");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Details")
.HasMaxLength(100)
@ -1095,7 +1151,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -1095,7 +1151,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("uuid");
b.Property<DateTime>("AccountRevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("ApiKey")
.IsRequired()
@ -1103,7 +1159,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -1103,7 +1159,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("character varying(30)");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("Culture")
.HasMaxLength(10)
@ -1151,7 +1207,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -1151,7 +1207,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime?>("LastFailedLoginDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("LicenseKey")
.HasMaxLength(100)
@ -1176,7 +1232,7 @@ namespace Bit.PostgresMigrations.Migrations @@ -1176,7 +1232,7 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("boolean");
b.Property<DateTime?>("PremiumExpirationDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("PrivateKey")
.HasColumnType("text");
@ -1188,10 +1244,10 @@ namespace Bit.PostgresMigrations.Migrations @@ -1188,10 +1244,10 @@ namespace Bit.PostgresMigrations.Migrations
.HasColumnType("text");
b.Property<DateTime?>("RenewalReminderDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp without time zone");
.HasColumnType("timestamp with time zone");
b.Property<string>("SecurityStamp")
.IsRequired()
@ -1219,6 +1275,23 @@ namespace Bit.PostgresMigrations.Migrations @@ -1219,6 +1275,23 @@ namespace Bit.PostgresMigrations.Migrations
b.ToTable("User", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.AuthRequest", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.Device", "ResponseDevice")
.WithMany()
.HasForeignKey("ResponseDeviceId");
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ResponseDevice");
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.Cipher", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.Organization", "Organization")

133
util/PostgresMigrations/Scripts/2022-09-12_00_PasswordlessAuthRequests.psql

@ -0,0 +1,133 @@ @@ -0,0 +1,133 @@
START TRANSACTION;
ALTER TABLE "User" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "User" ALTER COLUMN "RenewalReminderDate" TYPE timestamp with time zone;
ALTER TABLE "User" ALTER COLUMN "PremiumExpirationDate" TYPE timestamp with time zone;
ALTER TABLE "User" ALTER COLUMN "LastFailedLoginDate" TYPE timestamp with time zone;
ALTER TABLE "User" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "User" ALTER COLUMN "AccountRevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Transaction" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "SsoUser" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "SsoConfig" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "SsoConfig" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Send" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Send" ALTER COLUMN "ExpirationDate" TYPE timestamp with time zone;
ALTER TABLE "Send" ALTER COLUMN "DeletionDate" TYPE timestamp with time zone;
ALTER TABLE "Send" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "ProviderUser" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "ProviderUser" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "ProviderOrganization" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "ProviderOrganization" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Provider" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Provider" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Policy" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Policy" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "OrganizationUser" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "OrganizationUser" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "OrganizationSponsorship" ALTER COLUMN "ValidUntil" TYPE timestamp with time zone;
ALTER TABLE "OrganizationSponsorship" ALTER COLUMN "LastSyncDate" TYPE timestamp with time zone;
ALTER TABLE "OrganizationApiKey" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Organization" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Organization" ALTER COLUMN "OwnersNotifiedOfAutoscaling" TYPE timestamp with time zone;
ALTER TABLE "Organization" ALTER COLUMN "ExpirationDate" TYPE timestamp with time zone;
ALTER TABLE "Organization" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Installation" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Group" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Group" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Grant" ALTER COLUMN "ExpirationDate" TYPE timestamp with time zone;
ALTER TABLE "Grant" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Grant" ALTER COLUMN "ConsumedDate" TYPE timestamp with time zone;
ALTER TABLE "Folder" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Folder" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Event" ALTER COLUMN "Date" TYPE timestamp with time zone;
ALTER TABLE "EmergencyAccess" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "EmergencyAccess" ALTER COLUMN "RecoveryInitiatedDate" TYPE timestamp with time zone;
ALTER TABLE "EmergencyAccess" ALTER COLUMN "LastNotificationDate" TYPE timestamp with time zone;
ALTER TABLE "EmergencyAccess" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Device" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Device" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Collection" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Collection" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
ALTER TABLE "Cipher" ALTER COLUMN "RevisionDate" TYPE timestamp with time zone;
ALTER TABLE "Cipher" ALTER COLUMN "DeletedDate" TYPE timestamp with time zone;
ALTER TABLE "Cipher" ALTER COLUMN "CreationDate" TYPE timestamp with time zone;
CREATE TABLE "AuthRequest" (
"Id" uuid NOT NULL,
"UserId" uuid NOT NULL,
"Type" smallint NOT NULL,
"RequestDeviceIdentifier" text NULL,
"RequestDeviceType" smallint NOT NULL,
"RequestIpAddress" text NULL,
"RequestFingerprint" text NULL,
"ResponseDeviceId" uuid NULL,
"AccessCode" text NULL,
"PublicKey" text NULL,
"Key" text NULL,
"MasterPasswordHash" text NULL,
"CreationDate" timestamp with time zone NOT NULL,
"ResponseDate" timestamp with time zone NULL,
"AuthenticationDate" timestamp with time zone NULL,
CONSTRAINT "PK_AuthRequest" PRIMARY KEY ("Id"),
CONSTRAINT "FK_AuthRequest_Device_ResponseDeviceId" FOREIGN KEY ("ResponseDeviceId") REFERENCES "Device" ("Id"),
CONSTRAINT "FK_AuthRequest_User_UserId" FOREIGN KEY ("UserId") REFERENCES "User" ("Id") ON DELETE CASCADE
);
CREATE INDEX "IX_AuthRequest_ResponseDeviceId" ON "AuthRequest" ("ResponseDeviceId");
CREATE INDEX "IX_AuthRequest_UserId" ON "AuthRequest" ("UserId");
INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20220830163921_PasswordlessAuthRequests', '6.0.4');
COMMIT;
Loading…
Cancel
Save