1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-19 18:38:03 -05:00

[PM-21034] Database changes for signature keypairs (#5906)

* Add signing key repositories, models, and sql migration scripts

* Rename UserSigningKeys table to UserSigningKey

* Rename signedpublickeyownershipclaim to signedpublickey

* Move signedPublicKey to last parameter

* Add newline at end of file

* Rename to signature key pair

* Further rename to signaturekeypair

* Rename to UserSignatureKeyPairRepository

* Add newline

* Rename more instances to UserSignatureKeyPair

* Update parameter order

* Fix order

* Add more renames

* Cleanup

* Fix sql

* Add ef migrations

* Fix difference in SQL SP compared to migration SP

* Fix difference in SQL SP vs migration

* Fix difference in SQL SP vs migration

* Attempt to fix sql

* Rename migration to start later

* Address feedback

* Move UserSignatureKeyPair to KM codeownership

* Fix build

* Fix build

* Fix build

* Move out entitytypeconfiguration

* Use view for reading usersignaturekeypairs

* Fix migration script

* Fix migration script

* Drop view if exists

* Enable nullable

* Replace with create or alter view

* Switch go generatecomb

* Switch to generatecomb

* Move signature algorithm

* Move useresignaturekeypairentitytypeconfiguration to km ownership

* Move userSignatureKeyPair model

* Unswap file names

* Move sql files to km ownership

* Add index on userid for signature keys

* Fix wrong filename

* Remove string length limit

* Regenerate EF migrations

* Undo changes to program.cs

* Update util/Migrator/DbScripts/2025-06-01_00_AddSignatureKeyPairTable.sql

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>

* Update util/Migrator/DbScripts/2025-06-01_00_AddSignatureKeyPairTable.sql

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>

* Update util/Migrator/DbScripts/2025-06-01_00_AddSignatureKeyPairTable.sql

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>

* Update util/Migrator/DbScripts/2025-06-01_00_AddSignatureKeyPairTable.sql

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>

* Rename dbset to plural

* Update src/Infrastructure.EntityFramework/KeyManagement/Repositories/UserSignatureKeyPairRepository.cs

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>

---------

Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com>
This commit is contained in:
Bernd Schoolmann 2025-06-18 20:02:55 +02:00 committed by GitHub
parent 91b4ef756b
commit 2b3d92b789
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 10490 additions and 6 deletions

View File

@ -21,6 +21,9 @@ public class User : ITableObject<Guid>, IStorableSubscriber, IRevisable, ITwoFac
[MaxLength(256)] [MaxLength(256)]
public string Email { get; set; } = null!; public string Email { get; set; } = null!;
public bool EmailVerified { get; set; } public bool EmailVerified { get; set; }
/// <summary>
/// The server-side master-password hash
/// </summary>
[MaxLength(300)] [MaxLength(300)]
public string? MasterPassword { get; set; } public string? MasterPassword { get; set; }
[MaxLength(50)] [MaxLength(50)]
@ -41,9 +44,22 @@ public class User : ITableObject<Guid>, IStorableSubscriber, IRevisable, ITwoFac
/// organization membership. /// organization membership.
/// </summary> /// </summary>
public DateTime AccountRevisionDate { get; set; } = DateTime.UtcNow; public DateTime AccountRevisionDate { get; set; } = DateTime.UtcNow;
/// <summary>
/// The master-password-sealed user key.
/// </summary>
public string? Key { get; set; } public string? Key { get; set; }
/// <summary>
/// The raw public key, without a signature from the user's signature key.
/// </summary>
public string? PublicKey { get; set; } public string? PublicKey { get; set; }
/// <summary>
/// User key wrapped private key.
/// </summary>
public string? PrivateKey { get; set; } public string? PrivateKey { get; set; }
/// <summary>
/// The public key, signed by the user's signature key.
/// </summary>
public string? SignedPublicKey { get; set; }
public bool Premium { get; set; } public bool Premium { get; set; }
public DateTime? PremiumExpirationDate { get; set; } public DateTime? PremiumExpirationDate { get; set; }
public DateTime? RenewalReminderDate { get; set; } public DateTime? RenewalReminderDate { get; set; }

View File

@ -0,0 +1,25 @@
using Bit.Core.Entities;
using Bit.Core.KeyManagement.Enums;
using Bit.Core.Utilities;
#nullable enable
namespace Bit.Core.KeyManagement.Entities;
public class UserSignatureKeyPair : ITableObject<Guid>, IRevisable
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public SignatureAlgorithm SignatureAlgorithm { get; set; }
required public string VerifyingKey { get; set; }
required public string SigningKey { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
public DateTime RevisionDate { get; set; } = DateTime.UtcNow;
public void SetNewId()
{
Id = CoreHelpers.GenerateComb();
}
}

View File

@ -0,0 +1,9 @@
namespace Bit.Core.KeyManagement.Enums;
// <summary>
// Represents the algorithm / digital signature scheme used for a signature key pair.
// </summary>
public enum SignatureAlgorithm : byte
{
Ed25519 = 0
}

View File

@ -0,0 +1,22 @@
#nullable enable
using System.Text.Json.Serialization;
using Bit.Core.KeyManagement.Enums;
namespace Bit.Core.KeyManagement.Models.Data;
public class SignatureKeyPairData
{
public required SignatureAlgorithm SignatureAlgorithm { get; set; }
public required string WrappedSigningKey { get; set; }
public required string VerifyingKey { get; set; }
[JsonConstructor]
[System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute]
public SignatureKeyPairData(SignatureAlgorithm signatureAlgorithm, string wrappedSigningKey, string verifyingKey)
{
SignatureAlgorithm = signatureAlgorithm;
WrappedSigningKey = wrappedSigningKey ?? throw new ArgumentNullException(nameof(wrappedSigningKey));
VerifyingKey = verifyingKey ?? throw new ArgumentNullException(nameof(verifyingKey));
}
}

View File

@ -0,0 +1,15 @@
#nullable enable
using Bit.Core.KeyManagement.Entities;
using Bit.Core.KeyManagement.Models.Data;
using Bit.Core.KeyManagement.UserKey;
using Bit.Core.Repositories;
namespace Bit.Core.KeyManagement.Repositories;
public interface IUserSignatureKeyPairRepository : IRepository<UserSignatureKeyPair, Guid>
{
public Task<SignatureKeyPairData?> GetByUserIdAsync(Guid userId);
public UpdateEncryptedDataForKeyRotation UpdateForKeyRotation(Guid grantorId, SignatureKeyPairData signatureKeyPair);
public UpdateEncryptedDataForKeyRotation SetUserSignatureKeyPair(Guid userId, SignatureKeyPairData signatureKeyPair);
}

View File

@ -70,6 +70,7 @@ public static class DapperServiceCollectionExtensions
services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>(); services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>();
services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>(); services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>();
services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>(); services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>();
services.AddSingleton<IUserSignatureKeyPairRepository, UserSignatureKeyPairRepository>();
services.AddSingleton<IOrganizationMemberBaseDetailRepository, OrganizationMemberBaseDetailRepository>(); services.AddSingleton<IOrganizationMemberBaseDetailRepository, OrganizationMemberBaseDetailRepository>();
if (selfHosted) if (selfHosted)

View File

@ -0,0 +1,80 @@
#nullable enable
using System.Data;
using Bit.Core.KeyManagement.Entities;
using Bit.Core.KeyManagement.Models.Data;
using Bit.Core.KeyManagement.Repositories;
using Bit.Core.KeyManagement.UserKey;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using Bit.Infrastructure.Dapper.Repositories;
using Dapper;
using Microsoft.Data.SqlClient;
namespace Bit.Infrastructure.Dapper.KeyManagement.Repositories;
public class UserSignatureKeyPairRepository : Repository<UserSignatureKeyPair, Guid>, IUserSignatureKeyPairRepository
{
public UserSignatureKeyPairRepository(GlobalSettings globalSettings)
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
{
}
public UserSignatureKeyPairRepository(string connectionString, string readOnlyConnectionString) : base(
connectionString, readOnlyConnectionString)
{
}
public async Task<SignatureKeyPairData?> GetByUserIdAsync(Guid userId)
{
using (var connection = new SqlConnection(ConnectionString))
{
return await connection.QuerySingleOrDefaultAsync<SignatureKeyPairData>(
"[dbo].[UserSignatureKeyPair_ReadByUserId]",
new
{
UserId = userId
},
commandType: CommandType.StoredProcedure);
}
}
public UpdateEncryptedDataForKeyRotation SetUserSignatureKeyPair(Guid userId, SignatureKeyPairData signingKeys)
{
return async (SqlConnection connection, SqlTransaction transaction) =>
{
await connection.QueryAsync(
"[dbo].[UserSignatureKeyPair_SetForRotation]",
new
{
Id = CoreHelpers.GenerateComb(),
UserId = userId,
SignatureAlgorithm = (byte)signingKeys.SignatureAlgorithm,
SigningKey = signingKeys.WrappedSigningKey,
signingKeys.VerifyingKey,
CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow
},
commandType: CommandType.StoredProcedure,
transaction: transaction);
};
}
public UpdateEncryptedDataForKeyRotation UpdateForKeyRotation(Guid grantorId, SignatureKeyPairData signingKeys)
{
return async (SqlConnection connection, SqlTransaction transaction) =>
{
await connection.QueryAsync(
"[dbo].[UserSignatureKeyPair_UpdateForRotation]",
new
{
UserId = grantorId,
SignatureAlgorithm = (byte)signingKeys.SignatureAlgorithm,
SigningKey = signingKeys.WrappedSigningKey,
signingKeys.VerifyingKey,
RevisionDate = DateTime.UtcNow
},
commandType: CommandType.StoredProcedure,
transaction: transaction);
};
}
}

View File

@ -107,6 +107,7 @@ public static class EntityFrameworkServiceCollectionExtensions
services.AddSingleton<IPasswordHealthReportApplicationRepository, PasswordHealthReportApplicationRepository>(); services.AddSingleton<IPasswordHealthReportApplicationRepository, PasswordHealthReportApplicationRepository>();
services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>(); services.AddSingleton<ISecurityTaskRepository, SecurityTaskRepository>();
services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>(); services.AddSingleton<IUserAsymmetricKeysRepository, UserAsymmetricKeysRepository>();
services.AddSingleton<IUserSignatureKeyPairRepository, UserSignatureKeyPairRepository>();
services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>(); services.AddSingleton<IOrganizationInstallationRepository, OrganizationInstallationRepository>();
services.AddSingleton<IOrganizationMemberBaseDetailRepository, OrganizationMemberBaseDetailRepository>(); services.AddSingleton<IOrganizationMemberBaseDetailRepository, OrganizationMemberBaseDetailRepository>();

View File

@ -0,0 +1,17 @@
using Bit.Infrastructure.EntityFramework.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Bit.Infrastructure.EntityFramework.Configurations;
public class UserSignatureKeyPairEntityTypeConfiguration : IEntityTypeConfiguration<UserSignatureKeyPair>
{
public void Configure(EntityTypeBuilder<UserSignatureKeyPair> builder)
{
builder
.Property(s => s.Id)
.ValueGeneratedNever();
builder.ToTable(nameof(UserSignatureKeyPair));
}
}

View File

@ -0,0 +1,16 @@
using AutoMapper;
namespace Bit.Infrastructure.EntityFramework.Models;
public class UserSignatureKeyPair : Core.KeyManagement.Entities.UserSignatureKeyPair
{
public virtual User User { get; set; }
}
public class UserSignatureKeyPairMapperProfile : Profile
{
public UserSignatureKeyPairMapperProfile()
{
CreateMap<Core.KeyManagement.Entities.UserSignatureKeyPair, UserSignatureKeyPair>().ReverseMap();
}
}

View File

@ -0,0 +1,70 @@
#nullable enable
using AutoMapper;
using Bit.Core.KeyManagement.Models.Data;
using Bit.Core.KeyManagement.Repositories;
using Bit.Core.KeyManagement.UserKey;
using Bit.Core.Utilities;
using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Infrastructure.EntityFramework.KeyManagement.Repositories;
public class UserSignatureKeyPairRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper) : Repository<Core.KeyManagement.Entities.UserSignatureKeyPair, Models.UserSignatureKeyPair, Guid>(serviceScopeFactory, mapper, context => context.UserSignatureKeyPairs), IUserSignatureKeyPairRepository
{
public async Task<SignatureKeyPairData?> GetByUserIdAsync(Guid userId)
{
await using var scope = ServiceScopeFactory.CreateAsyncScope();
var dbContext = GetDatabaseContext(scope);
var signingKeys = await dbContext.UserSignatureKeyPairs.FirstOrDefaultAsync(x => x.UserId == userId);
if (signingKeys == null)
{
return null;
}
return new SignatureKeyPairData(
signingKeys.SignatureAlgorithm,
signingKeys.SigningKey,
signingKeys.VerifyingKey
);
}
public UpdateEncryptedDataForKeyRotation SetUserSignatureKeyPair(Guid userId, SignatureKeyPairData signingKeys)
{
return async (_, _) =>
{
await using var scope = ServiceScopeFactory.CreateAsyncScope();
var dbContext = GetDatabaseContext(scope);
var entity = new Models.UserSignatureKeyPair
{
Id = CoreHelpers.GenerateComb(),
UserId = userId,
SignatureAlgorithm = signingKeys.SignatureAlgorithm,
SigningKey = signingKeys.WrappedSigningKey,
VerifyingKey = signingKeys.VerifyingKey,
CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow,
};
await dbContext.UserSignatureKeyPairs.AddAsync(entity);
await dbContext.SaveChangesAsync();
};
}
public UpdateEncryptedDataForKeyRotation UpdateForKeyRotation(Guid grantorId, SignatureKeyPairData signingKeys)
{
return async (_, _) =>
{
await using var scope = ServiceScopeFactory.CreateAsyncScope();
var dbContext = GetDatabaseContext(scope);
var entity = await dbContext.UserSignatureKeyPairs.FirstOrDefaultAsync(x => x.UserId == grantorId);
if (entity != null)
{
entity.SignatureAlgorithm = signingKeys.SignatureAlgorithm;
entity.SigningKey = signingKeys.WrappedSigningKey;
entity.VerifyingKey = signingKeys.VerifyingKey;
entity.RevisionDate = DateTime.UtcNow;
await dbContext.SaveChangesAsync();
}
};
}
}

