1
0
mirror of https://github.com/bitwarden/server synced 2026-02-20 11:23:37 +00:00

[PM-30920] Server changes to encrypt send access email list (#6867)

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

* configure data protection for EmailHashes

* update SendAuthenticationQuery to use EmailHashes and perform validation

* respond to Claude's comments and update tests

* fix send.sql alignment

Co-authored-by: mkincaid-bw <mkincaid@bitwarden.com>

---------

Co-authored-by: Alex Dragovich <46065570+itsadrago@users.noreply.github.com>
Co-authored-by: mkincaid-bw <mkincaid@bitwarden.com>
This commit is contained in:
John Harrington
2026-01-28 07:13:25 -07:00
committed by GitHub
parent 2c39e336e0
commit fa06fe41ab
22 changed files with 11125 additions and 260 deletions

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

@@ -45,6 +45,6 @@ public record ResourcePassword(string Hash) : SendAuthenticationMethod;
/// Create a send claim by requesting a one time password (OTP) confirmation code.
/// </summary>
/// <param name="Emails">
/// The list of email addresses permitted access to the send.
/// The list of email address **hashes** permitted access to the send.
/// </param>
public record EmailOtp(string[] Emails) : SendAuthenticationMethod;

View File

@@ -37,8 +37,11 @@ public class SendAuthenticationQuery : ISendAuthenticationQuery
SendAuthenticationMethod method = send switch
{
null => NEVER_AUTHENTICATE,
var s when s.AccessCount >= s.MaxAccessCount => NEVER_AUTHENTICATE,
var s when s.AuthType == AuthType.Email && s.Emails is not null => emailOtp(s.Emails),
var s when s.Disabled => NEVER_AUTHENTICATE,
var s when s.AccessCount >= s.MaxAccessCount.GetValueOrDefault(int.MaxValue) => NEVER_AUTHENTICATE,
var s when s.ExpirationDate.GetValueOrDefault(DateTime.MaxValue) < DateTime.UtcNow => NEVER_AUTHENTICATE,
var s when s.DeletionDate <= DateTime.UtcNow => NEVER_AUTHENTICATE,
var s when s.AuthType == AuthType.Email && s.EmailHashes is not null => EmailOtp(s.EmailHashes),
var s when s.AuthType == AuthType.Password && s.Password is not null => new ResourcePassword(s.Password),
_ => NOT_AUTHENTICATED
};
@@ -46,9 +49,13 @@ public class SendAuthenticationQuery : ISendAuthenticationQuery
return method;
}
private EmailOtp emailOtp(string emails)
private static EmailOtp EmailOtp(string? emailHashes)
{
var list = emails.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (string.IsNullOrWhiteSpace(emailHashes))
{
return new EmailOtp([]);
}
var list = emailHashes.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
return new EmailOtp(list);
}
}