mirror of
https://github.com/bitwarden/server.git
synced 2025-05-08 05:02:21 -05:00
Add SmMaxProjects to OrganizationLicense (#5678)
* Add SmMaxProjects to OrganizationLicense * Run dotnet format
This commit is contained in:
parent
4b49b04409
commit
7fe022e26f
@ -1,9 +1,14 @@
|
|||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.Billing.Enums;
|
||||||
|
using Bit.Core.Billing.Licenses;
|
||||||
|
using Bit.Core.Billing.Licenses.Extensions;
|
||||||
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
using Bit.Core.SecretsManager.Queries.Projects.Interfaces;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
namespace Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
||||||
|
|
||||||
@ -11,13 +16,22 @@ public class MaxProjectsQuery : IMaxProjectsQuery
|
|||||||
{
|
{
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IProjectRepository _projectRepository;
|
private readonly IProjectRepository _projectRepository;
|
||||||
|
private readonly IGlobalSettings _globalSettings;
|
||||||
|
private readonly ILicensingService _licensingService;
|
||||||
|
private readonly IPricingClient _pricingClient;
|
||||||
|
|
||||||
public MaxProjectsQuery(
|
public MaxProjectsQuery(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IProjectRepository projectRepository)
|
IProjectRepository projectRepository,
|
||||||
|
IGlobalSettings globalSettings,
|
||||||
|
ILicensingService licensingService,
|
||||||
|
IPricingClient pricingClient)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_projectRepository = projectRepository;
|
_projectRepository = projectRepository;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
|
_licensingService = licensingService;
|
||||||
|
_pricingClient = pricingClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(short? max, bool? overMax)> GetByOrgIdAsync(Guid organizationId, int projectsToAdd)
|
public async Task<(short? max, bool? overMax)> GetByOrgIdAsync(Guid organizationId, int projectsToAdd)
|
||||||
@ -28,19 +42,47 @@ public class MaxProjectsQuery : IMaxProjectsQuery
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: PRICING -> https://bitwarden.atlassian.net/browse/PM-17122
|
var (planType, maxProjects) = await GetPlanTypeAndMaxProjectsAsync(org);
|
||||||
var plan = StaticStore.GetPlan(org.PlanType);
|
|
||||||
if (plan?.SecretsManager == null)
|
|
||||||
{
|
|
||||||
throw new BadRequestException("Existing plan not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (plan.Type == PlanType.Free)
|
if (planType != PlanType.Free)
|
||||||
{
|
{
|
||||||
var projects = await _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId);
|
|
||||||
return ((short? max, bool? overMax))(projects + projectsToAdd > plan.SecretsManager.MaxProjects ? (plan.SecretsManager.MaxProjects, true) : (plan.SecretsManager.MaxProjects, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var projects = await _projectRepository.GetProjectCountByOrganizationIdAsync(organizationId);
|
||||||
|
return ((short? max, bool? overMax))(projects + projectsToAdd > maxProjects ? (maxProjects, true) : (maxProjects, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<(PlanType planType, int maxProjects)> GetPlanTypeAndMaxProjectsAsync(Organization organization)
|
||||||
|
{
|
||||||
|
if (_globalSettings.SelfHosted)
|
||||||
|
{
|
||||||
|
var license = await _licensingService.ReadOrganizationLicenseAsync(organization);
|
||||||
|
|
||||||
|
if (license == null)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("License not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var claimsPrincipal = _licensingService.GetClaimsPrincipalFromLicense(license);
|
||||||
|
var maxProjects = claimsPrincipal.GetValue<int?>(OrganizationLicenseConstants.SmMaxProjects);
|
||||||
|
|
||||||
|
if (!maxProjects.HasValue)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("License does not contain a value for max Secrets Manager projects");
|
||||||
|
}
|
||||||
|
|
||||||
|
var planType = claimsPrincipal.GetValue<PlanType>(OrganizationLicenseConstants.PlanType);
|
||||||
|
return (planType, maxProjects.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var plan = await _pricingClient.GetPlan(organization.PlanType);
|
||||||
|
|
||||||
|
if (plan is { SupportsSecretsManager: true })
|
||||||
|
{
|
||||||
|
return (plan.Type, plan.SecretsManager.MaxProjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new BadRequestException("Existing plan not found.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
using Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
using System.Security.Claims;
|
||||||
|
using Bit.Commercial.Core.SecretsManager.Queries.Projects;
|
||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
|
using Bit.Core.Billing.Licenses;
|
||||||
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.SecretsManager.Repositories;
|
using Bit.Core.SecretsManager.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
@ -32,7 +39,7 @@ public class MaxProjectsQueryTests
|
|||||||
[BitAutoData(PlanType.FamiliesAnnually2019)]
|
[BitAutoData(PlanType.FamiliesAnnually2019)]
|
||||||
[BitAutoData(PlanType.Custom)]
|
[BitAutoData(PlanType.Custom)]
|
||||||
[BitAutoData(PlanType.FamiliesAnnually)]
|
[BitAutoData(PlanType.FamiliesAnnually)]
|
||||||
public async Task GetByOrgIdAsync_SmPlanIsNull_ThrowsBadRequest(PlanType planType,
|
public async Task GetByOrgIdAsync_Cloud_SmPlanIsNull_ThrowsBadRequest(PlanType planType,
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
{
|
{
|
||||||
organization.PlanType = planType;
|
organization.PlanType = planType;
|
||||||
@ -40,6 +47,34 @@ public class MaxProjectsQueryTests
|
|||||||
.GetByIdAsync(organization.Id)
|
.GetByIdAsync(organization.Id)
|
||||||
.Returns(organization);
|
.Returns(organization);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
||||||
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType).Returns(plan);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<BadRequestException>(
|
||||||
|
async () => await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1));
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IProjectRepository>()
|
||||||
|
.DidNotReceiveWithAnyArgs()
|
||||||
|
.GetProjectCountByOrganizationIdAsync(organization.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task GetByOrgIdAsync_SelfHosted_NoMaxProjectsClaim_ThrowsBadRequest(
|
||||||
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
|
{
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>()
|
||||||
|
.GetByIdAsync(organization.Id)
|
||||||
|
.Returns(organization);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||||
|
|
||||||
|
var license = new OrganizationLicense();
|
||||||
|
var claimsPrincipal = new ClaimsPrincipal();
|
||||||
|
sutProvider.GetDependency<ILicensingService>().ReadOrganizationLicenseAsync(organization).Returns(license);
|
||||||
|
sutProvider.GetDependency<ILicensingService>().GetClaimsPrincipalFromLicense(license).Returns(claimsPrincipal);
|
||||||
|
|
||||||
await Assert.ThrowsAsync<BadRequestException>(
|
await Assert.ThrowsAsync<BadRequestException>(
|
||||||
async () => await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1));
|
async () => await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1));
|
||||||
|
|
||||||
@ -62,12 +97,58 @@ public class MaxProjectsQueryTests
|
|||||||
[BitAutoData(PlanType.EnterpriseAnnually2019)]
|
[BitAutoData(PlanType.EnterpriseAnnually2019)]
|
||||||
[BitAutoData(PlanType.EnterpriseAnnually2020)]
|
[BitAutoData(PlanType.EnterpriseAnnually2020)]
|
||||||
[BitAutoData(PlanType.EnterpriseAnnually)]
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
||||||
public async Task GetByOrgIdAsync_SmNoneFreePlans_ReturnsNull(PlanType planType,
|
public async Task GetByOrgIdAsync_Cloud_SmNoneFreePlans_ReturnsNull(PlanType planType,
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
{
|
{
|
||||||
organization.PlanType = planType;
|
organization.PlanType = planType;
|
||||||
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
||||||
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType).Returns(plan);
|
||||||
|
|
||||||
|
var (limit, overLimit) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1);
|
||||||
|
|
||||||
|
Assert.Null(limit);
|
||||||
|
Assert.Null(overLimit);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IProjectRepository>().DidNotReceiveWithAnyArgs()
|
||||||
|
.GetProjectCountByOrganizationIdAsync(organization.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(PlanType.TeamsMonthly2019)]
|
||||||
|
[BitAutoData(PlanType.TeamsMonthly2020)]
|
||||||
|
[BitAutoData(PlanType.TeamsMonthly)]
|
||||||
|
[BitAutoData(PlanType.TeamsAnnually2019)]
|
||||||
|
[BitAutoData(PlanType.TeamsAnnually2020)]
|
||||||
|
[BitAutoData(PlanType.TeamsAnnually)]
|
||||||
|
[BitAutoData(PlanType.TeamsStarter)]
|
||||||
|
[BitAutoData(PlanType.EnterpriseMonthly2019)]
|
||||||
|
[BitAutoData(PlanType.EnterpriseMonthly2020)]
|
||||||
|
[BitAutoData(PlanType.EnterpriseMonthly)]
|
||||||
|
[BitAutoData(PlanType.EnterpriseAnnually2019)]
|
||||||
|
[BitAutoData(PlanType.EnterpriseAnnually2020)]
|
||||||
|
[BitAutoData(PlanType.EnterpriseAnnually)]
|
||||||
|
public async Task GetByOrgIdAsync_SelfHosted_SmNoneFreePlans_ReturnsNull(PlanType planType,
|
||||||
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
|
{
|
||||||
|
organization.PlanType = planType;
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||||
|
|
||||||
|
var license = new OrganizationLicense();
|
||||||
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
var claims = new List<Claim>
|
||||||
|
{
|
||||||
|
new (nameof(OrganizationLicenseConstants.PlanType), organization.PlanType.ToString()),
|
||||||
|
new (nameof(OrganizationLicenseConstants.SmMaxProjects), plan.SecretsManager.MaxProjects.ToString())
|
||||||
|
};
|
||||||
|
var identity = new ClaimsIdentity(claims, "TestAuthenticationType");
|
||||||
|
var claimsPrincipal = new ClaimsPrincipal(identity);
|
||||||
|
sutProvider.GetDependency<ILicensingService>().ReadOrganizationLicenseAsync(organization).Returns(license);
|
||||||
|
sutProvider.GetDependency<ILicensingService>().GetClaimsPrincipalFromLicense(license).Returns(claimsPrincipal);
|
||||||
|
|
||||||
var (limit, overLimit) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1);
|
var (limit, overLimit) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, 1);
|
||||||
|
|
||||||
Assert.Null(limit);
|
Assert.Null(limit);
|
||||||
@ -102,7 +183,7 @@ public class MaxProjectsQueryTests
|
|||||||
[BitAutoData(PlanType.Free, 3, 4, true)]
|
[BitAutoData(PlanType.Free, 3, 4, true)]
|
||||||
[BitAutoData(PlanType.Free, 4, 4, true)]
|
[BitAutoData(PlanType.Free, 4, 4, true)]
|
||||||
[BitAutoData(PlanType.Free, 40, 4, true)]
|
[BitAutoData(PlanType.Free, 40, 4, true)]
|
||||||
public async Task GetByOrgIdAsync_SmFreePlan__Success(PlanType planType, int projects, int projectsToAdd, bool expectedOverMax,
|
public async Task GetByOrgIdAsync_Cloud_SmFreePlan__Success(PlanType planType, int projects, int projectsToAdd, bool expectedOverMax,
|
||||||
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
{
|
{
|
||||||
organization.PlanType = planType;
|
organization.PlanType = planType;
|
||||||
@ -110,6 +191,67 @@ public class MaxProjectsQueryTests
|
|||||||
sutProvider.GetDependency<IProjectRepository>().GetProjectCountByOrganizationIdAsync(organization.Id)
|
sutProvider.GetDependency<IProjectRepository>().GetProjectCountByOrganizationIdAsync(organization.Id)
|
||||||
.Returns(projects);
|
.Returns(projects);
|
||||||
|
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(false);
|
||||||
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType).Returns(plan);
|
||||||
|
|
||||||
|
var (max, overMax) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, projectsToAdd);
|
||||||
|
|
||||||
|
Assert.NotNull(max);
|
||||||
|
Assert.NotNull(overMax);
|
||||||
|
Assert.Equal(3, max.Value);
|
||||||
|
Assert.Equal(expectedOverMax, overMax);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IProjectRepository>().Received(1)
|
||||||
|
.GetProjectCountByOrganizationIdAsync(organization.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData(PlanType.Free, 0, 1, false)]
|
||||||
|
[BitAutoData(PlanType.Free, 1, 1, false)]
|
||||||
|
[BitAutoData(PlanType.Free, 2, 1, false)]
|
||||||
|
[BitAutoData(PlanType.Free, 3, 1, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 4, 1, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 40, 1, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 0, 2, false)]
|
||||||
|
[BitAutoData(PlanType.Free, 1, 2, false)]
|
||||||
|
[BitAutoData(PlanType.Free, 2, 2, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 3, 2, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 4, 2, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 40, 2, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 0, 3, false)]
|
||||||
|
[BitAutoData(PlanType.Free, 1, 3, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 2, 3, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 3, 3, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 4, 3, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 40, 3, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 0, 4, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 1, 4, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 2, 4, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 3, 4, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 4, 4, true)]
|
||||||
|
[BitAutoData(PlanType.Free, 40, 4, true)]
|
||||||
|
public async Task GetByOrgIdAsync_SelfHosted_SmFreePlan__Success(PlanType planType, int projects, int projectsToAdd, bool expectedOverMax,
|
||||||
|
SutProvider<MaxProjectsQuery> sutProvider, Organization organization)
|
||||||
|
{
|
||||||
|
organization.PlanType = planType;
|
||||||
|
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
|
||||||
|
sutProvider.GetDependency<IProjectRepository>().GetProjectCountByOrganizationIdAsync(organization.Id)
|
||||||
|
.Returns(projects);
|
||||||
|
sutProvider.GetDependency<IGlobalSettings>().SelfHosted.Returns(true);
|
||||||
|
|
||||||
|
var license = new OrganizationLicense();
|
||||||
|
var plan = StaticStore.GetPlan(planType);
|
||||||
|
var claims = new List<Claim>
|
||||||
|
{
|
||||||
|
new (nameof(OrganizationLicenseConstants.PlanType), organization.PlanType.ToString()),
|
||||||
|
new (nameof(OrganizationLicenseConstants.SmMaxProjects), plan.SecretsManager.MaxProjects.ToString())
|
||||||
|
};
|
||||||
|
var identity = new ClaimsIdentity(claims, "TestAuthenticationType");
|
||||||
|
var claimsPrincipal = new ClaimsPrincipal(identity);
|
||||||
|
sutProvider.GetDependency<ILicensingService>().ReadOrganizationLicenseAsync(organization).Returns(license);
|
||||||
|
sutProvider.GetDependency<ILicensingService>().GetClaimsPrincipalFromLicense(license).Returns(claimsPrincipal);
|
||||||
|
|
||||||
var (max, overMax) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, projectsToAdd);
|
var (max, overMax) = await sutProvider.Sut.GetByOrgIdAsync(organization.Id, projectsToAdd);
|
||||||
|
|
||||||
Assert.NotNull(max);
|
Assert.NotNull(max);
|
||||||
|
@ -34,6 +34,7 @@ public static class OrganizationLicenseConstants
|
|||||||
public const string UseSecretsManager = nameof(UseSecretsManager);
|
public const string UseSecretsManager = nameof(UseSecretsManager);
|
||||||
public const string SmSeats = nameof(SmSeats);
|
public const string SmSeats = nameof(SmSeats);
|
||||||
public const string SmServiceAccounts = nameof(SmServiceAccounts);
|
public const string SmServiceAccounts = nameof(SmServiceAccounts);
|
||||||
|
public const string SmMaxProjects = nameof(SmMaxProjects);
|
||||||
public const string LimitCollectionCreationDeletion = nameof(LimitCollectionCreationDeletion);
|
public const string LimitCollectionCreationDeletion = nameof(LimitCollectionCreationDeletion);
|
||||||
public const string AllowAdminAccessToAllCollectionItems = nameof(AllowAdminAccessToAllCollectionItems);
|
public const string AllowAdminAccessToAllCollectionItems = nameof(AllowAdminAccessToAllCollectionItems);
|
||||||
public const string UseRiskInsights = nameof(UseRiskInsights);
|
public const string UseRiskInsights = nameof(UseRiskInsights);
|
||||||
|
@ -7,4 +7,5 @@ public class LicenseContext
|
|||||||
{
|
{
|
||||||
public Guid? InstallationId { get; init; }
|
public Guid? InstallationId { get; init; }
|
||||||
public required SubscriptionInfo SubscriptionInfo { get; init; }
|
public required SubscriptionInfo SubscriptionInfo { get; init; }
|
||||||
|
public int? SmMaxProjects { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,11 @@ public class OrganizationLicenseClaimsFactory : ILicenseClaimsFactory<Organizati
|
|||||||
}
|
}
|
||||||
claims.Add(new Claim(nameof(OrganizationLicenseConstants.UseAdminSponsoredFamilies), entity.UseAdminSponsoredFamilies.ToString()));
|
claims.Add(new Claim(nameof(OrganizationLicenseConstants.UseAdminSponsoredFamilies), entity.UseAdminSponsoredFamilies.ToString()));
|
||||||
|
|
||||||
|
if (licenseContext.SmMaxProjects.HasValue)
|
||||||
|
{
|
||||||
|
claims.Add(new Claim(nameof(OrganizationLicenseConstants.SmMaxProjects), licenseContext.SmMaxProjects.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
return Task.FromResult(claims);
|
return Task.FromResult(claims);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
@ -16,19 +17,22 @@ public class CloudGetOrganizationLicenseQuery : ICloudGetOrganizationLicenseQuer
|
|||||||
private readonly ILicensingService _licensingService;
|
private readonly ILicensingService _licensingService;
|
||||||
private readonly IProviderRepository _providerRepository;
|
private readonly IProviderRepository _providerRepository;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
|
private readonly IPricingClient _pricingClient;
|
||||||
|
|
||||||
public CloudGetOrganizationLicenseQuery(
|
public CloudGetOrganizationLicenseQuery(
|
||||||
IInstallationRepository installationRepository,
|
IInstallationRepository installationRepository,
|
||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
ILicensingService licensingService,
|
ILicensingService licensingService,
|
||||||
IProviderRepository providerRepository,
|
IProviderRepository providerRepository,
|
||||||
IFeatureService featureService)
|
IFeatureService featureService,
|
||||||
|
IPricingClient pricingClient)
|
||||||
{
|
{
|
||||||
_installationRepository = installationRepository;
|
_installationRepository = installationRepository;
|
||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_licensingService = licensingService;
|
_licensingService = licensingService;
|
||||||
_providerRepository = providerRepository;
|
_providerRepository = providerRepository;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
|
_pricingClient = pricingClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<OrganizationLicense> GetLicenseAsync(Organization organization, Guid installationId,
|
public async Task<OrganizationLicense> GetLicenseAsync(Organization organization, Guid installationId,
|
||||||
@ -42,7 +46,11 @@ public class CloudGetOrganizationLicenseQuery : ICloudGetOrganizationLicenseQuer
|
|||||||
|
|
||||||
var subscriptionInfo = await GetSubscriptionAsync(organization);
|
var subscriptionInfo = await GetSubscriptionAsync(organization);
|
||||||
var license = new OrganizationLicense(organization, subscriptionInfo, installationId, _licensingService, version);
|
var license = new OrganizationLicense(organization, subscriptionInfo, installationId, _licensingService, version);
|
||||||
license.Token = await _licensingService.CreateOrganizationTokenAsync(organization, installationId, subscriptionInfo);
|
var plan = await _pricingClient.GetPlan(organization.PlanType);
|
||||||
|
int? smMaxProjects = plan?.SupportsSecretsManager ?? false
|
||||||
|
? plan.SecretsManager.MaxProjects
|
||||||
|
: null;
|
||||||
|
license.Token = await _licensingService.CreateOrganizationTokenAsync(organization, installationId, subscriptionInfo, smMaxProjects);
|
||||||
|
|
||||||
return license;
|
return license;
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ public interface ILicensingService
|
|||||||
Task<string?> CreateOrganizationTokenAsync(
|
Task<string?> CreateOrganizationTokenAsync(
|
||||||
Organization organization,
|
Organization organization,
|
||||||
Guid installationId,
|
Guid installationId,
|
||||||
SubscriptionInfo subscriptionInfo);
|
SubscriptionInfo subscriptionInfo,
|
||||||
|
int? smMaxProjects);
|
||||||
|
|
||||||
Task<string?> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo);
|
Task<string?> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo);
|
||||||
}
|
}
|
||||||
|
@ -339,12 +339,13 @@ public class LicensingService : ILicensingService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo)
|
public async Task<string> CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo, int? smMaxProjects)
|
||||||
{
|
{
|
||||||
var licenseContext = new LicenseContext
|
var licenseContext = new LicenseContext
|
||||||
{
|
{
|
||||||
InstallationId = installationId,
|
InstallationId = installationId,
|
||||||
SubscriptionInfo = subscriptionInfo,
|
SubscriptionInfo = subscriptionInfo,
|
||||||
|
SmMaxProjects = smMaxProjects
|
||||||
};
|
};
|
||||||
|
|
||||||
var claims = await _organizationLicenseClaimsFactory.GenerateClaims(organization, licenseContext);
|
var claims = await _organizationLicenseClaimsFactory.GenerateClaims(organization, licenseContext);
|
||||||
|
@ -62,7 +62,7 @@ public class NoopLicensingService : ILicensingService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string?> CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo)
|
public Task<string?> CreateOrganizationTokenAsync(Organization organization, Guid installationId, SubscriptionInfo subscriptionInfo, int? smMaxProjects)
|
||||||
{
|
{
|
||||||
return Task.FromResult<string?>(null);
|
return Task.FromResult<string?>(null);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
|
using Bit.Core.Billing.Pricing;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
@ -8,6 +9,7 @@ using Bit.Core.OrganizationFeatures.OrganizationLicenses;
|
|||||||
using Bit.Core.Platform.Installations;
|
using Bit.Core.Platform.Installations;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Test.AutoFixture;
|
using Bit.Core.Test.AutoFixture;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
@ -76,8 +78,10 @@ public class CloudGetOrganizationLicenseQueryTests
|
|||||||
sutProvider.GetDependency<IInstallationRepository>().GetByIdAsync(installationId).Returns(installation);
|
sutProvider.GetDependency<IInstallationRepository>().GetByIdAsync(installationId).Returns(installation);
|
||||||
sutProvider.GetDependency<IPaymentService>().GetSubscriptionAsync(organization).Returns(subInfo);
|
sutProvider.GetDependency<IPaymentService>().GetSubscriptionAsync(organization).Returns(subInfo);
|
||||||
sutProvider.GetDependency<ILicensingService>().SignLicense(Arg.Any<ILicense>()).Returns(licenseSignature);
|
sutProvider.GetDependency<ILicensingService>().SignLicense(Arg.Any<ILicense>()).Returns(licenseSignature);
|
||||||
|
var plan = StaticStore.GetPlan(organization.PlanType);
|
||||||
|
sutProvider.GetDependency<IPricingClient>().GetPlan(organization.PlanType).Returns(plan);
|
||||||
sutProvider.GetDependency<ILicensingService>()
|
sutProvider.GetDependency<ILicensingService>()
|
||||||
.CreateOrganizationTokenAsync(organization, installationId, subInfo)
|
.CreateOrganizationTokenAsync(organization, installationId, subInfo, plan.SecretsManager.MaxProjects)
|
||||||
.Returns(token);
|
.Returns(token);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetLicenseAsync(organization, installationId);
|
var result = await sutProvider.Sut.GetLicenseAsync(organization, installationId);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user