1
0
mirror of https://github.com/bitwarden/server synced 2025-12-10 13:23:27 +00:00

[PM-22736] Send password hasher (#6112)

* feat: 
  - Add SendPasswordHasher class and interface
  - DI for SendPasswordHasher to use Marker class allowing us to use custom options for the SendPasswordHasher without impacting other PasswordHashers.
* test: Unit tests for SendPasswordHasher implementation
* doc: docs for interface and comments

Co-authored-by: Maciej Zieniuk <167752252+mzieniukbw@users.noreply.github.com>
This commit is contained in:
Ike
2025-07-24 12:49:15 -04:00
committed by GitHub
parent 76d1a2e875
commit 05398ad8a4
7 changed files with 208 additions and 1 deletions

View File

@@ -0,0 +1,103 @@
using Bit.Core.KeyManagement.Sends;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using Microsoft.AspNetCore.Identity;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.KeyManagement.Sends;
[SutProviderCustomize]
public class SendPasswordHasherTests
{
[Theory]
[BitAutoData(PasswordVerificationResult.Success)]
[BitAutoData(PasswordVerificationResult.SuccessRehashNeeded)]
void VerifyPasswordHash_WithValidMatching_ReturnsTrue(
PasswordVerificationResult passwordVerificationResult,
SutProvider<SendPasswordHasher> sutProvider,
string sendPasswordHash,
string inputPasswordHash)
{
// Arrange
sutProvider.GetDependency<IPasswordHasher<SendPasswordHasherMarker>>()
.VerifyHashedPassword(Arg.Any<SendPasswordHasherMarker>(), sendPasswordHash, inputPasswordHash)
.Returns(passwordVerificationResult);
// Act
var result = sutProvider.Sut.PasswordHashMatches(sendPasswordHash, inputPasswordHash);
// Assert
Assert.True(result);
sutProvider.GetDependency<IPasswordHasher<SendPasswordHasherMarker>>()
.Received(1)
.VerifyHashedPassword(Arg.Any<SendPasswordHasherMarker>(), sendPasswordHash, inputPasswordHash);
}
[Theory, BitAutoData]
void VerifyPasswordHash_WithNonMatchingPasswords_ReturnsFalse(
SutProvider<SendPasswordHasher> sutProvider,
string sendPasswordHash,
string inputPasswordHash)
{
// Arrange
sutProvider.GetDependency<IPasswordHasher<SendPasswordHasherMarker>>()
.VerifyHashedPassword(Arg.Any<SendPasswordHasherMarker>(), sendPasswordHash, inputPasswordHash)
.Returns(PasswordVerificationResult.Failed);
// Act
var result = sutProvider.Sut.PasswordHashMatches(sendPasswordHash, inputPasswordHash);
// Assert
Assert.False(result);
sutProvider.GetDependency<IPasswordHasher<SendPasswordHasherMarker>>()
.Received(1)
.VerifyHashedPassword(Arg.Any<SendPasswordHasherMarker>(), sendPasswordHash, inputPasswordHash);
}
[Theory]
[InlineData(null, "inputPassword")]
[InlineData("", "inputPassword")]
[InlineData(" ", "inputPassword")]
[InlineData("sendPassword", null)]
[InlineData("sendPassword", "")]
[InlineData("sendPassword", " ")]
[InlineData(null, null)]
[InlineData("", "")]
public void VerifyPasswordHash_WithNullOrEmptyParameters_ReturnsFalse(
string? sendPasswordHash,
string? inputPasswordHash)
{
// Arrange
var passwordHasher = Substitute.For<IPasswordHasher<SendPasswordHasherMarker>>();
var sut = new SendPasswordHasher(passwordHasher);
// Act
var result = sut.PasswordHashMatches(sendPasswordHash, inputPasswordHash);
// Assert
Assert.False(result);
passwordHasher.DidNotReceive().VerifyHashedPassword(Arg.Any<SendPasswordHasherMarker>(), Arg.Any<string>(), Arg.Any<string>());
}
[Theory, BitAutoData]
void HashPasswordHash_WithValidInput_ReturnsHashedPassword(
SutProvider<SendPasswordHasher> sutProvider,
string clientHashedPassword,
string expectedHashedResult)
{
// Arrange
sutProvider.GetDependency<IPasswordHasher<SendPasswordHasherMarker>>()
.HashPassword(Arg.Any<SendPasswordHasherMarker>(), clientHashedPassword)
.Returns(expectedHashedResult);
// Act
var result = sutProvider.Sut.HashOfClientPasswordHash(clientHashedPassword);
// Assert
Assert.Equal(expectedHashedResult, result);
sutProvider.GetDependency<IPasswordHasher<SendPasswordHasherMarker>>()
.Received(1)
.HashPassword(Arg.Any<SendPasswordHasherMarker>(), clientHashedPassword);
}
}