mirror of
https://github.com/bitwarden/server
synced 2025-12-31 07:33:43 +00:00
[PM-29556] Fix: changing organization plan nulls out public and private keys (#6738)
Main fix: only assign new key value where old keys are not set and new keys have been provided. Refactors: - use consistent DTO model for keypairs - delete duplicate property assignment for new orgs
This commit is contained in:
@@ -66,8 +66,8 @@ public class ProviderClientsControllerTests
|
||||
signup.Plan == requestBody.PlanType &&
|
||||
signup.AdditionalSeats == requestBody.Seats &&
|
||||
signup.OwnerKey == requestBody.Key &&
|
||||
signup.PublicKey == requestBody.KeyPair.PublicKey &&
|
||||
signup.PrivateKey == requestBody.KeyPair.EncryptedPrivateKey &&
|
||||
signup.Keys.PublicKey == requestBody.KeyPair.PublicKey &&
|
||||
signup.Keys.WrappedPrivateKey == requestBody.KeyPair.EncryptedPrivateKey &&
|
||||
signup.CollectionName == requestBody.CollectionName),
|
||||
requestBody.OwnerEmail,
|
||||
user)
|
||||
|
||||
@@ -3,6 +3,7 @@ using Bit.Core.AdminConsole.OrganizationFeatures.Organizations.Update;
|
||||
using Bit.Core.Billing.Organizations.Services;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
@@ -162,8 +163,9 @@ public class OrganizationUpdateCommandTests
|
||||
OrganizationId = organizationId,
|
||||
Name = organization.Name,
|
||||
BillingEmail = organization.BillingEmail,
|
||||
PublicKey = publicKey,
|
||||
EncryptedPrivateKey = encryptedPrivateKey
|
||||
Keys = new PublicKeyEncryptionKeyPairData(
|
||||
wrappedPrivateKey: encryptedPrivateKey,
|
||||
publicKey: publicKey)
|
||||
};
|
||||
|
||||
// Act
|
||||
@@ -207,8 +209,9 @@ public class OrganizationUpdateCommandTests
|
||||
OrganizationId = organizationId,
|
||||
Name = organization.Name,
|
||||
BillingEmail = organization.BillingEmail,
|
||||
PublicKey = newPublicKey,
|
||||
EncryptedPrivateKey = newEncryptedPrivateKey
|
||||
Keys = new PublicKeyEncryptionKeyPairData(
|
||||
wrappedPrivateKey: newEncryptedPrivateKey,
|
||||
publicKey: newPublicKey)
|
||||
};
|
||||
|
||||
// Act
|
||||
@@ -394,8 +397,9 @@ public class OrganizationUpdateCommandTests
|
||||
OrganizationId = organizationId,
|
||||
Name = newName, // Should be ignored
|
||||
BillingEmail = newBillingEmail, // Should be ignored
|
||||
PublicKey = publicKey,
|
||||
EncryptedPrivateKey = encryptedPrivateKey
|
||||
Keys = new PublicKeyEncryptionKeyPairData(
|
||||
wrappedPrivateKey: encryptedPrivateKey,
|
||||
publicKey: publicKey)
|
||||
};
|
||||
|
||||
// Act
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Bit.Core.Billing.Pricing;
|
||||
using Bit.Core.Billing.Services;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.KeyManagement.Models.Data;
|
||||
using Bit.Core.Models.Business;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions;
|
||||
@@ -242,4 +243,134 @@ public class UpgradeOrganizationPlanCommandTests
|
||||
|
||||
await sutProvider.GetDependency<IOrganizationService>().DidNotReceiveWithAnyArgs().ReplaceAndUpdateCacheAsync(default);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[FreeOrganizationUpgradeCustomize, BitAutoData]
|
||||
public async Task UpgradePlan_WhenOrganizationIsMissingPublicAndPrivateKeys_Backfills(
|
||||
Organization organization,
|
||||
OrganizationUpgrade upgrade,
|
||||
string newPublicKey,
|
||||
string newPrivateKey,
|
||||
SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
|
||||
{
|
||||
organization.PublicKey = null;
|
||||
organization.PrivateKey = null;
|
||||
|
||||
upgrade.Plan = PlanType.TeamsAnnually;
|
||||
upgrade.Keys = new PublicKeyEncryptionKeyPairData(
|
||||
wrappedPrivateKey: newPrivateKey,
|
||||
publicKey: newPublicKey);
|
||||
upgrade.AdditionalSeats = 10;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(organization.PlanType)
|
||||
.Returns(MockPlans.Get(organization.PlanType));
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(upgrade.Plan)
|
||||
.Returns(MockPlans.Get(upgrade.Plan));
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id)
|
||||
.Returns(new OrganizationSeatCounts { Sponsored = 0, Users = 1 });
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(newPublicKey, organization.PublicKey);
|
||||
Assert.Equal(newPrivateKey, organization.PrivateKey);
|
||||
await sutProvider.GetDependency<IOrganizationService>()
|
||||
.Received(1)
|
||||
.ReplaceAndUpdateCacheAsync(organization);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[FreeOrganizationUpgradeCustomize, BitAutoData]
|
||||
public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotOverwriteWithNull(
|
||||
Organization organization,
|
||||
OrganizationUpgrade upgrade,
|
||||
SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
const string existingPublicKey = "existing-public-key";
|
||||
const string existingPrivateKey = "existing-private-key";
|
||||
|
||||
organization.PublicKey = existingPublicKey;
|
||||
organization.PrivateKey = existingPrivateKey;
|
||||
|
||||
upgrade.Plan = PlanType.TeamsAnnually;
|
||||
upgrade.Keys = null;
|
||||
upgrade.AdditionalSeats = 10;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(organization.PlanType)
|
||||
.Returns(MockPlans.Get(organization.PlanType));
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(upgrade.Plan)
|
||||
.Returns(MockPlans.Get(upgrade.Plan));
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id)
|
||||
.Returns(new OrganizationSeatCounts { Sponsored = 0, Users = 1 });
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(existingPublicKey, organization.PublicKey);
|
||||
Assert.Equal(existingPrivateKey, organization.PrivateKey);
|
||||
await sutProvider.GetDependency<IOrganizationService>()
|
||||
.Received(1)
|
||||
.ReplaceAndUpdateCacheAsync(organization);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[FreeOrganizationUpgradeCustomize, BitAutoData]
|
||||
public async Task UpgradePlan_WhenOrganizationAlreadyHasPublicAndPrivateKeys_DoesNotBackfillWithNewKeys(
|
||||
Organization organization,
|
||||
OrganizationUpgrade upgrade,
|
||||
SutProvider<UpgradeOrganizationPlanCommand> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
const string existingPublicKey = "existing-public-key";
|
||||
const string existingPrivateKey = "existing-private-key";
|
||||
const string newPublicKey = "new-public-key";
|
||||
const string newPrivateKey = "new-private-key";
|
||||
|
||||
organization.PublicKey = existingPublicKey;
|
||||
organization.PrivateKey = existingPrivateKey;
|
||||
|
||||
upgrade.Plan = PlanType.TeamsAnnually;
|
||||
upgrade.Keys = new PublicKeyEncryptionKeyPairData(
|
||||
wrappedPrivateKey: newPrivateKey,
|
||||
publicKey: newPublicKey);
|
||||
upgrade.AdditionalSeats = 10;
|
||||
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetByIdAsync(organization.Id)
|
||||
.Returns(organization);
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(organization.PlanType)
|
||||
.Returns(MockPlans.Get(organization.PlanType));
|
||||
sutProvider.GetDependency<IPricingClient>()
|
||||
.GetPlanOrThrow(upgrade.Plan)
|
||||
.Returns(MockPlans.Get(upgrade.Plan));
|
||||
sutProvider.GetDependency<IOrganizationRepository>()
|
||||
.GetOccupiedSeatCountByOrganizationIdAsync(organization.Id)
|
||||
.Returns(new OrganizationSeatCounts { Sponsored = 0, Users = 1 });
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.UpgradePlanAsync(organization.Id, upgrade);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(existingPublicKey, organization.PublicKey);
|
||||
Assert.Equal(existingPrivateKey, organization.PrivateKey);
|
||||
await sutProvider.GetDependency<IOrganizationService>()
|
||||
.Received(1)
|
||||
.ReplaceAndUpdateCacheAsync(organization);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user