mirror of
https://github.com/bitwarden/server
synced 2026-01-04 17:43:53 +00:00
[PM-22696] send enumeration protection (#6352)
* 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
This commit is contained in:
@@ -1,12 +1,8 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Identity.IdentityServer.Enums;
|
||||
using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
@@ -81,7 +77,7 @@ public class SendAccessGrantValidatorTests
|
||||
var context = new ExtensionGrantValidationContext();
|
||||
|
||||
tokenRequest.GrantType = CustomGrantTypes.SendAccess;
|
||||
tokenRequest.Raw = CreateTokenRequestBody(Guid.Empty);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(Guid.Empty);
|
||||
|
||||
// To preserve the CreateTokenRequestBody method for more general usage we over write the sendId
|
||||
tokenRequest.Raw.Set(SendAccessConstants.TokenRequest.SendId, "invalid-guid-format");
|
||||
@@ -118,7 +114,9 @@ public class SendAccessGrantValidatorTests
|
||||
public async Task ValidateAsync_NeverAuthenticateMethod_ReturnsInvalidGrant(
|
||||
[AutoFixture.ValidatedTokenRequest] ValidatedTokenRequest tokenRequest,
|
||||
SutProvider<SendAccessGrantValidator> sutProvider,
|
||||
Guid sendId)
|
||||
NeverAuthenticate neverAuthenticate,
|
||||
Guid sendId,
|
||||
GrantValidationResult expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var context = SetupTokenRequest(
|
||||
@@ -128,14 +126,20 @@ public class SendAccessGrantValidatorTests
|
||||
|
||||
sutProvider.GetDependency<ISendAuthenticationQuery>()
|
||||
.GetAuthenticationMethod(sendId)
|
||||
.Returns(new NeverAuthenticate());
|
||||
.Returns(neverAuthenticate);
|
||||
|
||||
sutProvider.GetDependency<ISendAuthenticationMethodValidator<NeverAuthenticate>>()
|
||||
.ValidateRequestAsync(context, neverAuthenticate, sendId)
|
||||
.Returns(expectedResult);
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.ValidateAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(OidcConstants.TokenErrors.InvalidGrant, context.Result.Error);
|
||||
Assert.Equal($"{SendAccessConstants.TokenRequest.SendId} is invalid.", context.Result.ErrorDescription);
|
||||
Assert.Equal(expectedResult, context.Result);
|
||||
await sutProvider.GetDependency<ISendAuthenticationMethodValidator<NeverAuthenticate>>()
|
||||
.Received(1)
|
||||
.ValidateRequestAsync(context, neverAuthenticate, sendId);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
@@ -264,7 +268,7 @@ public class SendAccessGrantValidatorTests
|
||||
public void GrantType_ReturnsCorrectType()
|
||||
{
|
||||
// Arrange & Act
|
||||
var validator = new SendAccessGrantValidator(null!, null!, null!, null!);
|
||||
var validator = new SendAccessGrantValidator(null!, null!, null!, null!, null!);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(CustomGrantTypes.SendAccess, ((IExtensionGrantValidator)validator).GrantType);
|
||||
@@ -289,44 +293,9 @@ public class SendAccessGrantValidatorTests
|
||||
var context = new ExtensionGrantValidationContext();
|
||||
|
||||
request.GrantType = CustomGrantTypes.SendAccess;
|
||||
request.Raw = CreateTokenRequestBody(sendId);
|
||||
request.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId);
|
||||
context.Request = request;
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private static NameValueCollection CreateTokenRequestBody(
|
||||
Guid sendId,
|
||||
string passwordHash = null,
|
||||
string sendEmail = null,
|
||||
string otpCode = null)
|
||||
{
|
||||
var sendIdBase64 = CoreHelpers.Base64UrlEncode(sendId.ToByteArray());
|
||||
|
||||
var rawRequestParameters = new NameValueCollection
|
||||
{
|
||||
{ OidcConstants.TokenRequest.GrantType, CustomGrantTypes.SendAccess },
|
||||
{ OidcConstants.TokenRequest.ClientId, BitwardenClient.Send },
|
||||
{ OidcConstants.TokenRequest.Scope, ApiScopes.ApiSendAccess },
|
||||
{ "deviceType", ((int)DeviceType.FirefoxBrowser).ToString() },
|
||||
{ SendAccessConstants.TokenRequest.SendId, sendIdBase64 }
|
||||
};
|
||||
|
||||
if (passwordHash != null)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.ClientB64HashedPassword, passwordHash);
|
||||
}
|
||||
|
||||
if (sendEmail != null)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.Email, sendEmail);
|
||||
}
|
||||
|
||||
if (otpCode != null && sendEmail != null)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.Otp, otpCode);
|
||||
}
|
||||
|
||||
return rawRequestParameters;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Identity.IdentityServer.Enums;
|
||||
using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
|
||||
using Duende.IdentityModel;
|
||||
|
||||
namespace Bit.Identity.Test.IdentityServer.SendAccess;
|
||||
|
||||
public static class SendAccessTestUtilities
|
||||
{
|
||||
public static NameValueCollection CreateValidatedTokenRequest(
|
||||
Guid sendId,
|
||||
string sendEmail = null,
|
||||
string otpCode = null,
|
||||
params string[] passwordHash)
|
||||
{
|
||||
var sendIdBase64 = CoreHelpers.Base64UrlEncode(sendId.ToByteArray());
|
||||
|
||||
var rawRequestParameters = new NameValueCollection
|
||||
{
|
||||
{ OidcConstants.TokenRequest.GrantType, CustomGrantTypes.SendAccess },
|
||||
{ OidcConstants.TokenRequest.ClientId, BitwardenClient.Send },
|
||||
{ OidcConstants.TokenRequest.Scope, ApiScopes.ApiSendAccess },
|
||||
{ "device_type", ((int)DeviceType.FirefoxBrowser).ToString() },
|
||||
{ SendAccessConstants.TokenRequest.SendId, sendIdBase64 }
|
||||
};
|
||||
|
||||
if (sendEmail != null)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.Email, sendEmail);
|
||||
}
|
||||
|
||||
if (otpCode != null && sendEmail != null)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.Otp, otpCode);
|
||||
}
|
||||
|
||||
if (passwordHash != null && passwordHash.Length > 0)
|
||||
{
|
||||
foreach (var hash in passwordHash)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.ClientB64HashedPassword, hash);
|
||||
}
|
||||
}
|
||||
|
||||
return rawRequestParameters;
|
||||
}
|
||||
}
|
||||
@@ -31,9 +31,9 @@ public class SendConstantsSnapshotTests
|
||||
public void GrantValidatorResults_Constants_HaveCorrectValues()
|
||||
{
|
||||
// Assert
|
||||
Assert.Equal("valid_send_guid", SendAccessConstants.GrantValidatorResults.ValidSendGuid);
|
||||
Assert.Equal("send_id_required", SendAccessConstants.GrantValidatorResults.SendIdRequired);
|
||||
Assert.Equal("send_id_invalid", SendAccessConstants.GrantValidatorResults.InvalidSendId);
|
||||
Assert.Equal("valid_send_guid", SendAccessConstants.SendIdGuidValidatorResults.ValidSendGuid);
|
||||
Assert.Equal("send_id_required", SendAccessConstants.SendIdGuidValidatorResults.SendIdRequired);
|
||||
Assert.Equal("send_id_invalid", SendAccessConstants.SendIdGuidValidatorResults.InvalidSendId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Identity.IdentityServer.Enums;
|
||||
using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
@@ -28,7 +23,7 @@ public class SendEmailOtpRequestValidatorTests
|
||||
Guid sendId)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId);
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
Request = tokenRequest
|
||||
@@ -61,8 +56,7 @@ public class SendEmailOtpRequestValidatorTests
|
||||
Guid sendId)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, email);
|
||||
var emailOTP = new EmailOtp(["user@test.dev"]);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, email);
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
Request = tokenRequest
|
||||
@@ -96,7 +90,7 @@ public class SendEmailOtpRequestValidatorTests
|
||||
string generatedToken)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, email);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, email);
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
Request = tokenRequest
|
||||
@@ -144,7 +138,7 @@ public class SendEmailOtpRequestValidatorTests
|
||||
string email)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, email);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, email);
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
Request = tokenRequest
|
||||
@@ -179,7 +173,7 @@ public class SendEmailOtpRequestValidatorTests
|
||||
string otp)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, email, otp);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, email, otp);
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
Request = tokenRequest
|
||||
@@ -231,7 +225,7 @@ public class SendEmailOtpRequestValidatorTests
|
||||
string invalidOtp)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, email, invalidOtp);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, email, invalidOtp);
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
Request = tokenRequest
|
||||
@@ -278,33 +272,4 @@ public class SendEmailOtpRequestValidatorTests
|
||||
// Assert
|
||||
Assert.NotNull(validator);
|
||||
}
|
||||
|
||||
private static NameValueCollection CreateValidatedTokenRequest(
|
||||
Guid sendId,
|
||||
string sendEmail = null,
|
||||
string otpCode = null)
|
||||
{
|
||||
var sendIdBase64 = CoreHelpers.Base64UrlEncode(sendId.ToByteArray());
|
||||
|
||||
var rawRequestParameters = new NameValueCollection
|
||||
{
|
||||
{ OidcConstants.TokenRequest.GrantType, CustomGrantTypes.SendAccess },
|
||||
{ OidcConstants.TokenRequest.ClientId, BitwardenClient.Send },
|
||||
{ OidcConstants.TokenRequest.Scope, ApiScopes.ApiSendAccess },
|
||||
{ "device_type", ((int)DeviceType.FirefoxBrowser).ToString() },
|
||||
{ SendAccessConstants.TokenRequest.SendId, sendIdBase64 }
|
||||
};
|
||||
|
||||
if (sendEmail != null)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.Email, sendEmail);
|
||||
}
|
||||
|
||||
if (otpCode != null && sendEmail != null)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.Otp, otpCode);
|
||||
}
|
||||
|
||||
return rawRequestParameters;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
using System.Collections.Specialized;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.IdentityServer;
|
||||
using Bit.Core.Auth.Identity;
|
||||
using Bit.Core.Auth.UserFeatures.SendAccess;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.KeyManagement.Sends;
|
||||
using Bit.Core.Tools.Models.Data;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Identity.IdentityServer.Enums;
|
||||
using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
@@ -28,7 +23,7 @@ public class SendPasswordRequestValidatorTests
|
||||
Guid sendId)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId);
|
||||
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
@@ -58,7 +53,7 @@ public class SendPasswordRequestValidatorTests
|
||||
string clientPasswordHash)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, clientPasswordHash);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, passwordHash: clientPasswordHash);
|
||||
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
@@ -92,7 +87,7 @@ public class SendPasswordRequestValidatorTests
|
||||
string clientPasswordHash)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, clientPasswordHash);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, passwordHash: clientPasswordHash);
|
||||
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
@@ -130,7 +125,7 @@ public class SendPasswordRequestValidatorTests
|
||||
Guid sendId)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, string.Empty);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, passwordHash: string.Empty);
|
||||
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
@@ -163,7 +158,7 @@ public class SendPasswordRequestValidatorTests
|
||||
{
|
||||
// Arrange
|
||||
var whitespacePassword = " ";
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, whitespacePassword);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, passwordHash: whitespacePassword);
|
||||
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
@@ -196,7 +191,7 @@ public class SendPasswordRequestValidatorTests
|
||||
// Arrange
|
||||
var firstPassword = "first-password";
|
||||
var secondPassword = "second-password";
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, firstPassword, secondPassword);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, passwordHash: [firstPassword, secondPassword]);
|
||||
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
@@ -229,7 +224,7 @@ public class SendPasswordRequestValidatorTests
|
||||
string clientPasswordHash)
|
||||
{
|
||||
// Arrange
|
||||
tokenRequest.Raw = CreateValidatedTokenRequest(sendId, clientPasswordHash);
|
||||
tokenRequest.Raw = SendAccessTestUtilities.CreateValidatedTokenRequest(sendId, passwordHash: clientPasswordHash);
|
||||
|
||||
var context = new ExtensionGrantValidationContext
|
||||
{
|
||||
@@ -268,30 +263,4 @@ public class SendPasswordRequestValidatorTests
|
||||
// Assert
|
||||
Assert.NotNull(validator);
|
||||
}
|
||||
|
||||
private static NameValueCollection CreateValidatedTokenRequest(
|
||||
Guid sendId,
|
||||
params string[] passwordHash)
|
||||
{
|
||||
var sendIdBase64 = CoreHelpers.Base64UrlEncode(sendId.ToByteArray());
|
||||
|
||||
var rawRequestParameters = new NameValueCollection
|
||||
{
|
||||
{ OidcConstants.TokenRequest.GrantType, CustomGrantTypes.SendAccess },
|
||||
{ OidcConstants.TokenRequest.ClientId, BitwardenClient.Send },
|
||||
{ OidcConstants.TokenRequest.Scope, ApiScopes.ApiSendAccess },
|
||||
{ "device_type", ((int)DeviceType.FirefoxBrowser).ToString() },
|
||||
{ SendAccessConstants.TokenRequest.SendId, sendIdBase64 }
|
||||
};
|
||||
|
||||
if (passwordHash != null && passwordHash.Length > 0)
|
||||
{
|
||||
foreach (var hash in passwordHash)
|
||||
{
|
||||
rawRequestParameters.Add(SendAccessConstants.TokenRequest.ClientB64HashedPassword, hash);
|
||||
}
|
||||
}
|
||||
|
||||
return rawRequestParameters;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user