mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 07:36:14 -05:00
[PM-4168] update keys for WebAuthnLoginCredential (#3506)
* allow update of webauthnlogincredential * Added Tests * fixed tests to use commands * addressing various feedback items
This commit is contained in:
@ -3,7 +3,10 @@ using Bit.Api.Auth.Models.Request.Accounts;
|
||||
using Bit.Api.Auth.Models.Request.Webauthn;
|
||||
using Bit.Core.AdminConsole.Enums;
|
||||
using Bit.Core.AdminConsole.Services;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Models.Api.Response.Accounts;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.Repositories;
|
||||
using Bit.Core.Auth.UserFeatures.WebAuthnLogin;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Exceptions;
|
||||
@ -36,34 +39,34 @@ public class WebAuthnControllerTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostOptions_UserNotFound_ThrowsUnauthorizedAccessException(SecretVerificationRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
public async Task AttestationOptions_UserNotFound_ThrowsUnauthorizedAccessException(SecretVerificationRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsNullForAnyArgs();
|
||||
|
||||
// Act
|
||||
var result = () => sutProvider.Sut.PostOptions(requestModel);
|
||||
var result = () => sutProvider.Sut.AttestationOptions(requestModel);
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostOptions_UserVerificationFailed_ThrowsBadRequestException(SecretVerificationRequestModel requestModel, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
public async Task AttestationOptions_UserVerificationFailed_ThrowsBadRequestException(SecretVerificationRequestModel requestModel, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
|
||||
sutProvider.GetDependency<IUserService>().VerifySecretAsync(user, default).Returns(false);
|
||||
|
||||
// Act
|
||||
var result = () => sutProvider.Sut.PostOptions(requestModel);
|
||||
var result = () => sutProvider.Sut.AttestationOptions(requestModel);
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<BadRequestException>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostOptions_RequireSsoPolicyApplicable_ThrowsBadRequestException(
|
||||
public async Task AttestationOptions_RequireSsoPolicyApplicable_ThrowsBadRequestException(
|
||||
SecretVerificationRequestModel requestModel, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
@ -73,12 +76,58 @@ public class WebAuthnControllerTests
|
||||
|
||||
// Act & Assert
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||
() => sutProvider.Sut.PostOptions(requestModel));
|
||||
() => sutProvider.Sut.AttestationOptions(requestModel));
|
||||
Assert.Contains("Passkeys cannot be created for your account. SSO login is required", exception.Message);
|
||||
}
|
||||
|
||||
#region Assertion Options
|
||||
[Theory, BitAutoData]
|
||||
public async Task Post_UserNotFound_ThrowsUnauthorizedAccessException(WebAuthnCredentialRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
public async Task AssertionOptions_UserNotFound_ThrowsUnauthorizedAccessException(SecretVerificationRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsNullForAnyArgs();
|
||||
|
||||
// Act
|
||||
var result = () => sutProvider.Sut.AssertionOptions(requestModel);
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<UnauthorizedAccessException>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task AssertionOptions_UserVerificationFailed_ThrowsBadRequestException(SecretVerificationRequestModel requestModel, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
|
||||
sutProvider.GetDependency<IUserService>().VerifySecretAsync(user, default).Returns(false);
|
||||
|
||||
// Act
|
||||
var result = () => sutProvider.Sut.AssertionOptions(requestModel);
|
||||
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<BadRequestException>(result);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task AssertionOptions_UserVerificationSuccess_ReturnsAssertionOptions(SecretVerificationRequestModel requestModel, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsForAnyArgs(user);
|
||||
sutProvider.GetDependency<IUserService>().VerifySecretAsync(user, requestModel.Secret).Returns(true);
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
|
||||
.Protect(Arg.Any<WebAuthnLoginAssertionOptionsTokenable>()).Returns("token");
|
||||
|
||||
// Act
|
||||
var result = await sutProvider.Sut.AssertionOptions(requestModel);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.IsType<WebAuthnLoginAssertionOptionsResponseModel>(result);
|
||||
}
|
||||
#endregion
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Post_UserNotFound_ThrowsUnauthorizedAccessException(WebAuthnLoginCredentialCreateRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(default).ReturnsNullForAnyArgs();
|
||||
@ -91,7 +140,7 @@ public class WebAuthnControllerTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Post_ExpiredToken_ThrowsBadRequestException(WebAuthnCredentialRequestModel requestModel, CredentialCreateOptions createOptions, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
public async Task Post_ExpiredToken_ThrowsBadRequestException(WebAuthnLoginCredentialCreateRequestModel requestModel, CredentialCreateOptions createOptions, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var token = new WebAuthnCredentialCreateOptionsTokenable(user, createOptions);
|
||||
@ -110,7 +159,7 @@ public class WebAuthnControllerTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Post_ValidInput_Returns(WebAuthnCredentialRequestModel requestModel, CredentialCreateOptions createOptions, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
public async Task Post_ValidInput_Returns(WebAuthnLoginCredentialCreateRequestModel requestModel, CredentialCreateOptions createOptions, User user, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var token = new WebAuthnCredentialCreateOptionsTokenable(user, createOptions);
|
||||
@ -128,12 +177,17 @@ public class WebAuthnControllerTests
|
||||
await sutProvider.Sut.Post(requestModel);
|
||||
|
||||
// Assert
|
||||
// Nothing to assert since return is void
|
||||
await sutProvider.GetDependency<IUserService>()
|
||||
.Received(1)
|
||||
.GetUserByPrincipalAsync(default);
|
||||
await sutProvider.GetDependency<ICreateWebAuthnLoginCredentialCommand>()
|
||||
.Received(1)
|
||||
.CreateWebAuthnLoginCredentialAsync(user, requestModel.Name, createOptions, Arg.Any<AuthenticatorAttestationRawResponse>(), requestModel.SupportsPrf, requestModel.EncryptedUserKey, requestModel.EncryptedPublicKey, requestModel.EncryptedPrivateKey);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Post_RequireSsoPolicyApplicable_ThrowsBadRequestException(
|
||||
WebAuthnCredentialRequestModel requestModel,
|
||||
WebAuthnLoginCredentialCreateRequestModel requestModel,
|
||||
CredentialCreateOptions createOptions,
|
||||
User user,
|
||||
SutProvider<WebAuthnController> sutProvider)
|
||||
@ -183,5 +237,91 @@ public class WebAuthnControllerTests
|
||||
// Assert
|
||||
await Assert.ThrowsAsync<BadRequestException>(result);
|
||||
}
|
||||
}
|
||||
|
||||
#region Update Credential
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_TokenVerificationFailed_ThrowsBadRequestException(AssertionOptions assertionOptions, WebAuthnLoginCredentialUpdateRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var expectedMessage = "The token associated with your request is invalid or has expired. A valid token is required to continue.";
|
||||
var token = new WebAuthnLoginAssertionOptionsTokenable(
|
||||
Core.Auth.Enums.WebAuthnLoginAssertionOptionsScope.PrfRegistration, assertionOptions);
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
|
||||
.Unprotect(requestModel.Token)
|
||||
.Returns(token);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateCredential(requestModel));
|
||||
// Assert
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_CredentialNotFound_ThrowsBadRequestException(AssertionOptions assertionOptions, WebAuthnLoginCredentialUpdateRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var expectedMessage = "Unable to update credential.";
|
||||
var token = new WebAuthnLoginAssertionOptionsTokenable(
|
||||
Core.Auth.Enums.WebAuthnLoginAssertionOptionsScope.UpdateKeySet, assertionOptions);
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
|
||||
.Unprotect(requestModel.Token)
|
||||
.Returns(token);
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateCredential(requestModel));
|
||||
// Assert
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_PrfNotSupported_ThrowsBadRequestException(User user, WebAuthnCredential credential, AssertionOptions assertionOptions, WebAuthnLoginCredentialUpdateRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var expectedMessage = "Unable to update credential.";
|
||||
credential.SupportsPrf = false;
|
||||
var token = new WebAuthnLoginAssertionOptionsTokenable(
|
||||
Core.Auth.Enums.WebAuthnLoginAssertionOptionsScope.UpdateKeySet, assertionOptions);
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
|
||||
.Unprotect(requestModel.Token)
|
||||
.Returns(token);
|
||||
|
||||
sutProvider.GetDependency<IAssertWebAuthnLoginCredentialCommand>()
|
||||
.AssertWebAuthnLoginCredential(assertionOptions, requestModel.DeviceResponse)
|
||||
.Returns((user, credential));
|
||||
|
||||
// Act
|
||||
var exception = await Assert.ThrowsAsync<BadRequestException>(() => sutProvider.Sut.UpdateCredential(requestModel));
|
||||
// Assert
|
||||
Assert.Equal(expectedMessage, exception.Message);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task Put_UpdateCredential_Success(User user, WebAuthnCredential credential, AssertionOptions assertionOptions, WebAuthnLoginCredentialUpdateRequestModel requestModel, SutProvider<WebAuthnController> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var token = new WebAuthnLoginAssertionOptionsTokenable(
|
||||
Core.Auth.Enums.WebAuthnLoginAssertionOptionsScope.UpdateKeySet, assertionOptions);
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
|
||||
.Unprotect(requestModel.Token)
|
||||
.Returns(token);
|
||||
|
||||
sutProvider.GetDependency<IAssertWebAuthnLoginCredentialCommand>()
|
||||
.AssertWebAuthnLoginCredential(assertionOptions, requestModel.DeviceResponse)
|
||||
.Returns((user, credential));
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.UpdateCredential(requestModel);
|
||||
|
||||
// Assert
|
||||
sutProvider.GetDependency<IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable>>()
|
||||
.Received(1)
|
||||
.Unprotect(requestModel.Token);
|
||||
await sutProvider.GetDependency<IAssertWebAuthnLoginCredentialCommand>()
|
||||
.Received(1)
|
||||
.AssertWebAuthnLoginCredential(assertionOptions, requestModel.DeviceResponse);
|
||||
await sutProvider.GetDependency<IWebAuthnCredentialRepository>()
|
||||
.Received(1)
|
||||
.UpdateAsync(credential);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
Reference in New Issue
Block a user