View File

@ -72,6 +72,7 @@ public class DatabaseContext : DbContext
public DbSet<TaxRate> TaxRates { get; set; } public DbSet<TaxRate> TaxRates { get; set; }
public DbSet<Transaction> Transactions { get; set; } public DbSet<Transaction> Transactions { get; set; }
public DbSet<User> Users { get; set; } public DbSet<User> Users { get; set; }
public DbSet<UserSignatureKeyPair> UserSignatureKeyPairs { get; set; }
public DbSet<AuthRequest> AuthRequests { get; set; } public DbSet<AuthRequest> AuthRequests { get; set; }
public DbSet<OrganizationDomain> OrganizationDomains { get; set; } public DbSet<OrganizationDomain> OrganizationDomains { get; set; }
public DbSet<WebAuthnCredential> WebAuthnCredentials { get; set; } public DbSet<WebAuthnCredential> WebAuthnCredentials { get; set; }

View File

@ -0,0 +1,8 @@
CREATE PROCEDURE [dbo].[UserSignatureKeyPair_ReadByUserId]
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SELECT *
FROM [dbo].[UserSignatureKeyPairView]
WHERE [UserId] = @UserId;
END

View File

@ -0,0 +1,13 @@
CREATE PROCEDURE [dbo].[UserSignatureKeyPair_SetForRotation]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@SignatureKeyPairAlgorithm TINYINT,
@SigningKey VARCHAR(MAX),
@VerifyingKey VARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
INSERT INTO [dbo].[UserSignatureKeyPair] ([Id], [UserId], [SignatureKeyPairAlgorithm], [SigningKey], [VerifyingKey], [CreationDate], [RevisionDate])
VALUES (@Id, @UserId, @SignatureKeyPairAlgorithm, @SigningKey, @VerifyingKey, @CreationDate, @RevisionDate)
END

