mirror of
https://github.com/bitwarden/server
synced 2025-12-16 08:13:33 +00:00
* feat: add static enumeration helper class * test: add enumeration helper class unit tests * feat: implement NeverAuthenticateValidator * test: unit and integration tests SendNeverAuthenticateValidator * test: use static class for common integration test setup for Send Access unit and integration tests * test: update tests to use static helper
281 lines
12 KiB
C#
281 lines
12 KiB
C#
using Bit.Core.Tools.Models.Data;
|
|
using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
|
|
using Bit.Test.Common.AutoFixture;
|
|
using Bit.Test.Common.AutoFixture.Attributes;
|
|
using Duende.IdentityModel;
|
|
using Duende.IdentityServer.Validation;
|
|
using Xunit;
|
|
|
|
namespace Bit.Identity.Test.IdentityServer.SendAccess;
|
|
|
|
[SutProviderCustomize]
|
|
public class SendNeverAuthenticateRequestValidatorTests
|
|
{
|
|
/// <summary>
|
|
/// To support the static hashing function <see cref="EnumerationProtectionHelpers.GetIndexForSaltHash"/> theses GUIDs and Key must be hardcoded
|
|
/// </summary>
|
|
private static readonly string _testHashKey = "test-key-123456789012345678901234567890";
|
|
// These Guids are static and ensure the correct index for each error type
|
|
private static readonly Guid _invalidSendGuid = Guid.Parse("1b35fbf3-a14a-4d48-82b7-2adc34fdae6f");
|
|
private static readonly Guid _emailSendGuid = Guid.Parse("bc8e2ef5-a0bd-44d2-bdb7-5902be6f5c41");
|
|
private static readonly Guid _passwordSendGuid = Guid.Parse("da36fa27-f0e8-4701-a585-d3d8c2f67c4b");
|
|
|
|
private static readonly NeverAuthenticate _authMethod = new();
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_GuidErrorSelected_ReturnsInvalidSendId(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(_invalidSendGuid);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = _testHashKey;
|
|
|
|
// Act
|
|
var result = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, _invalidSendGuid);
|
|
|
|
// Assert
|
|
Assert.True(result.IsError);
|
|
Assert.Equal(OidcConstants.TokenErrors.InvalidGrant, result.Error);
|
|
Assert.Equal(SendAccessConstants.SendIdGuidValidatorResults.InvalidSendId, result.ErrorDescription);
|
|
|
|
var customResponse = result.CustomResponse as Dictionary<string, object>;
|
|
Assert.NotNull(customResponse);
|
|
Assert.Equal(
|
|
SendAccessConstants.SendIdGuidValidatorResults.InvalidSendId, customResponse[SendAccessConstants.SendAccessError]);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_EmailErrorSelected_HasEmail_ReturnsEmailInvalid(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
|
|
string email)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(_emailSendGuid, sendEmail: email);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = _testHashKey;
|
|
|
|
// Act
|
|
var result = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, _emailSendGuid);
|
|
|
|
// Assert
|
|
Assert.True(result.IsError);
|
|
Assert.Equal(OidcConstants.TokenErrors.InvalidGrant, result.Error);
|
|
Assert.Equal(SendAccessConstants.EmailOtpValidatorResults.EmailInvalid, result.ErrorDescription);
|
|
|
|
var customResponse = result.CustomResponse as Dictionary<string, object>;
|
|
Assert.NotNull(customResponse);
|
|
Assert.Equal(SendAccessConstants.EmailOtpValidatorResults.EmailInvalid, customResponse[SendAccessConstants.SendAccessError]);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_EmailErrorSelected_NoEmail_ReturnsEmailRequired(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(_emailSendGuid);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = _testHashKey;
|
|
|
|
// Act
|
|
var result = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, _emailSendGuid);
|
|
|
|
// Assert
|
|
Assert.True(result.IsError);
|
|
Assert.Equal(OidcConstants.TokenErrors.InvalidRequest, result.Error);
|
|
Assert.Equal(SendAccessConstants.EmailOtpValidatorResults.EmailRequired, result.ErrorDescription);
|
|
|
|
var customResponse = result.CustomResponse as Dictionary<string, object>;
|
|
Assert.NotNull(customResponse);
|
|
Assert.Equal(SendAccessConstants.EmailOtpValidatorResults.EmailRequired, customResponse[SendAccessConstants.SendAccessError]);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_PasswordErrorSelected_HasPassword_ReturnsPasswordDoesNotMatch(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
|
|
string password)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(_passwordSendGuid, passwordHash: password);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = _testHashKey;
|
|
|
|
// Act
|
|
var result = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, _passwordSendGuid);
|
|
|
|
// Assert
|
|
Assert.True(result.IsError);
|
|
Assert.Equal(OidcConstants.TokenErrors.InvalidRequest, result.Error);
|
|
Assert.Equal(SendAccessConstants.PasswordValidatorResults.RequestPasswordDoesNotMatch, result.ErrorDescription);
|
|
|
|
var customResponse = result.CustomResponse as Dictionary<string, object>;
|
|
Assert.NotNull(customResponse);
|
|
Assert.Equal(SendAccessConstants.PasswordValidatorResults.RequestPasswordDoesNotMatch, customResponse[SendAccessConstants.SendAccessError]);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_PasswordErrorSelected_NoPassword_ReturnsPasswordRequired(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest)
|
|
{
|
|
// Arrange
|
|
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(_passwordSendGuid);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = _testHashKey;
|
|
|
|
// Act
|
|
var result = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, _passwordSendGuid);
|
|
|
|
// Assert
|
|
Assert.True(result.IsError);
|
|
Assert.Equal(OidcConstants.TokenErrors.InvalidGrant, result.Error);
|
|
Assert.Equal(SendAccessConstants.PasswordValidatorResults.RequestPasswordIsRequired, result.ErrorDescription);
|
|
|
|
var customResponse = result.CustomResponse as Dictionary<string, object>;
|
|
Assert.NotNull(customResponse);
|
|
Assert.Equal(SendAccessConstants.PasswordValidatorResults.RequestPasswordIsRequired, customResponse[SendAccessConstants.SendAccessError]);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_NullHashKey_UsesEmptyKey(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(_invalidSendGuid);
|
|
var context = new ExtensionGrantValidationContext { Request = tokenRequest };
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = null;
|
|
|
|
// Act
|
|
var result = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, _invalidSendGuid);
|
|
|
|
// Assert
|
|
Assert.True(result.IsError);
|
|
Assert.Equal(OidcConstants.TokenErrors.InvalidGrant, result.Error);
|
|
Assert.Contains(result.ErrorDescription, SendAccessConstants.SendIdGuidValidatorResults.InvalidSendId);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_EmptyHashKey_UsesEmptyKey(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(_invalidSendGuid);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = "";
|
|
|
|
// Act
|
|
var result = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, _invalidSendGuid);
|
|
|
|
// Assert
|
|
Assert.True(result.IsError);
|
|
Assert.Equal(OidcConstants.TokenErrors.InvalidGrant, result.Error);
|
|
Assert.Contains(result.ErrorDescription, SendAccessConstants.SendIdGuidValidatorResults.InvalidSendId);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_ConsistentBehavior_SameSendIdSameResult(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
|
|
Guid sendId)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = "consistent-test-key-123456789012345678901234567890";
|
|
|
|
// Act
|
|
var result1 = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, sendId);
|
|
var result2 = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, sendId);
|
|
|
|
// Assert
|
|
Assert.Equal(result1.ErrorDescription, result2.ErrorDescription);
|
|
Assert.Equal(result1.Error, result2.Error);
|
|
|
|
var customResponse1 = result1.CustomResponse as Dictionary<string, object>;
|
|
var customResponse2 = result2.CustomResponse as Dictionary<string, object>;
|
|
Assert.Equal(customResponse1[SendAccessConstants.SendAccessError], customResponse2[SendAccessConstants.SendAccessError]);
|
|
}
|
|
|
|
[Theory, BitAutoData]
|
|
public async Task ValidateRequestAsync_DifferentSendIds_CanReturnDifferentResults(
|
|
SutProvider<SendNeverAuthenticateRequestValidator> sutProvider,
|
|
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
|
|
Guid sendId1,
|
|
Guid sendId2)
|
|
{
|
|
// Arrange
|
|
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId1);
|
|
var context = new ExtensionGrantValidationContext
|
|
{
|
|
Request = tokenRequest
|
|
};
|
|
|
|
sutProvider.GetDependency<Core.Settings.GlobalSettings>().SendDefaultHashKey = "different-test-key-123456789012345678901234567890";
|
|
|
|
// Act
|
|
var result1 = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, sendId1);
|
|
var result2 = await sutProvider.Sut.ValidateRequestAsync(context, _authMethod, sendId2);
|
|
|
|
// Assert - Both should be errors
|
|
Assert.True(result1.IsError);
|
|
Assert.True(result2.IsError);
|
|
|
|
// Both should have valid error types
|
|
var validErrors = new[]
|
|
{
|
|
SendAccessConstants.SendIdGuidValidatorResults.InvalidSendId,
|
|
SendAccessConstants.EmailOtpValidatorResults.EmailRequired,
|
|
SendAccessConstants.PasswordValidatorResults.RequestPasswordIsRequired
|
|
};
|
|
Assert.Contains(result1.ErrorDescription, validErrors);
|
|
Assert.Contains(result2.ErrorDescription, validErrors);
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_WithValidGlobalSettings_CreatesInstance()
|
|
{
|
|
// Arrange
|
|
var globalSettings = new Core.Settings.GlobalSettings
|
|
{
|
|
SendDefaultHashKey = "test-key-123456789012345678901234567890"
|
|
};
|
|
|
|
// Act
|
|
var validator = new SendNeverAuthenticateRequestValidator(globalSettings);
|
|
|
|
// Assert
|
|
Assert.NotNull(validator);
|
|
}
|
|
}
|