1
0
mirror of https://github.com/bitwarden/server synced 2025-12-31 15:43:16 +00:00

[PM-27280] Support v2 encryption on key-connector signups (#6712)

* account v2 registration for key connector

* use new user repository functions

* test coverage

* integration test coverage

* documentation

* code review

* missing test coverage

* fix failing test

* failing test

* incorrect ticket number

* moved back request model to Api, created dedicated data class in Core

* sql stored procedure type mismatch, simplification

* key connector authorization handler
This commit is contained in:
Maciej Zieniuk
2025-12-18 19:43:03 +01:00
committed by GitHub
parent 2b742b0343
commit a92d7ac129
22 changed files with 1283 additions and 50 deletions

View File

@@ -238,10 +238,13 @@ public class AccountsKeyManagementControllerTests
[Theory]
[BitAutoData]
public async Task PostSetKeyConnectorKeyAsync_UserNull_Throws(
public async Task PostSetKeyConnectorKeyAsync_V1_UserNull_Throws(
SutProvider<AccountsKeyManagementController> sutProvider,
SetKeyConnectorKeyRequestModel data)
{
data.KeyConnectorKeyWrappedUserKey = null;
data.AccountKeys = null;
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).ReturnsNull();
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.PostSetKeyConnectorKeyAsync(data));
@@ -252,10 +255,13 @@ public class AccountsKeyManagementControllerTests
[Theory]
[BitAutoData]
public async Task PostSetKeyConnectorKeyAsync_SetKeyConnectorKeyFails_ThrowsBadRequestWithErrorResponse(
public async Task PostSetKeyConnectorKeyAsync_V1_SetKeyConnectorKeyFails_ThrowsBadRequestWithErrorResponse(
SutProvider<AccountsKeyManagementController> sutProvider,
SetKeyConnectorKeyRequestModel data, User expectedUser)
{
data.KeyConnectorKeyWrappedUserKey = null;
data.AccountKeys = null;
expectedUser.PublicKey = null;
expectedUser.PrivateKey = null;
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
@@ -278,17 +284,20 @@ public class AccountsKeyManagementControllerTests
Assert.Equal(data.KdfIterations, user.KdfIterations);
Assert.Equal(data.KdfMemory, user.KdfMemory);
Assert.Equal(data.KdfParallelism, user.KdfParallelism);
Assert.Equal(data.Keys.PublicKey, user.PublicKey);
Assert.Equal(data.Keys.EncryptedPrivateKey, user.PrivateKey);
Assert.Equal(data.Keys!.PublicKey, user.PublicKey);
Assert.Equal(data.Keys!.EncryptedPrivateKey, user.PrivateKey);
}), Arg.Is(data.Key), Arg.Is(data.OrgIdentifier));
}
[Theory]
[BitAutoData]
public async Task PostSetKeyConnectorKeyAsync_SetKeyConnectorKeySucceeds_OkResponse(
public async Task PostSetKeyConnectorKeyAsync_V1_SetKeyConnectorKeySucceeds_OkResponse(
SutProvider<AccountsKeyManagementController> sutProvider,
SetKeyConnectorKeyRequestModel data, User expectedUser)
{
data.KeyConnectorKeyWrappedUserKey = null;
data.AccountKeys = null;
expectedUser.PublicKey = null;
expectedUser.PrivateKey = null;
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
@@ -308,11 +317,108 @@ public class AccountsKeyManagementControllerTests
Assert.Equal(data.KdfIterations, user.KdfIterations);
Assert.Equal(data.KdfMemory, user.KdfMemory);
Assert.Equal(data.KdfParallelism, user.KdfParallelism);
Assert.Equal(data.Keys.PublicKey, user.PublicKey);
Assert.Equal(data.Keys.EncryptedPrivateKey, user.PrivateKey);
Assert.Equal(data.Keys!.PublicKey, user.PublicKey);
Assert.Equal(data.Keys!.EncryptedPrivateKey, user.PrivateKey);
}), Arg.Is(data.Key), Arg.Is(data.OrgIdentifier));
}
[Theory]
[BitAutoData]
public async Task PostSetKeyConnectorKeyAsync_V2_UserNull_Throws(
SutProvider<AccountsKeyManagementController> sutProvider)
{
var request = new SetKeyConnectorKeyRequestModel
{
KeyConnectorKeyWrappedUserKey = "wrapped-user-key",
AccountKeys = new AccountKeysRequestModel
{
AccountPublicKey = "public-key",
UserKeyEncryptedAccountPrivateKey = "encrypted-private-key"
},
OrgIdentifier = "test-org"
};
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).ReturnsNull();
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.PostSetKeyConnectorKeyAsync(request));
await sutProvider.GetDependency<ISetKeyConnectorKeyCommand>().DidNotReceive()
.SetKeyConnectorKeyForUserAsync(Arg.Any<User>(), Arg.Any<KeyConnectorKeysData>());
}
[Theory]
[BitAutoData]
public async Task PostSetKeyConnectorKeyAsync_V2_Success(
SutProvider<AccountsKeyManagementController> sutProvider,
User expectedUser)
{
var request = new SetKeyConnectorKeyRequestModel
{
KeyConnectorKeyWrappedUserKey = "wrapped-user-key",
AccountKeys = new AccountKeysRequestModel
{
AccountPublicKey = "public-key",
UserKeyEncryptedAccountPrivateKey = "encrypted-private-key"
},
OrgIdentifier = "test-org"
};
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
.Returns(expectedUser);
await sutProvider.Sut.PostSetKeyConnectorKeyAsync(request);
await sutProvider.GetDependency<ISetKeyConnectorKeyCommand>().Received(1)
.SetKeyConnectorKeyForUserAsync(Arg.Is(expectedUser),
Arg.Do<KeyConnectorKeysData>(data =>
{
Assert.Equal(request.KeyConnectorKeyWrappedUserKey, data.KeyConnectorKeyWrappedUserKey);
Assert.Equal(request.AccountKeys.AccountPublicKey, data.AccountKeys.AccountPublicKey);
Assert.Equal(request.AccountKeys.UserKeyEncryptedAccountPrivateKey,
data.AccountKeys.UserKeyEncryptedAccountPrivateKey);
Assert.Equal(request.OrgIdentifier, data.OrgIdentifier);
}));
}
[Theory]
[BitAutoData]
public async Task PostSetKeyConnectorKeyAsync_V2_CommandThrows_PropagatesException(
SutProvider<AccountsKeyManagementController> sutProvider,
User expectedUser)
{
var request = new SetKeyConnectorKeyRequestModel
{
KeyConnectorKeyWrappedUserKey = "wrapped-user-key",
AccountKeys = new AccountKeysRequestModel
{
AccountPublicKey = "public-key",
UserKeyEncryptedAccountPrivateKey = "encrypted-private-key"
},
OrgIdentifier = "test-org"
};
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
.Returns(expectedUser);
sutProvider.GetDependency<ISetKeyConnectorKeyCommand>()
.When(x => x.SetKeyConnectorKeyForUserAsync(Arg.Any<User>(), Arg.Any<KeyConnectorKeysData>()))
.Do(_ => throw new BadRequestException("Command failed"));
var exception = await Assert.ThrowsAsync<BadRequestException>(
() => sutProvider.Sut.PostSetKeyConnectorKeyAsync(request));
Assert.Equal("Command failed", exception.Message);
await sutProvider.GetDependency<ISetKeyConnectorKeyCommand>().Received(1)
.SetKeyConnectorKeyForUserAsync(Arg.Is(expectedUser),
Arg.Do<KeyConnectorKeysData>(data =>
{
Assert.Equal(request.KeyConnectorKeyWrappedUserKey, data.KeyConnectorKeyWrappedUserKey);
Assert.Equal(request.AccountKeys.AccountPublicKey, data.AccountKeys.AccountPublicKey);
Assert.Equal(request.AccountKeys.UserKeyEncryptedAccountPrivateKey,
data.AccountKeys.UserKeyEncryptedAccountPrivateKey);
Assert.Equal(request.OrgIdentifier, data.OrgIdentifier);
}));
}
[Theory]
[BitAutoData]
public async Task PostConvertToKeyConnectorAsync_UserNull_Throws(