1
0
mirror of https://github.com/bitwarden/server synced 2025-12-06 00:03:34 +00:00

remove hardcoded storage values (#6571)

This commit is contained in:
Kyle Denney
2025-11-17 11:16:02 -06:00
committed by GitHub
parent c620ec2aca
commit a9bb01031a
14 changed files with 85 additions and 23 deletions

View File

@@ -75,8 +75,7 @@ public class CloudOrganizationSignUpCommand(
PlanType = plan!.Type,
Seats = (short)(plan.PasswordManager.BaseSeats + signup.AdditionalSeats),
MaxCollections = plan.PasswordManager.MaxCollections,
MaxStorageGb = !plan.PasswordManager.BaseStorageGb.HasValue ?
(short?)null : (short)(plan.PasswordManager.BaseStorageGb.Value + signup.AdditionalStorageGb),
MaxStorageGb = (short)(plan.PasswordManager.BaseStorageGb + signup.AdditionalStorageGb),
UsePolicies = plan.HasPolicies,
UseSso = plan.HasSso,
UseGroups = plan.HasGroups,

View File

@@ -73,7 +73,7 @@ public class ProviderClientOrganizationSignUpCommand : IProviderClientOrganizati
PlanType = plan!.Type,
Seats = signup.AdditionalSeats,
MaxCollections = plan.PasswordManager.MaxCollections,
MaxStorageGb = 1,
MaxStorageGb = plan.PasswordManager.BaseStorageGb,
UsePolicies = plan.HasPolicies,
UseSso = plan.HasSso,
UseOrganizationDomains = plan.HasOrganizationDomains,

View File

@@ -148,7 +148,7 @@ public class OrganizationService : IOrganizationService
}
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, organization, storageAdjustmentGb,
plan.PasswordManager.StripeStoragePlanId);
plan.PasswordManager.StripeStoragePlanId, plan.PasswordManager.BaseStorageGb);
await ReplaceAndUpdateCacheAsync(organization);
return secret;
}

View File

@@ -97,7 +97,7 @@ public abstract record Plan
public decimal PremiumAccessOptionPrice { get; init; }
public short? MaxSeats { get; init; }
// Storage
public short? BaseStorageGb { get; init; }
public short BaseStorageGb { get; init; }
public bool HasAdditionalStorageOption { get; init; }
public decimal AdditionalStoragePricePerGb { get; init; }
public string StripeStoragePlanId { get; init; }

View File