View File

@ -0,0 +1,15 @@
CREATE PROCEDURE [dbo].[UserSignatureKeyPair_UpdateForRotation]
@UserId UNIQUEIDENTIFIER,
@SignatureKeyPairAlgorithm TINYINT,
@SigningKey VARCHAR(MAX),
@VerifyingKey VARCHAR(MAX),
@RevisionDate DATETIME2(7)
AS
BEGIN
UPDATE [dbo].[UserSignatureKeyPair]
SET [SignatureKeyPairAlgorithm] = @SignatureKeyPairAlgorithm,
[SigningKey] = @SigningKey,
[VerifyingKey] = @VerifyingKey,
[RevisionDate] = @RevisionDate
WHERE [UserId] = @UserId;
END

View File

@ -0,0 +1,16 @@
CREATE TABLE [dbo].[UserSignatureKeyPair] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[UserId] UNIQUEIDENTIFIER NOT NULL,
[SignatureKeyPairAlgorithm] TINYINT NOT NULL,
[SigningKey] VARCHAR(MAX) NOT NULL,
[VerifyingKey] VARCHAR(MAX) NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_UserSignatureKeyPair] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_UserSignatureKeyPair_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
);
GO
CREATE NONCLUSTERED INDEX [IX_UserSignatureKeyPair_UserId]
ON [dbo].[UserSignatureKeyPair]([UserId] ASC);
GO

