1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-01 23:31:41 +01:00

[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>
This commit is contained in:
Addison Beck 2022-09-26 13:21:13 -04:00 committed by GitHub
parent 7c3637c8ba
commit 02bea3c48d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 5853 additions and 61 deletions

View File

@ -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");
}
}

View File

@ -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
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
services.AddTransient<DatabaseExpiredSponsorshipsJob>();
services.AddTransient<DeleteSendsJob>();
services.AddTransient<DeleteCiphersJob>();
services.AddTransient<DeleteAuthRequestsJob>();
}
}

View File

@ -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);
}
}

View File

@ -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
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;
}
}

View File

@ -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; }
}

View File

@ -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; }
}

View File

@ -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);
}
}

View File

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

View File

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

View File

@ -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
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
_userService = userService;
_currentContext = currentContext;
_captchaValidationService = captchaValidationService;
_authRequestRepository = authRequestRepository;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
@ -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,

View File

@ -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; }
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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));

View File

@ -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));

View File

@ -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);

View File

@ -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

View File

@ -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)
{

View File

@ -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
get => string.IsNullOrWhiteSpace(_apiUri) ? "https://api.bitwarden.com" : _apiUri;
set => _apiUri = value;
}
}
public class AmazonSettings
@ -519,4 +521,8 @@ public class GlobalSettings : IGlobalSettings
public int SlidingWindowSeconds { get; set; } = 120;
}
public class PasswordlessAuthSettings : IPasswordlessAuthSettings
{
public bool KnownDevicesOnly { get; set; } = true;
}
}

View File

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

View File

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

View File

@ -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)
{

View File

@ -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();
}
}
}

View File

@ -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)
{

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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
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
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
eUser.ToTable(nameof(User));
eOrganizationApiKey.ToTable(nameof(OrganizationApiKey));
eOrganizationConnection.ToTable(nameof(OrganizationConnection));
eAuthRequest.ToTable(nameof(AuthRequest));
}
}

View File

@ -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();
}
}

View File

@ -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
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
try
{
await HubHelpers.SendNotificationToHubAsync(
message.DecodeMessageText(), _hubContext, cancellationToken);
message.DecodeMessageText(), _hubContext, _anonymousHubContext, cancellationToken);
await _queueClient.DeleteMessageAsync(message.MessageId, message.PopReceipt);
}
catch (Exception e)

View File

@ -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);
}
}
}

View File

@ -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
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;
}

View File

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

View File

@ -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();

View File

@ -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 @@
<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 @@
<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" />

View File

@ -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

View File

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

View File

@ -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

View File

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

View File

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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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)
{ }
}

View File

@ -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>();

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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");
}
}

View File

@ -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
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")

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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");
}
}

View File

@ -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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
.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
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
.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
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
.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
.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
.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
.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
.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
.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
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")

View File

@ -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;