You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
12 KiB
280 lines
12 KiB
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_ReturnsEmailAndOtpRequired( |
|
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.InvalidRequest, result.Error); |
|
Assert.Equal(SendAccessConstants.EmailOtpValidatorResults.EmailAndOtpRequired, result.ErrorDescription); |
|
|
|
var customResponse = result.CustomResponse as Dictionary<string, object>; |
|
Assert.NotNull(customResponse); |
|
Assert.Equal(SendAccessConstants.EmailOtpValidatorResults.EmailAndOtpRequired, 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); |
|
} |
|
}
|
|
|