View File

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

View File

@ -41,7 +41,8 @@
@LastKdfChangeDate DATETIME2(7) = NULL, @LastKdfChangeDate DATETIME2(7) = NULL,
@LastKeyRotationDate DATETIME2(7) = NULL, @LastKeyRotationDate DATETIME2(7) = NULL,
@LastEmailChangeDate DATETIME2(7) = NULL, @LastEmailChangeDate DATETIME2(7) = NULL,
@VerifyDevices BIT = 1 @VerifyDevices BIT = 1,
@SignedPublicKey NVARCHAR(MAX) = NULL
AS AS
BEGIN BEGIN
SET NOCOUNT ON SET NOCOUNT ON
@ -90,7 +91,8 @@ BEGIN
[LastKdfChangeDate], [LastKdfChangeDate],
[LastKeyRotationDate], [LastKeyRotationDate],
[LastEmailChangeDate], [LastEmailChangeDate],
[VerifyDevices] [VerifyDevices],
[SignedPublicKey]
) )
VALUES VALUES
( (
@ -136,6 +138,7 @@ BEGIN
@LastKdfChangeDate, @LastKdfChangeDate,
@LastKeyRotationDate, @LastKeyRotationDate,
@LastEmailChangeDate, @LastEmailChangeDate,
@VerifyDevices @VerifyDevices,
@SignedPublicKey
) )
END END

View File

@ -41,7 +41,8 @@
@LastKdfChangeDate DATETIME2(7) = NULL, @LastKdfChangeDate DATETIME2(7) = NULL,
@LastKeyRotationDate DATETIME2(7) = NULL, @LastKeyRotationDate DATETIME2(7) = NULL,
@LastEmailChangeDate DATETIME2(7) = NULL, @LastEmailChangeDate DATETIME2(7) = NULL,
@VerifyDevices BIT = 1 @VerifyDevices BIT = 1,
@SignedPublicKey NVARCHAR(MAX) = NULL
AS AS
BEGIN BEGIN
SET NOCOUNT ON SET NOCOUNT ON
@ -90,7 +91,8 @@ BEGIN
[LastKdfChangeDate] = @LastKdfChangeDate, [LastKdfChangeDate] = @LastKdfChangeDate,
[LastKeyRotationDate] = @LastKeyRotationDate, [LastKeyRotationDate] = @LastKeyRotationDate,
[LastEmailChangeDate] = @LastEmailChangeDate, [LastEmailChangeDate] = @LastEmailChangeDate,
[VerifyDevices] = @VerifyDevices [VerifyDevices] = @VerifyDevices,
[SignedPublicKey] = @SignedPublicKey
WHERE WHERE
[Id] = @Id [Id] = @Id
END END

