mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 21:18:13 -05:00

* Remove gRPC and convert PricingClient to HttpClient wrapper * Add PlanType.GetProductTier extension Many instances of StaticStore use are just to get the ProductTierType of a PlanType, but this can be derived from the PlanType itself without having to fetch the entire plan. * Remove invocations of the StaticStore in non-Test code * Deprecate StaticStore entry points * Run dotnet format * Matt's feedback * Run dotnet format * Rui's feedback * Run dotnet format * Replacements since approval * Run dotnet format
154 lines
6.3 KiB
C#
154 lines
6.3 KiB
C#
using System.Net;
|
|
using Bit.Api.Billing.Public.Models;
|
|
using Bit.Api.Models.Public.Response;
|
|
using Bit.Core.Billing.Pricing;
|
|
using Bit.Core.Context;
|
|
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
|
|
using Bit.Core.Repositories;
|
|
using Bit.Core.Services;
|
|
using Bit.Core.Utilities;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using OrganizationSubscriptionUpdateRequestModel = Bit.Api.Billing.Public.Models.OrganizationSubscriptionUpdateRequestModel;
|
|
|
|
namespace Bit.Api.Billing.Public.Controllers;
|
|
|
|
[Route("public/organization")]
|
|
[Authorize("Organization")]
|
|
public class OrganizationController : Controller
|
|
{
|
|
private readonly IOrganizationService _organizationService;
|
|
private readonly ICurrentContext _currentContext;
|
|
private readonly IOrganizationRepository _organizationRepository;
|
|
private readonly IUpdateSecretsManagerSubscriptionCommand _updateSecretsManagerSubscriptionCommand;
|
|
private readonly ILogger<OrganizationController> _logger;
|
|
private readonly IPricingClient _pricingClient;
|
|
|
|
public OrganizationController(
|
|
IOrganizationService organizationService,
|
|
ICurrentContext currentContext,
|
|
IOrganizationRepository organizationRepository,
|
|
IUpdateSecretsManagerSubscriptionCommand updateSecretsManagerSubscriptionCommand,
|
|
ILogger<OrganizationController> logger,
|
|
IPricingClient pricingClient)
|
|
{
|
|
_organizationService = organizationService;
|
|
_currentContext = currentContext;
|
|
_organizationRepository = organizationRepository;
|
|
_updateSecretsManagerSubscriptionCommand = updateSecretsManagerSubscriptionCommand;
|
|
_logger = logger;
|
|
_pricingClient = pricingClient;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the subscription details for the current organization.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// Returns an object containing the subscription details if successful.
|
|
/// </returns>
|
|
[HttpGet("subscription")]
|
|
[SelfHosted(NotSelfHostedOnly = true)]
|
|
[ProducesResponseType(typeof(OrganizationSubscriptionDetailsResponseModel), (int)HttpStatusCode.OK)]
|
|
[ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.NotFound)]
|
|
public async Task<IActionResult> GetSubscriptionAsync()
|
|
{
|
|
try
|
|
{
|
|
var organizationId = _currentContext.OrganizationId.Value;
|
|
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
|
|
|
var subscriptionDetails = new OrganizationSubscriptionDetailsResponseModel
|
|
{
|
|
PasswordManager = new PasswordManagerSubscriptionDetails
|
|
{
|
|
Seats = organization.Seats,
|
|
MaxAutoScaleSeats = organization.MaxAutoscaleSeats,
|
|
Storage = organization.MaxStorageGb
|
|
},
|
|
SecretsManager = new SecretsManagerSubscriptionDetails
|
|
{
|
|
Seats = organization.SmSeats,
|
|
MaxAutoScaleSeats = organization.MaxAutoscaleSmSeats,
|
|
ServiceAccounts = organization.SmServiceAccounts,
|
|
MaxAutoScaleServiceAccounts = organization.MaxAutoscaleSmServiceAccounts
|
|
}
|
|
};
|
|
|
|
return Ok(subscriptionDetails);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Unhandled error while retrieving the subscription details");
|
|
return StatusCode(500, new { Message = "An error occurred while retrieving the subscription details." });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the organization's current subscription for Password Manager and/or Secrets Manager.
|
|
/// </summary>
|
|
/// <param name="model">The request model containing the updated subscription information.</param>
|
|
[HttpPut("subscription")]
|
|
[SelfHosted(NotSelfHostedOnly = true)]
|
|
[ProducesResponseType((int)HttpStatusCode.OK)]
|
|
[ProducesResponseType(typeof(ErrorResponseModel), (int)HttpStatusCode.BadRequest)]
|
|
[ProducesResponseType((int)HttpStatusCode.NotFound)]
|
|
public async Task<IActionResult> PostSubscriptionAsync([FromBody] OrganizationSubscriptionUpdateRequestModel model)
|
|
{
|
|
try
|
|
{
|
|
await UpdatePasswordManagerAsync(model, _currentContext.OrganizationId.Value);
|
|
|
|
var secretsManagerResult = await UpdateSecretsManagerAsync(model, _currentContext.OrganizationId.Value);
|
|
|
|
if (!string.IsNullOrEmpty(secretsManagerResult))
|
|
{
|
|
return Ok(new { Message = secretsManagerResult });
|
|
}
|
|
|
|
return Ok(new { Message = "Subscription updated successfully." });
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Unhandled error while updating the subscription");
|
|
return StatusCode(500, new { Message = "An error occurred while updating the subscription." });
|
|
}
|
|
}
|
|
|
|
private async Task UpdatePasswordManagerAsync(OrganizationSubscriptionUpdateRequestModel model, Guid organizationId)
|
|
{
|
|
if (model.PasswordManager != null)
|
|
{
|
|
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
|
|
|
model.PasswordManager.ToPasswordManagerSubscriptionUpdate(organization);
|
|
await _organizationService.UpdateSubscription(organization.Id, (int)model.PasswordManager.Seats,
|
|
model.PasswordManager.MaxAutoScaleSeats);
|
|
if (model.PasswordManager.Storage.HasValue)
|
|
{
|
|
await _organizationService.AdjustStorageAsync(organization.Id, (short)model.PasswordManager.Storage);
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task<string> UpdateSecretsManagerAsync(OrganizationSubscriptionUpdateRequestModel model, Guid organizationId)
|
|
{
|
|
if (model.SecretsManager == null)
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
var organization = await _organizationRepository.GetByIdAsync(organizationId);
|
|
|
|
if (!organization.UseSecretsManager)
|
|
{
|
|
return "Organization has no access to Secrets Manager.";
|
|
}
|
|
|
|
var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
|
|
var secretsManagerUpdate = model.SecretsManager.ToSecretsManagerSubscriptionUpdate(organization, plan);
|
|
await _updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(secretsManagerUpdate);
|
|
|
|
return string.Empty;
|
|
}
|
|
}
|