mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
Auth/PM-12613 - Registration with Email Verification - Provider Invite Flow (#4917)
* PM-12613 - Add RegisterUserViaProviderInviteToken flow (needs manual, unit, and integration tests) * PM-12613 - RegisterUserCommandTests - test register via provider inv * PM-12613 - AccountsControllerTests.cs - Add integration test for provider * PM-12613 - Remove comment * PM-12613 - Add temp logging to help debug integration test failure in pipeline * PM-12613 - WebApplicationFactoryBase.cs - add ConfigureServices * PM-12613 - AccountsControllerTests.cs - refactor test to sidestep encryption * PM-12613 - Per PR feedback, refactor AccountsController.cs and move token type checking into request model. * PM-12613 - Remove debug writelines * PM-12613 - Add RegisterFinishRequestModelTests
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Auth.Entities;
|
||||
using Bit.Core.Auth.Models.Api.Request.Accounts;
|
||||
@ -9,10 +10,12 @@ using Bit.Core.Models.Business.Tokenables;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Tokens;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Identity.Models.Request.Accounts;
|
||||
using Bit.IntegrationTestCommon.Factories;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
@ -470,6 +473,80 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
Assert.Equal(kdfParallelism, user.KdfParallelism);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RegistrationWithEmailVerification_WithProviderInviteToken_Succeeds(
|
||||
[StringLength(1000)] string masterPasswordHash, [StringLength(50)] string masterPasswordHint, string userSymmetricKey,
|
||||
KeysRequestModel userAsymmetricKeys, int kdfMemory, int kdfParallelism)
|
||||
{
|
||||
|
||||
// Localize factory to just this test.
|
||||
var localFactory = new IdentityApplicationFactory();
|
||||
|
||||
// Hardcoded, valid data
|
||||
var email = "jsnider+local253@bitwarden.com";
|
||||
var providerUserId = new Guid("c6fdba35-2e52-43b4-8fb7-b211011d154a");
|
||||
var nowMillis = CoreHelpers.ToEpocMilliseconds(DateTime.UtcNow);
|
||||
var decryptedProviderInviteToken = $"ProviderUserInvite {providerUserId} {email} {nowMillis}";
|
||||
// var providerInviteToken = await GetValidProviderInviteToken(localFactory, email, providerUserId);
|
||||
|
||||
// Get the byte array of the plaintext
|
||||
var decryptedProviderInviteTokenByteArray = Encoding.UTF8.GetBytes(decryptedProviderInviteToken);
|
||||
|
||||
// Base64 encode the byte array (this is passed to protector.protect(bytes))
|
||||
var base64EncodedProviderInvToken = WebEncoders.Base64UrlEncode(decryptedProviderInviteTokenByteArray);
|
||||
|
||||
var mockDataProtector = Substitute.For<IDataProtector>();
|
||||
mockDataProtector.Unprotect(Arg.Any<byte[]>()).Returns(decryptedProviderInviteTokenByteArray);
|
||||
|
||||
localFactory.SubstituteService<IDataProtectionProvider>(dataProtectionProvider =>
|
||||
{
|
||||
dataProtectionProvider.CreateProtector(Arg.Any<string>())
|
||||
.Returns(mockDataProtector);
|
||||
});
|
||||
|
||||
// As token contains now milliseconds for when it was created, create 1k year timespan for expiration
|
||||
// to ensure token is valid for a good long while.
|
||||
localFactory.UpdateConfiguration("globalSettings:OrganizationInviteExpirationHours", "8760000");
|
||||
|
||||
var registerFinishReqModel = new RegisterFinishRequestModel
|
||||
{
|
||||
Email = email,
|
||||
MasterPasswordHash = masterPasswordHash,
|
||||
MasterPasswordHint = masterPasswordHint,
|
||||
ProviderInviteToken = base64EncodedProviderInvToken,
|
||||
ProviderUserId = providerUserId,
|
||||
Kdf = KdfType.PBKDF2_SHA256,
|
||||
KdfIterations = AuthConstants.PBKDF2_ITERATIONS.Default,
|
||||
UserSymmetricKey = userSymmetricKey,
|
||||
UserAsymmetricKeys = userAsymmetricKeys,
|
||||
KdfMemory = kdfMemory,
|
||||
KdfParallelism = kdfParallelism
|
||||
};
|
||||
|
||||
var postRegisterFinishHttpContext = await localFactory.PostRegisterFinishAsync(registerFinishReqModel);
|
||||
|
||||
Assert.Equal(StatusCodes.Status200OK, postRegisterFinishHttpContext.Response.StatusCode);
|
||||
|
||||
var database = localFactory.GetDatabaseContext();
|
||||
var user = await database.Users
|
||||
.SingleAsync(u => u.Email == email);
|
||||
|
||||
Assert.NotNull(user);
|
||||
|
||||
// Assert user properties match the request model
|
||||
Assert.Equal(email, user.Email);
|
||||
Assert.NotEqual(masterPasswordHash, user.MasterPassword); // We execute server side hashing
|
||||
Assert.NotNull(user.MasterPassword);
|
||||
Assert.Equal(masterPasswordHint, user.MasterPasswordHint);
|
||||
Assert.Equal(userSymmetricKey, user.Key);
|
||||
Assert.Equal(userAsymmetricKeys.EncryptedPrivateKey, user.PrivateKey);
|
||||
Assert.Equal(userAsymmetricKeys.PublicKey, user.PublicKey);
|
||||
Assert.Equal(KdfType.PBKDF2_SHA256, user.Kdf);
|
||||
Assert.Equal(AuthConstants.PBKDF2_ITERATIONS.Default, user.KdfIterations);
|
||||
Assert.Equal(kdfMemory, user.KdfMemory);
|
||||
Assert.Equal(kdfParallelism, user.KdfParallelism);
|
||||
}
|
||||
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task PostRegisterVerificationEmailClicked_Success(
|
||||
@ -527,4 +604,5 @@ public class AccountsControllerTests : IClassFixture<IdentityApplicationFactory>
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user