1
0
mirror of https://github.com/bitwarden/server synced 2026-02-25 08:53:21 +00:00

models, entity, and stored procs updated to work with EmailHashes with migrations

This commit is contained in:
John Harrington
2026-01-17 16:48:41 -07:00
parent aa33a67aee
commit ee9995616e
16 changed files with 10806 additions and 16 deletions

View File

@@ -102,9 +102,17 @@ public class SendRequestModel
/// Comma-separated list of emails that may access the send using OTP
/// authentication. Mutually exclusive with <see cref="Password"/>.
/// </summary>
[StringLength(4000)]
[EncryptedString]
[EncryptedStringLength(4000)]
public string Emails { get; set; }
/// <summary>
/// Comma-separated list of email **hashes** that may access the send using OTP
/// authentication. Mutually exclusive with <see cref="Password"/>.
/// </summary>
[StringLength(1000)]
public string EmailHashes { get; set; }
/// <summary>
/// When <see langword="true"/>, send access is disabled.
/// Defaults to <see langword="false"/>.
@@ -253,6 +261,7 @@ public class SendRequestModel
// normalize encoding
var emails = Emails.Split(',', RemoveEmptyEntries | TrimEntries);
existingSend.Emails = string.Join(",", emails);
existingSend.EmailHashes = EmailHashes;
existingSend.Password = null;
existingSend.AuthType = Core.Tools.Enums.AuthType.Email;
}

View File

@@ -47,6 +47,7 @@ public class SendResponseModel : ResponseModel
DeletionDate = send.DeletionDate;
Password = send.Password;
Emails = send.Emails;
EmailHashes = send.EmailHashes;
Disabled = send.Disabled;
HideEmail = send.HideEmail.GetValueOrDefault();
@@ -154,6 +155,12 @@ public class SendResponseModel : ResponseModel
/// </summary>
public string Emails { get; set; }
/// <summary>
/// Comma-separated list of email **hashes** that may access the send using OTP
/// authentication. Mutually exclusive with <see cref="Password"/>.
/// </summary>
public string EmailHashes { get; set; }
/// <summary>
/// When <see langword="true"/>, send access is disabled.
/// </summary>

View File

@@ -81,6 +81,15 @@ public class Send : ITableObject<Guid>
[MaxLength(4000)]
public string? Emails { get; set; }
/// <summary>
/// Comma-separated list of email **hashes** for OTP authentication.
/// </summary>
/// <remarks>
/// This field is mutually exclusive with <see cref="Password" />
/// </remarks>
[MaxLength(4000)]
public string? EmailHashes { get; set; }
/// <summary>
/// The send becomes unavailable to API callers when
/// <see cref="AccessCount"/> &gt;= <see cref="MaxAccessCount"/>.

View File

@@ -18,7 +18,8 @@
-- FIXME: remove null default value once this argument has been
-- in 2 server releases
@Emails NVARCHAR(4000) = NULL,
@AuthType TINYINT = NULL
@AuthType TINYINT = NULL,
@EmailHashes NVARCHAR(4000) = NULL
AS
BEGIN
SET NOCOUNT ON
@@ -42,7 +43,8 @@ BEGIN
[HideEmail],
[CipherId],
[Emails],
[AuthType]
[AuthType],
[EmailHashes]
)
VALUES
(
@@ -63,7 +65,8 @@ BEGIN
@HideEmail,
@CipherId,
@Emails,
@AuthType
@AuthType,
@EmailHashes
)
IF @UserId IS NOT NULL

View File

@@ -16,7 +16,8 @@
@HideEmail BIT,
@CipherId UNIQUEIDENTIFIER = NULL,
@Emails NVARCHAR(4000) = NULL,
@AuthType TINYINT = NULL
@AuthType TINYINT = NULL,
@EmailHashes NVARCHAR(4000) = NULL
AS
BEGIN
SET NOCOUNT ON
@@ -40,7 +41,8 @@ BEGIN
[HideEmail] = @HideEmail,
[CipherId] = @CipherId,
[Emails] = @Emails,
[AuthType] = @AuthType
[AuthType] = @AuthType,
[EmailHashes] = @EmailHashes
WHERE
[Id] = @Id