View File

@ -42,6 +42,7 @@
[LastKeyRotationDate] DATETIME2 (7) NULL, [LastKeyRotationDate] DATETIME2 (7) NULL,
[LastEmailChangeDate] DATETIME2 (7) NULL, [LastEmailChangeDate] DATETIME2 (7) NULL,
[VerifyDevices] BIT DEFAULT ((1)) NOT NULL, [VerifyDevices] BIT DEFAULT ((1)) NOT NULL,
[SignedPublicKey] VARCHAR (MAX) NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC) CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC)
); );

View File

@ -29,7 +29,8 @@ public class UserCompare : IEqualityComparer<User>
x.LicenseKey == y.LicenseKey && x.LicenseKey == y.LicenseKey &&
x.ApiKey == y.ApiKey && x.ApiKey == y.ApiKey &&
x.Kdf == y.Kdf && x.Kdf == y.Kdf &&
x.KdfIterations == y.KdfIterations; x.KdfIterations == y.KdfIterations &&
x.SignedPublicKey == y.SignedPublicKey;
} }
public int GetHashCode([DisallowNull] User obj) public int GetHashCode([DisallowNull] User obj)

View File

@ -0,0 +1,334 @@
IF OBJECT_ID('[dbo].[UserSignatureKeyPair]') IS NULL
BEGIN
CREATE TABLE [dbo].[UserSignatureKeyPair]
(
[Id] UNIQUEIDENTIFIER NOT NULL,
[UserId] UNIQUEIDENTIFIER NOT NULL,
[SignatureKeyPairAlgorithm] TINYINT NOT NULL,
[SigningKey] VARCHAR(MAX) NOT NULL,
[VerifyingKey] VARCHAR(MAX) NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_UserSignatureKeyPair] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_UserSignatureKeyPair_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
);
END
GO
IF NOT EXISTS(SELECT name
FROM sys.indexes
WHERE name = 'IX_UserSignatureKeyPair_UserId')
BEGIN
CREATE NONCLUSTERED INDEX [IX_UserSignatureKeyPair_UserId]
ON [dbo].[UserSignatureKeyPair]([UserId] ASC);
END
GO
CREATE OR ALTER VIEW [dbo].[UserSignatureKeyPairView]
AS
SELECT
*
FROM
[dbo].[UserSignatureKeyPair]
GO
CREATE OR ALTER PROCEDURE [dbo].[UserSignatureKeyPair_ReadByUserId]
@UserId UNIQUEIDENTIFIER
AS
BEGIN
SELECT *
FROM [dbo].[UserSignatureKeyPairView]
WHERE [UserId] = @UserId;
END
GO
CREATE OR ALTER PROCEDURE [dbo].[UserSignatureKeyPair_UpdateForRotation]
@UserId UNIQUEIDENTIFIER,
@SignatureKeyPairAlgorithm TINYINT,
@SigningKey VARCHAR(MAX),
@VerifyingKey VARCHAR(MAX),
@RevisionDate DATETIME2(7)
AS
BEGIN
UPDATE [dbo].[UserSignatureKeyPair]
SET [SignatureKeyPairAlgorithm] = @SignatureKeyPairAlgorithm,
[SigningKey] = @SigningKey,
[VerifyingKey] = @VerifyingKey,
[RevisionDate] = @RevisionDate
WHERE [UserId] = @UserId;
END
GO
CREATE OR ALTER PROCEDURE [dbo].[UserSignatureKeyPair_SetForRotation]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@SignatureKeyPairAlgorithm TINYINT,
@SigningKey VARCHAR(MAX),
@VerifyingKey VARCHAR(MAX),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
INSERT INTO [dbo].[UserSignatureKeyPair] ([Id], [UserId], [SignatureKeyPairAlgorithm], [SigningKey], [VerifyingKey], [CreationDate], [RevisionDate])
VALUES (@Id, @UserId, @SignatureKeyPairAlgorithm, @SigningKey, @VerifyingKey, @CreationDate, @RevisionDate)
END
GO
IF COL_LENGTH('[dbo].[User]', 'SignedPublicKey') IS NULL
BEGIN
ALTER TABLE
[dbo].[User]
ADD
[SignedPublicKey] VARCHAR(MAX) NULL;
END
GO
EXECUTE sp_refreshview 'dbo.UserView'
GO
CREATE OR ALTER PROCEDURE [dbo].[User_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@Name NVARCHAR(50),
@Email NVARCHAR(256),
@EmailVerified BIT,
@MasterPassword NVARCHAR(300),
@MasterPasswordHint NVARCHAR(50),
@Culture NVARCHAR(10),
@SecurityStamp NVARCHAR(50),
@TwoFactorProviders NVARCHAR(MAX),
@TwoFactorRecoveryCode NVARCHAR(32),
@EquivalentDomains NVARCHAR(MAX),
@ExcludedGlobalEquivalentDomains NVARCHAR(MAX),
@AccountRevisionDate DATETIME2(7),
@Key NVARCHAR(MAX),
@PublicKey NVARCHAR(MAX),
@PrivateKey NVARCHAR(MAX),
@Premium BIT,
@PremiumExpirationDate DATETIME2(7),
@RenewalReminderDate DATETIME2(7),
@Storage BIGINT,
@MaxStorageGb SMALLINT,
@Gateway TINYINT,
@GatewayCustomerId VARCHAR(50),
@GatewaySubscriptionId VARCHAR(50),
@ReferenceData VARCHAR(MAX),
@LicenseKey VARCHAR(100),
@Kdf TINYINT,
@KdfIterations INT,
@KdfMemory INT = NULL,
@KdfParallelism INT = NULL,
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@ApiKey VARCHAR(30),
@ForcePasswordReset BIT = 0,
@UsesKeyConnector BIT = 0,
@FailedLoginCount INT = 0,
@LastFailedLoginDate DATETIME2(7),
@AvatarColor VARCHAR(7) = NULL,
@LastPasswordChangeDate DATETIME2(7) = NULL,
@LastKdfChangeDate DATETIME2(7) = NULL,
@LastKeyRotationDate DATETIME2(7) = NULL,
@LastEmailChangeDate DATETIME2(7) = NULL,
@VerifyDevices BIT = 1,
@SignedPublicKey NVARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[User]
(
[Id],
[Name],
[Email],
[EmailVerified],
[MasterPassword],
[MasterPasswordHint],
[Culture],
[SecurityStamp],
[TwoFactorProviders],
[TwoFactorRecoveryCode],
[EquivalentDomains],
[ExcludedGlobalEquivalentDomains],
[AccountRevisionDate],
[Key],
[PublicKey],
[PrivateKey],
[Premium],
[PremiumExpirationDate],
[RenewalReminderDate],
[Storage],
[MaxStorageGb],
[Gateway],
[GatewayCustomerId],
[GatewaySubscriptionId],
[ReferenceData],
[LicenseKey],
[Kdf],
[KdfIterations],
[CreationDate],
[RevisionDate],
[ApiKey],
[ForcePasswordReset],
[UsesKeyConnector],
[FailedLoginCount],
[LastFailedLoginDate],
[AvatarColor],
[KdfMemory],
[KdfParallelism],
[LastPasswordChangeDate],
[LastKdfChangeDate],
[LastKeyRotationDate],
[LastEmailChangeDate],
[VerifyDevices],
[SignedPublicKey]
)
VALUES
(
@Id,
@Name,
@Email,
@EmailVerified,
@MasterPassword,
@MasterPasswordHint,
@Culture,
@SecurityStamp,
@TwoFactorProviders,
@TwoFactorRecoveryCode,
@EquivalentDomains,
@ExcludedGlobalEquivalentDomains,
@AccountRevisionDate,
@Key,
@PublicKey,
@PrivateKey,
@Premium,
@PremiumExpirationDate,
@RenewalReminderDate,
@Storage,
@MaxStorageGb,
@Gateway,
@GatewayCustomerId,
@GatewaySubscriptionId,
@ReferenceData,
@LicenseKey,
@Kdf,
@KdfIterations,
@CreationDate,
@RevisionDate,
@ApiKey,
@ForcePasswordReset,
@UsesKeyConnector,
@FailedLoginCount,
@LastFailedLoginDate,
@AvatarColor,
@KdfMemory,
@KdfParallelism,
@LastPasswordChangeDate,
@LastKdfChangeDate,
@LastKeyRotationDate,
@LastEmailChangeDate,
@VerifyDevices,
@SignedPublicKey
)
END
GO
CREATE OR ALTER PROCEDURE [dbo].[User_Update]
@Id UNIQUEIDENTIFIER,
@Name NVARCHAR(50),
@Email NVARCHAR(256),
@EmailVerified BIT,
@MasterPassword NVARCHAR(300),
@MasterPasswordHint NVARCHAR(50),
@Culture NVARCHAR(10),
@SecurityStamp NVARCHAR(50),
@TwoFactorProviders NVARCHAR(MAX),
@TwoFactorRecoveryCode NVARCHAR(32),
@EquivalentDomains NVARCHAR(MAX),
@ExcludedGlobalEquivalentDomains NVARCHAR(MAX),
@AccountRevisionDate DATETIME2(7),
@Key NVARCHAR(MAX),
@PublicKey NVARCHAR(MAX),
@PrivateKey NVARCHAR(MAX),
@Premium BIT,
@PremiumExpirationDate DATETIME2(7),
@RenewalReminderDate DATETIME2(7),
@Storage BIGINT,
@MaxStorageGb SMALLINT,
@Gateway TINYINT,
@GatewayCustomerId VARCHAR(50),
@GatewaySubscriptionId VARCHAR(50),
@ReferenceData VARCHAR(MAX),
@LicenseKey VARCHAR(100),
@Kdf TINYINT,
@KdfIterations INT,
@KdfMemory INT = NULL,
@KdfParallelism INT = NULL,
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@ApiKey VARCHAR(30),
@ForcePasswordReset BIT = 0,
@UsesKeyConnector BIT = 0,
@FailedLoginCount INT,
@LastFailedLoginDate DATETIME2(7),
@AvatarColor VARCHAR(7),
@LastPasswordChangeDate DATETIME2(7) = NULL,
@LastKdfChangeDate DATETIME2(7) = NULL,
@LastKeyRotationDate DATETIME2(7) = NULL,
@LastEmailChangeDate DATETIME2(7) = NULL,
@VerifyDevices BIT = 1,
@SignedPublicKey NVARCHAR(MAX) = NULL
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[User]
SET
[Name] = @Name,
[Email] = @Email,
[EmailVerified] = @EmailVerified,
[MasterPassword] = @MasterPassword,
[MasterPasswordHint] = @MasterPasswordHint,
[Culture] = @Culture,
[SecurityStamp] = @SecurityStamp,
[TwoFactorProviders] = @TwoFactorProviders,
[TwoFactorRecoveryCode] = @TwoFactorRecoveryCode,
[EquivalentDomains] = @EquivalentDomains,
[ExcludedGlobalEquivalentDomains] = @ExcludedGlobalEquivalentDomains,
[AccountRevisionDate] = @AccountRevisionDate,
[Key] = @Key,
[PublicKey] = @PublicKey,
[PrivateKey] = @PrivateKey,
[Premium] = @Premium,
[PremiumExpirationDate] = @PremiumExpirationDate,
[RenewalReminderDate] = @RenewalReminderDate,
[Storage] = @Storage,
[MaxStorageGb] = @MaxStorageGb,
[Gateway] = @Gateway,
[GatewayCustomerId] = @GatewayCustomerId,
[GatewaySubscriptionId] = @GatewaySubscriptionId,
[ReferenceData] = @ReferenceData,
[LicenseKey] = @LicenseKey,
[Kdf] = @Kdf,
[KdfIterations] = @KdfIterations,
[KdfMemory] = @KdfMemory,
[KdfParallelism] = @KdfParallelism,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate,
[ApiKey] = @ApiKey,
[ForcePasswordReset] = @ForcePasswordReset,
[UsesKeyConnector] = @UsesKeyConnector,
[FailedLoginCount] = @FailedLoginCount,
[LastFailedLoginDate] = @LastFailedLoginDate,
[AvatarColor] = @AvatarColor,
[LastPasswordChangeDate] = @LastPasswordChangeDate,
[LastKdfChangeDate] = @LastKdfChangeDate,
[LastKeyRotationDate] = @LastKeyRotationDate,
[LastEmailChangeDate] = @LastEmailChangeDate,
[VerifyDevices] = @VerifyDevices,
[SignedPublicKey] = @SignedPublicKey
WHERE
[Id] = @Id
END
GO

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.MySqlMigrations.Migrations;
/// <inheritdoc />
public partial class UserSignatureKeyPair : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SignedPublicKey",
table: "User",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "UserSignatureKeyPair",
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"),
SignatureAlgorithm = table.Column<byte>(type: "tinyint unsigned", nullable: false),
VerifyingKey = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
SigningKey = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreationDate = table.Column<DateTime>(type: "datetime(6)", nullable: false),
RevisionDate = table.Column<DateTime>(type: "datetime(6)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserSignatureKeyPair", x => x.Id);
table.ForeignKey(
name: "FK_UserSignatureKeyPair_User_UserId",
column: x => x.UserId,
principalTable: "User",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_UserSignatureKeyPair_UserId",
table: "UserSignatureKeyPair",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserSignatureKeyPair");
migrationBuilder.DropColumn(
name: "SignedPublicKey",
table: "User");
}
}