@@ -80,6 +80,8 @@ public class CreatePremiumCloudHostedSubscriptionCommand(
return new BadRequest("Additional storage must be greater than 0.");
}
var premiumPlan = await pricingClient.GetAvailablePremiumPlan();
Customer? customer;
/*
@@ -107,7 +109,7 @@ public class CreatePremiumCloudHostedSubscriptionCommand(
customer = await ReconcileBillingLocationAsync(customer, billingAddress);
var subscription = await CreateSubscriptionAsync(user.Id, customer, additionalStorageGb > 0 ? additionalStorageGb : null);
var subscription = await CreateSubscriptionAsync(user.Id, customer, premiumPlan, additionalStorageGb > 0 ? additionalStorageGb : null);
paymentMethod.Switch(
tokenized =>
@@ -140,7 +142,7 @@ public class CreatePremiumCloudHostedSubscriptionCommand(
user.Gateway = GatewayType.Stripe;
user.GatewayCustomerId = customer.Id;
user.GatewaySubscriptionId = subscription.Id;
user.MaxStorageGb = (short)(1 + additionalStorageGb);
user.MaxStorageGb = (short)(premiumPlan.Storage.Provided + additionalStorageGb);
user.LicenseKey = CoreHelpers.SecureRandomString(20);
user.RevisionDate = DateTime.UtcNow;
@@ -304,9 +306,9 @@ public class CreatePremiumCloudHostedSubscriptionCommand(
private async Task<Subscription> CreateSubscriptionAsync(
Guid userId,
Customer customer,
Pricing.Premium.Plan premiumPlan,
int? storage)
{
var premiumPlan = await pricingClient.GetAvailablePremiumPlan();
var subscriptionItemOptionsList = new List<SubscriptionItemOptions>
{

View File

@@ -99,7 +99,7 @@ public record PlanAdapter : Core.Models.StaticStore.Plan
_ => true);
var baseSeats = GetBaseSeats(plan.Seats);
var maxSeats = GetMaxSeats(plan.Seats);
var baseStorageGb = (short?)plan.Storage?.Provided;
var baseStorageGb = (short)(plan.Storage?.Provided ?? 0);
var hasAdditionalStorageOption = plan.Storage != null;
var additionalStoragePricePerGb = plan.Storage?.Price ?? 0;
var stripeStoragePlanId = plan.Storage?.StripePriceId;

View File

@@ -4,4 +4,5 @@ public class Purchasable
{
public string StripePriceId { get; init; } = null!;
public decimal Price { get; init; }
public int Provided { get; init; }
}

View File

@@ -186,6 +186,6 @@ public class PricingClient(
Available = true,
LegacyYear = null,
Seat = new Purchasable { Price = 10M, StripePriceId = StripeConstants.Prices.PremiumAnnually },
Storage = new Purchasable { Price = 4M, StripePriceId = StripeConstants.Prices.StoragePlanPersonal }
Storage = new Purchasable { Price = 4M, StripePriceId = StripeConstants.Prices.StoragePlanPersonal, Provided = 1 }
};
}

View File

@@ -101,7 +101,9 @@ public class PremiumUserBillingService(
*/
customer = await ReconcileBillingLocationAsync(customer, customerSetup.TaxInformation);
var subscription = await CreateSubscriptionAsync(user.Id, customer, storage);
var premiumPlan = await pricingClient.GetAvailablePremiumPlan();
var subscription = await CreateSubscriptionAsync(user.Id, customer, premiumPlan, storage);
switch (customerSetup.TokenizedPaymentSource)
{
@@ -119,6 +121,7 @@ public class PremiumUserBillingService(
user.Gateway = GatewayType.Stripe;
user.GatewayCustomerId = customer.Id;
user.GatewaySubscriptionId = subscription.Id;
user.MaxStorageGb = (short)(premiumPlan.Storage.Provided + (storage ?? 0));
await userRepository.ReplaceAsync(user);
}
@@ -301,9 +304,9 @@ public class PremiumUserBillingService(
private async Task<Subscription> CreateSubscriptionAsync(
Guid userId,
Customer customer,
Pricing.Premium.Plan premiumPlan,
int? storage)
{
var premiumPlan = await pricingClient.GetAvailablePremiumPlan();
var subscriptionItemOptionsList = new List<SubscriptionItemOptions>
{

View File

@@ -299,7 +299,7 @@ public class CompleteSubscriptionUpdate : SubscriptionUpdate
? organization.SmServiceAccounts - plan.SecretsManager.BaseServiceAccount
: 0,
PurchasedAdditionalStorage = organization.MaxStorageGb.HasValue
? organization.MaxStorageGb.Value - (plan.PasswordManager.BaseStorageGb ?? 0) :
? organization.MaxStorageGb.Value - plan.PasswordManager.BaseStorageGb :
0
};
}

View File

@@ -254,9 +254,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
organization.UseApi = newPlan.HasApi;
organization.SelfHost = newPlan.HasSelfHost;
organization.UsePolicies = newPlan.HasPolicies;
organization.MaxStorageGb = !newPlan.PasswordManager.BaseStorageGb.HasValue
? (short?)null
: (short)(newPlan.PasswordManager.BaseStorageGb.Value + upgrade.AdditionalStorageGb);
organization.MaxStorageGb = (short)(newPlan.PasswordManager.BaseStorageGb + upgrade.AdditionalStorageGb);
organization.UseGroups = newPlan.HasGroups;
organization.UseDirectory = newPlan.HasDirectory;
organization.UseEvents = newPlan.HasEvents;

View File

@@ -904,7 +904,6 @@ public class UserService : UserManager<User>, IUserService
}
else
{
user.MaxStorageGb = (short)(1 + additionalStorageGb);
user.LicenseKey = CoreHelpers.SecureRandomString(20);
}
@@ -977,7 +976,8 @@ public class UserService : UserManager<User>, IUserService
var premiumPlan = await _pricingClient.GetAvailablePremiumPlan();
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, user, storageAdjustmentGb, premiumPlan.Storage.StripePriceId);
var baseStorageGb = (short)premiumPlan.Storage.Provided;
var secret = await BillingHelpers.AdjustStorageAsync(_paymentService, user, storageAdjustmentGb, premiumPlan.Storage.StripePriceId, baseStorageGb);
await SaveUserAsync(user);
return secret;
}

View File