View File

@@ -1,22 +1,24 @@
CREATE TABLE [dbo].[Send] (
CREATE TABLE [dbo].[Send]
(
[Id] UNIQUEIDENTIFIER NOT NULL,
[UserId] UNIQUEIDENTIFIER NULL,
[OrganizationId] UNIQUEIDENTIFIER NULL,
[Type] TINYINT NOT NULL,
[Data] VARCHAR(MAX) NOT NULL,
[Key] VARCHAR (MAX) NOT NULL,
[Password] NVARCHAR (300) NULL,
[Emails] NVARCHAR (4000) NULL,
[Key] VARCHAR(MAX) NOT NULL,
[Password] NVARCHAR(300) NULL,
[Emails] NVARCHAR(4000) NULL,
[MaxAccessCount] INT NULL,
[AccessCount] INT NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[RevisionDate] DATETIME2 (7) NOT NULL,
[ExpirationDate] DATETIME2 (7) NULL,
[DeletionDate] DATETIME2 (7) NOT NULL,
[CreationDate] DATETIME2(7) NOT NULL,
[RevisionDate] DATETIME2(7) NOT NULL,
[ExpirationDate] DATETIME2(7) NULL,
[DeletionDate] DATETIME2(7) NOT NULL,
[Disabled] BIT NOT NULL,
[HideEmail] BIT NULL,
[CipherId] UNIQUEIDENTIFIER NULL,
[AuthType] TINYINT NULL,
[EmailHashes] NVARCHAR(4000) NULL,
CONSTRAINT [PK_Send] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Send_Organization] FOREIGN KEY ([OrganizationId]) REFERENCES [dbo].[Organization] ([Id]),
CONSTRAINT [FK_Send_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]),
@@ -26,9 +28,9 @@
GO
CREATE NONCLUSTERED INDEX [IX_Send_UserId_OrganizationId]
ON [dbo].[Send]([UserId] ASC, [OrganizationId] ASC);
ON [dbo].[Send] ([UserId] ASC, [OrganizationId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_Send_DeletionDate]
ON [dbo].[Send]([DeletionDate] ASC);
ON [dbo].[Send] ([DeletionDate] ASC);

View File

@@ -0,0 +1,148 @@
-- Update Send table to add EmailHashes Column
IF COL_LENGTH('[dbo].[Send]', 'EmailHashes') IS NULL
BEGIN
ALTER TABLE [dbo].[Send]
ADD [EmailHashes] NVARCHAR(4000) NULL;
END
GO
-- Update Send_Create to include EmailHashes column
CREATE OR ALTER PROCEDURE [dbo].[Send_Create]
@Id UNIQUEIDENTIFIER OUTPUT,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data VARCHAR(MAX),
@Key VARCHAR(MAX),
@Password NVARCHAR(300),
@MaxAccessCount INT,
@AccessCount INT,
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@ExpirationDate DATETIME2(7),
@DeletionDate DATETIME2(7),
@Disabled BIT,
@HideEmail BIT,
@CipherId UNIQUEIDENTIFIER = NULL,
@Emails NVARCHAR(4000) = NULL,
@AuthType TINYINT = NULL,
@EmailHashes NVARCHAR(4000) = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[Send]
(
[Id],
[UserId],
[OrganizationId],
[Type],
[Data],
[Key],
[Password],
[MaxAccessCount],
[AccessCount],
[CreationDate],
[RevisionDate],
[ExpirationDate],
[DeletionDate],
[Disabled],
[HideEmail],
[CipherId],
[Emails],
[AuthType],
[EmailHashes]
)
VALUES
(
@Id,
@UserId,
@OrganizationId,
@Type,
@Data,
@Key,
@Password,
@MaxAccessCount,
@AccessCount,
@CreationDate,
@RevisionDate,
@ExpirationDate,
@DeletionDate,
@Disabled,
@HideEmail,
@CipherId,
@Emails,
@AuthType,
@EmailHashes
)
IF @UserId IS NOT NULL
BEGIN
IF @Type = 1 --File
BEGIN
EXEC [dbo].[User_UpdateStorage] @UserId
END
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
END
-- TODO: OrganizationId bump?
END
GO
-- Update Send_Update to include EmailHashes column
CREATE OR ALTER PROCEDURE [dbo].[Send_Update]
@Id UNIQUEIDENTIFIER,
@UserId UNIQUEIDENTIFIER,
@OrganizationId UNIQUEIDENTIFIER,
@Type TINYINT,
@Data VARCHAR(MAX),
@Key VARCHAR(MAX),
@Password NVARCHAR(300),
@MaxAccessCount INT,
@AccessCount INT,
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7),
@ExpirationDate DATETIME2(7),
@DeletionDate DATETIME2(7),
@Disabled BIT,
@HideEmail BIT,
@CipherId UNIQUEIDENTIFIER = NULL,
@Emails NVARCHAR(4000) = NULL,
@AuthType TINYINT = NULL,
@EmailHashes NVARCHAR(4000) = NULL
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[Send]
SET
[UserId] = @UserId,
[OrganizationId] = @OrganizationId,
[Type] = @Type,
[Data] = @Data,
[Key] = @Key,
[Password] = @Password,
[MaxAccessCount] = @MaxAccessCount,
[AccessCount] = @AccessCount,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate,
[ExpirationDate] = @ExpirationDate,
[DeletionDate] = @DeletionDate,
[Disabled] = @Disabled,
[HideEmail] = @HideEmail,
[CipherId] = @CipherId,
[Emails] = @Emails,
[AuthType] = @AuthType,
[EmailHashes] = @EmailHashes
WHERE
[Id] = @Id
IF @UserId IS NOT NULL
BEGIN
EXEC [dbo].[User_BumpAccountRevisionDate] @UserId
END
-- TODO: OrganizationId bump?
END
GO
EXECUTE sp_refreshview N'[dbo].[SendView]'
GO

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.MySqlMigrations.Migrations;
/// <inheritdoc />
public partial class _20260117_00_Send_EmailHashes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "EmailHashes",
table: "Send",
type: "varchar(4000)",
maxLength: 4000,
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EmailHashes",
table: "Send");
}
}