View File

@ -1786,6 +1786,9 @@ namespace Bit.MySqlMigrations.Migrations
.HasMaxLength(50) .HasMaxLength(50)
.HasColumnType("varchar(50)"); .HasColumnType("varchar(50)");
b.Property<string>("SignedPublicKey")
.HasColumnType("longtext");
b.Property<long?>("Storage") b.Property<long?>("Storage")
.HasColumnType("bigint"); .HasColumnType("bigint");
@ -1814,6 +1817,38 @@ namespace Bit.MySqlMigrations.Migrations
b.ToTable("User", (string)null); b.ToTable("User", (string)null);
}); });
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b =>
{
b.Property<Guid>("Id")
.HasColumnType("char(36)");
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime(6)");
b.Property<DateTime>("RevisionDate")
.HasColumnType("datetime(6)");
b.Property<byte>("SignatureAlgorithm")
.HasColumnType("tinyint unsigned");
b.Property<string>("SigningKey")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("UserId")
.HasColumnType("char(36)");
b.Property<string>("VerifyingKey")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserSignatureKeyPair", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -2800,6 +2835,17 @@ namespace Bit.MySqlMigrations.Migrations
b.Navigation("User"); b.Navigation("User");
}); });
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b =>
{ {
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.PostgresMigrations.Migrations;
/// <inheritdoc />
public partial class UserSignatureKeyPair : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SignedPublicKey",
table: "User",
type: "text",
nullable: true);
migrationBuilder.CreateTable(
name: "UserSignatureKeyPair",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
UserId = table.Column<Guid>(type: "uuid", nullable: false),
SignatureAlgorithm = table.Column<byte>(type: "smallint", nullable: false),
VerifyingKey = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
SigningKey = table.Column<string>(type: "character varying(500)", maxLength: 500, nullable: false),
CreationDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
RevisionDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserSignatureKeyPair", x => x.Id);
table.ForeignKey(
name: "FK_UserSignatureKeyPair_User_UserId",
column: x => x.UserId,
principalTable: "User",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_UserSignatureKeyPair_UserId",
table: "UserSignatureKeyPair",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserSignatureKeyPair");
migrationBuilder.DropColumn(
name: "SignedPublicKey",
table: "User");
}
}

View File

@ -1792,6 +1792,9 @@ namespace Bit.PostgresMigrations.Migrations
.HasMaxLength(50) .HasMaxLength(50)
.HasColumnType("character varying(50)"); .HasColumnType("character varying(50)");
b.Property<string>("SignedPublicKey")
.HasColumnType("text");
b.Property<long?>("Storage") b.Property<long?>("Storage")
.HasColumnType("bigint"); .HasColumnType("bigint");
@ -1820,6 +1823,38 @@ namespace Bit.PostgresMigrations.Migrations
b.ToTable("User", (string)null); b.ToTable("User", (string)null);
}); });
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTime>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("RevisionDate")
.HasColumnType("timestamp with time zone");
b.Property<byte>("SignatureAlgorithm")
.HasColumnType("smallint");
b.Property<string>("SigningKey")
.IsRequired()
.HasColumnType("text");
b.Property<Guid>("UserId")
.HasColumnType("uuid");
b.Property<string>("VerifyingKey")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserSignatureKeyPair", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -2806,6 +2841,17 @@ namespace Bit.PostgresMigrations.Migrations
b.Navigation("User"); b.Navigation("User");
}); });
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b =>
{ {
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.SqliteMigrations.Migrations;
/// <inheritdoc />
public partial class UserSignatureKeyPair : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "SignedPublicKey",
table: "User",
type: "TEXT",
nullable: true);
migrationBuilder.CreateTable(
name: "UserSignatureKeyPair",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
UserId = table.Column<Guid>(type: "TEXT", nullable: false),
SignatureAlgorithm = table.Column<byte>(type: "INTEGER", nullable: false),
VerifyingKey = table.Column<string>(type: "TEXT", maxLength: 500, nullable: false),
SigningKey = table.Column<string>(type: "TEXT", maxLength: 500, nullable: false),
CreationDate = table.Column<DateTime>(type: "TEXT", nullable: false),
RevisionDate = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserSignatureKeyPair", x => x.Id);
table.ForeignKey(
name: "FK_UserSignatureKeyPair_User_UserId",
column: x => x.UserId,
principalTable: "User",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_UserSignatureKeyPair_UserId",
table: "UserSignatureKeyPair",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserSignatureKeyPair");
migrationBuilder.DropColumn(
name: "SignedPublicKey",
table: "User");
}
}

View File

@ -1775,6 +1775,9 @@ namespace Bit.SqliteMigrations.Migrations
.HasMaxLength(50) .HasMaxLength(50)
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("SignedPublicKey")
.HasColumnType("TEXT");
b.Property<long?>("Storage") b.Property<long?>("Storage")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -1803,6 +1806,38 @@ namespace Bit.SqliteMigrations.Migrations
b.ToTable("User", (string)null); b.ToTable("User", (string)null);
}); });
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b =>
{
b.Property<Guid>("Id")
.HasColumnType("TEXT");
b.Property<DateTime>("CreationDate")
.HasColumnType("TEXT");
b.Property<DateTime>("RevisionDate")
.HasColumnType("TEXT");
b.Property<byte>("SignatureAlgorithm")
.HasColumnType("INTEGER");
b.Property<string>("SigningKey")
.IsRequired()
.HasColumnType("TEXT");
b.Property<Guid>("UserId")
.HasColumnType("TEXT");
b.Property<string>("VerifyingKey")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserSignatureKeyPair", (string)null);
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -2789,6 +2824,17 @@ namespace Bit.SqliteMigrations.Migrations
b.Navigation("User"); b.Navigation("User");
}); });
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Models.UserSignatureKeyPair", b =>
{
b.HasOne("Bit.Infrastructure.EntityFramework.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b => modelBuilder.Entity("Bit.Infrastructure.EntityFramework.NotificationCenter.Models.Notification", b =>
{ {
b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization") b.HasOne("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", "Organization")