diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 3ca4044995..6d7991a998 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -23,7 +23,6 @@ namespace Bit.Api.Controllers private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationService _organizationService; private readonly IUserService _userService; - private readonly ILicensingService _licensingService; private readonly CurrentContext _currentContext; private readonly GlobalSettings _globalSettings; private readonly UserManager _userManager; @@ -33,7 +32,6 @@ namespace Bit.Api.Controllers IOrganizationUserRepository organizationUserRepository, IOrganizationService organizationService, IUserService userService, - ILicensingService licensingService, CurrentContext currentContext, GlobalSettings globalSettings, UserManager userManager) @@ -44,7 +42,6 @@ namespace Bit.Api.Controllers _userService = userService; _currentContext = currentContext; _userManager = userManager; - _licensingService = licensingService; _globalSettings = globalSettings; } @@ -107,15 +104,13 @@ namespace Bit.Api.Controllers throw new NotFoundException(); } - var organization = await _organizationRepository.GetByIdAsync(orgIdGuid); - if(organization == null) + var license = await _organizationService.GenerateLicenseAsync(orgIdGuid, installationId); + if(license == null) { throw new NotFoundException(); } - var paymentService = new StripePaymentService(); - var billingInfo = await paymentService.GetBillingAsync(organization); - return new OrganizationLicense(organization, billingInfo, installationId, _licensingService); + return license; } [HttpGet("")] diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs index 48054f6b73..75b2229b89 100644 --- a/src/Core/Models/Business/OrganizationLicense.cs +++ b/src/Core/Models/Business/OrganizationLicense.cs @@ -1,6 +1,7 @@ using Bit.Core.Enums; using Bit.Core.Models.Table; using Bit.Core.Services; +using Newtonsoft.Json; using System; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -39,7 +40,7 @@ namespace Bit.Core.Models.Business Trial = true; } else if(billingInfo.Subscription.TrialEndDate.HasValue && - billingInfo.Subscription.TrialEndDate.Value < DateTime.UtcNow) + billingInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow) { Expires = Refresh = billingInfo.Subscription.TrialEndDate.Value; Trial = true; @@ -89,6 +90,7 @@ namespace Bit.Core.Models.Business public DateTime? Expires { get; set; } public bool Trial { get; set; } public string Signature { get; set; } + [JsonIgnore] public byte[] SignatureBytes => Convert.FromBase64String(Signature); public byte[] GetSignatureData() @@ -124,19 +126,14 @@ namespace Bit.Core.Models.Business public bool CanUse(Guid installationId) { - if(Issued > DateTime.UtcNow) - { - return false; - } - - if(Expires < DateTime.UtcNow) + if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow) { return false; } if(Version == 1) { - return InstallationId == installationId; + return InstallationId == installationId && SelfHost; } else { @@ -146,12 +143,7 @@ namespace Bit.Core.Models.Business public bool VerifyData(Organization organization) { - if(Issued > DateTime.UtcNow) - { - return false; - } - - if(Expires < DateTime.UtcNow) + if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow) { return false; } @@ -185,7 +177,15 @@ namespace Bit.Core.Models.Business public byte[] Sign(X509Certificate2 certificate) { - throw new NotImplementedException(); + if(!certificate.HasPrivateKey) + { + throw new InvalidOperationException("You don't have the private key!"); + } + + using(var rsa = certificate.GetRSAPrivateKey()) + { + return rsa.SignData(GetSignatureData(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } } } } diff --git a/src/Core/Models/Business/UserLicense.cs b/src/Core/Models/Business/UserLicense.cs index ea33693f07..5ca3099c78 100644 --- a/src/Core/Models/Business/UserLicense.cs +++ b/src/Core/Models/Business/UserLicense.cs @@ -70,19 +70,14 @@ namespace Bit.Core.Models.Business public bool CanUse(User user) { - if(Issued > DateTime.UtcNow) - { - return false; - } - - if(Expires < DateTime.UtcNow) + if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow) { return false; } if(Version == 1) { - return user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase); + return user.EmailVerified && user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase); } else { @@ -92,12 +87,7 @@ namespace Bit.Core.Models.Business public bool VerifyData(User user) { - if(Issued > DateTime.UtcNow) - { - return false; - } - - if(Expires < DateTime.UtcNow) + if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow) { return false; } diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs index fa5ebf44f7..94b43227cc 100644 --- a/src/Core/Models/StaticStore/Plan.cs +++ b/src/Core/Models/StaticStore/Plan.cs @@ -22,5 +22,6 @@ namespace Bit.Core.Models.StaticStore public int UpgradeSortOrder { get; set; } public bool Disabled { get; set; } public int? TrialPeriodDays { get; set; } + public bool SelfHost { get; set; } } } diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 68b78252db..7323407319 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -35,6 +35,7 @@ namespace Bit.Core.Services Task SaveUserAsync(OrganizationUser user, Guid savingUserId, IEnumerable collections); Task DeleteUserAsync(Guid organizationId, Guid organizationUserId, Guid deletingUserId); Task DeleteUserAsync(Guid organizationId, Guid userId); + Task GenerateLicenseAsync(Guid organizationId, Guid installationId); Task ImportAsync(Guid organizationId, Guid importingUserId, IEnumerable groups, IEnumerable newUsers, IEnumerable removeUserExternalIds); } diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index a81b6cdbd6..8ccfb3b327 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -29,6 +29,7 @@ namespace Bit.Core.Services private readonly IPushRegistrationService _pushRegistrationService; private readonly IDeviceRepository _deviceRepository; private readonly ILicensingService _licensingService; + private readonly IInstallationRepository _installationRepository; private readonly StripePaymentService _stripePaymentService; private readonly GlobalSettings _globalSettings; @@ -44,6 +45,7 @@ namespace Bit.Core.Services IPushRegistrationService pushRegistrationService, IDeviceRepository deviceRepository, ILicensingService licensingService, + IInstallationRepository installationRepository, GlobalSettings globalSettings) { _organizationRepository = organizationRepository; @@ -57,6 +59,7 @@ namespace Bit.Core.Services _pushRegistrationService = pushRegistrationService; _deviceRepository = deviceRepository; _licensingService = licensingService; + _installationRepository = installationRepository; _stripePaymentService = new StripePaymentService(); _globalSettings = globalSettings; } @@ -522,6 +525,7 @@ namespace Bit.Core.Services UseGroups = plan.UseGroups, UseDirectory = plan.UseDirectory, UseTotp = plan.UseTotp, + SelfHost = plan.SelfHost, Plan = plan.Name, Gateway = GatewayType.Stripe, GatewayCustomerId = customer?.Id, @@ -563,6 +567,7 @@ namespace Bit.Core.Services UseDirectory = license.UseDirectory, UseTotp = license.UseTotp, Plan = license.Plan, + SelfHost = license.SelfHost, Gateway = null, GatewayCustomerId = null, GatewaySubscriptionId = null, @@ -999,6 +1004,25 @@ namespace Bit.Core.Services } } + public async Task GenerateLicenseAsync(Guid organizationId, Guid installationId) + { + var organization = await _organizationRepository.GetByIdAsync(organizationId); + if(organization == null) + { + throw new NotFoundException(); + } + + var installation = await _installationRepository.GetByIdAsync(installationId); + if(installation == null || !installation.Enabled) + { + throw new BadRequestException("Invalid installation id"); + } + + var paymentService = new StripePaymentService(); + var billingInfo = await paymentService.GetBillingAsync(organization); + return new OrganizationLicense(organization, billingInfo, installationId, _licensingService); + } + public async Task ImportAsync(Guid organizationId, Guid importingUserId, IEnumerable groups, diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs index e6ba3b928d..6e98dddc65 100644 --- a/src/Core/Utilities/StaticStore.cs +++ b/src/Core/Utilities/StaticStore.cs @@ -164,7 +164,8 @@ namespace Bit.Core.Utilities UseGroups = true, UseDirectory = true, UseTotp = true, - MaxStorageGb = 1 + MaxStorageGb = 1, + SelfHost = true }, new Plan { @@ -182,7 +183,8 @@ namespace Bit.Core.Utilities UseGroups = true, UseDirectory = true, UseTotp = true, - MaxStorageGb = 1 + MaxStorageGb = 1, + SelfHost = true } };