View File

@@ -1689,6 +1689,10 @@ namespace Bit.MySqlMigrations.Migrations
b.Property<bool>("Disabled")
.HasColumnType("tinyint(1)");
b.Property<string>("EmailHashes")
.HasMaxLength(4000)
.HasColumnType("varchar(4000)");
b.Property<string>("Emails")
.HasMaxLength(4000)
.HasColumnType("varchar(4000)");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.PostgresMigrations.Migrations;
/// <inheritdoc />
public partial class _20260117_00_Send_EmailHashes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "EmailHashes",
table: "Send",
type: "character varying(4000)",
maxLength: 4000,
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EmailHashes",
table: "Send");
}
}

View File

@@ -1694,6 +1694,10 @@ namespace Bit.PostgresMigrations.Migrations
b.Property<bool>("Disabled")
.HasColumnType("boolean");
b.Property<string>("EmailHashes")
.HasMaxLength(4000)
.HasColumnType("character varying(4000)");
b.Property<string>("Emails")
.HasMaxLength(4000)
.HasColumnType("character varying(4000)");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.SqliteMigrations.Migrations;
/// <inheritdoc />
public partial class _20260117_00_Send_EmailHashes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "EmailHashes",
table: "Send",
type: "TEXT",
maxLength: 4000,
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EmailHashes",
table: "Send");
}
}

View File

@@ -1678,6 +1678,10 @@ namespace Bit.SqliteMigrations.Migrations
b.Property<bool>("Disabled")
.HasColumnType("INTEGER");
b.Property<string>("EmailHashes")
.HasMaxLength(4000)
.HasColumnType("TEXT");
b.Property<string>("Emails")
.HasMaxLength(4000)
.HasColumnType("TEXT");