@@ -7,7 +7,7 @@ namespace Bit.Core.Utilities;
public static class BillingHelpers
{
internal static async Task<string> AdjustStorageAsync(IPaymentService paymentService, IStorableSubscriber storableSubscriber,
short storageAdjustmentGb, string storagePlanId)
short storageAdjustmentGb, string storagePlanId, short baseStorageGb)
{
if (storableSubscriber == null)
{
@@ -30,9 +30,9 @@ public static class BillingHelpers
}
var newStorageGb = (short)(storableSubscriber.MaxStorageGb.Value + storageAdjustmentGb);
if (newStorageGb < 1)
if (newStorageGb < baseStorageGb)
{
newStorageGb = 1;
newStorageGb = baseStorageGb;
}
if (newStorageGb > 100)
@@ -48,7 +48,7 @@ public static class BillingHelpers
"Delete some stored data first.");
}
var additionalStorage = newStorageGb - 1;
var additionalStorage = newStorageGb - baseStorageGb;
var paymentIntentClientSecret = await paymentService.AdjustStorageAsync(storableSubscriber,
additionalStorage, storagePlanId);
storableSubscriber.MaxStorageGb = newStorageGb;

View File

@@ -53,7 +53,7 @@ public class CreatePremiumCloudHostedSubscriptionCommandTests
Available = true,
LegacyYear = null,
Seat = new PremiumPurchasable { Price = 10M, StripePriceId = StripeConstants.Prices.PremiumAnnually },
Storage = new PremiumPurchasable { Price = 4M, StripePriceId = StripeConstants.Prices.StoragePlanPersonal }
Storage = new PremiumPurchasable { Price = 4M, StripePriceId = StripeConstants.Prices.StoragePlanPersonal, Provided = 1 }
};
_pricingClient.GetAvailablePremiumPlan().Returns(premiumPlan);
@@ -720,4 +720,63 @@ public class CreatePremiumCloudHostedSubscriptionCommandTests
await _stripeAdapter.DidNotReceive().SubscriptionCreateAsync(Arg.Any<SubscriptionCreateOptions>());
await _userService.DidNotReceive().SaveUserAsync(Arg.Any<User>());
}
[Theory, BitAutoData]
public async Task Run_WithAdditionalStorage_SetsCorrectMaxStorageGb(
User user,
TokenizedPaymentMethod paymentMethod,
BillingAddress billingAddress)
{
// Arrange
user.Premium = false;
user.GatewayCustomerId = null;
user.Email = "test@example.com";
paymentMethod.Type = TokenizablePaymentMethodType.Card;
paymentMethod.Token = "card_token_123";
billingAddress.Country = "US";
billingAddress.PostalCode = "12345";
const short additionalStorage = 2;
// Setup premium plan with 5GB provided storage
var premiumPlan = new PremiumPlan
{
Name = "Premium",
Available = true,
LegacyYear = null,
Seat = new PremiumPurchasable { Price = 10M, StripePriceId = StripeConstants.Prices.PremiumAnnually },
Storage = new PremiumPurchasable { Price = 4M, StripePriceId = StripeConstants.Prices.StoragePlanPersonal, Provided = 1 }
};
_pricingClient.GetAvailablePremiumPlan().Returns(premiumPlan);
var mockCustomer = Substitute.For<StripeCustomer>();
mockCustomer.Id = "cust_123";
mockCustomer.Address = new Address { Country = "US", PostalCode = "12345" };
mockCustomer.Metadata = new Dictionary<string, string>();
var mockSubscription = Substitute.For<StripeSubscription>();
mockSubscription.Id = "sub_123";
mockSubscription.Status = "active";
mockSubscription.Items = new StripeList<SubscriptionItem>
{
Data =
[
new SubscriptionItem
{
CurrentPeriodEnd = DateTime.UtcNow.AddDays(30)
}
]
};
_stripeAdapter.CustomerCreateAsync(Arg.Any<CustomerCreateOptions>()).Returns(mockCustomer);
_stripeAdapter.SubscriptionCreateAsync(Arg.Any<SubscriptionCreateOptions>()).Returns(mockSubscription);
// Act
var result = await _command.Run(user, paymentMethod, billingAddress, additionalStorage);
// Assert
Assert.True(result.IsT0);
Assert.Equal((short)3, user.MaxStorageGb); // 1 (provided) + 2 (additional) = 3
await _userService.Received(1).SaveUserAsync(user);
}
}