mirror of
https://github.com/bitwarden/server.git
synced 2025-02-16 01:51:21 +01:00
Added the ability to create a JWT on a user license that contains all license properties as claims
This commit is contained in:
parent
02fd8b0f3e
commit
a473487e19
@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.Billing.Licenses.Services;
|
using Bit.Core.Billing.Licenses.Services;
|
||||||
using Bit.Core.Billing.Licenses.Services.Implementations;
|
using Bit.Core.Billing.Licenses.Services.Implementations;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Bit.Core.Billing.Licenses.Extensions;
|
namespace Bit.Core.Billing.Licenses.Extensions;
|
||||||
@ -10,5 +11,6 @@ public static class LicenseServiceCollectionExtensions
|
|||||||
public static void AddLicenseServices(this IServiceCollection services)
|
public static void AddLicenseServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddTransient<ILicenseClaimsFactory<Organization>, OrganizationLicenseClaimsFactory>();
|
services.AddTransient<ILicenseClaimsFactory<Organization>, OrganizationLicenseClaimsFactory>();
|
||||||
|
services.AddTransient<ILicenseClaimsFactory<User>, UserLicenseClaimsFactory>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Bit.Core.Billing.Licenses.Models;
|
||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
|
|
||||||
|
namespace Bit.Core.Billing.Licenses.Services.Implementations;
|
||||||
|
|
||||||
|
public class UserLicenseClaimsFactory : ILicenseClaimsFactory<User>
|
||||||
|
{
|
||||||
|
public Task<List<Claim>> GenerateClaims(User entity, LicenseContext licenseContext)
|
||||||
|
{
|
||||||
|
var subscriptionInfo = licenseContext.SubscriptionInfo;
|
||||||
|
|
||||||
|
var expires = subscriptionInfo.UpcomingInvoice?.Date?.AddDays(7) ?? entity.PremiumExpirationDate?.AddDays(7);
|
||||||
|
var refresh = subscriptionInfo.UpcomingInvoice?.Date ?? entity.PremiumExpirationDate;
|
||||||
|
var trial = (subscriptionInfo.Subscription?.TrialEndDate.HasValue ?? false) &&
|
||||||
|
subscriptionInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow;
|
||||||
|
|
||||||
|
var claims = new List<Claim>
|
||||||
|
{
|
||||||
|
new(nameof(UserLicense.LicenseType), LicenseType.User.ToString()),
|
||||||
|
new(nameof(UserLicense.LicenseKey), entity.LicenseKey),
|
||||||
|
new(nameof(UserLicense.Id), entity.Id.ToString()),
|
||||||
|
new(nameof(UserLicense.Name), entity.Name),
|
||||||
|
new(nameof(UserLicense.Email), entity.Email),
|
||||||
|
new(nameof(UserLicense.Premium), entity.Premium.ToString()),
|
||||||
|
new(nameof(UserLicense.MaxStorageGb), entity.MaxStorageGb.ToString()),
|
||||||
|
new(nameof(UserLicense.Issued), DateTime.UtcNow.ToString(CultureInfo.InvariantCulture)),
|
||||||
|
new(nameof(UserLicense.Expires), expires.ToString()),
|
||||||
|
new(nameof(UserLicense.Refresh), refresh.ToString()),
|
||||||
|
new(nameof(UserLicense.Trial), trial.ToString()),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Task.FromResult(claims);
|
||||||
|
}
|
||||||
|
}
|
@ -70,6 +70,7 @@ public class UserLicense : ILicense
|
|||||||
public LicenseType? LicenseType { get; set; }
|
public LicenseType? LicenseType { get; set; }
|
||||||
public string Hash { get; set; }
|
public string Hash { get; set; }
|
||||||
public string Signature { get; set; }
|
public string Signature { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
||||||
|
|
||||||
@ -84,6 +85,7 @@ public class UserLicense : ILicense
|
|||||||
!p.Name.Equals(nameof(Signature)) &&
|
!p.Name.Equals(nameof(Signature)) &&
|
||||||
!p.Name.Equals(nameof(SignatureBytes)) &&
|
!p.Name.Equals(nameof(SignatureBytes)) &&
|
||||||
!p.Name.Equals(nameof(LicenseType)) &&
|
!p.Name.Equals(nameof(LicenseType)) &&
|
||||||
|
!p.Name.Equals(nameof(Token)) &&
|
||||||
(
|
(
|
||||||
!forHash ||
|
!forHash ||
|
||||||
(
|
(
|
||||||
|
@ -18,4 +18,6 @@ public interface ILicensingService
|
|||||||
Organization organization,
|
Organization organization,
|
||||||
Guid installationId,
|
Guid installationId,
|
||||||
SubscriptionInfo subscriptionInfo);
|
SubscriptionInfo subscriptionInfo);
|
||||||
|
|
||||||
|
Task<string> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo);
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,10 @@ public class LicensingService : ILicensingService
|
|||||||
private readonly IGlobalSettings _globalSettings;
|
private readonly IGlobalSettings _globalSettings;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
||||||
private readonly IMailService _mailService;
|
private readonly IMailService _mailService;
|
||||||
private readonly ILogger<LicensingService> _logger;
|
private readonly ILogger<LicensingService> _logger;
|
||||||
private readonly ILicenseClaimsFactory<Organization> _organizationLicenseClaimsFactory;
|
private readonly ILicenseClaimsFactory<Organization> _organizationLicenseClaimsFactory;
|
||||||
|
private readonly ILicenseClaimsFactory<User> _userLicenseClaimsFactory;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
|
|
||||||
private IDictionary<Guid, DateTime> _userCheckCache = new Dictionary<Guid, DateTime>();
|
private IDictionary<Guid, DateTime> _userCheckCache = new Dictionary<Guid, DateTime>();
|
||||||
@ -37,22 +37,22 @@ public class LicensingService : ILicensingService
|
|||||||
public LicensingService(
|
public LicensingService(
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
|
||||||
IMailService mailService,
|
IMailService mailService,
|
||||||
IWebHostEnvironment environment,
|
IWebHostEnvironment environment,
|
||||||
ILogger<LicensingService> logger,
|
ILogger<LicensingService> logger,
|
||||||
IGlobalSettings globalSettings,
|
IGlobalSettings globalSettings,
|
||||||
ILicenseClaimsFactory<Organization> organizationLicenseClaimsFactory,
|
ILicenseClaimsFactory<Organization> organizationLicenseClaimsFactory,
|
||||||
IFeatureService featureService)
|
IFeatureService featureService,
|
||||||
|
ILicenseClaimsFactory<User> userLicenseClaimsFactory)
|
||||||
{
|
{
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
|
||||||
_mailService = mailService;
|
_mailService = mailService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_organizationLicenseClaimsFactory = organizationLicenseClaimsFactory;
|
_organizationLicenseClaimsFactory = organizationLicenseClaimsFactory;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
|
_userLicenseClaimsFactory = userLicenseClaimsFactory;
|
||||||
|
|
||||||
var certThumbprint = environment.IsDevelopment() ?
|
var certThumbprint = environment.IsDevelopment() ?
|
||||||
"207E64A231E8AA32AAF68A61037C075EBEBD553F" :
|
"207E64A231E8AA32AAF68A61037C075EBEBD553F" :
|
||||||
@ -305,6 +305,21 @@ public class LicensingService : ILicensingService
|
|||||||
return GenerateToken(claims, audience, expires);
|
return GenerateToken(claims, audience, expires);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo)
|
||||||
|
{
|
||||||
|
if (!_featureService.IsEnabled(FeatureFlagKeys.SelfHostLicenseRefactor))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var licenseContext = new LicenseContext { SubscriptionInfo = subscriptionInfo };
|
||||||
|
var claims = await _userLicenseClaimsFactory.GenerateClaims(user, licenseContext);
|
||||||
|
var audience = user.Id.ToString();
|
||||||
|
var expires = user.PremiumExpirationDate ?? DateTime.UtcNow.AddDays(7);
|
||||||
|
|
||||||
|
return GenerateToken(claims, audience, expires);
|
||||||
|
}
|
||||||
|
|
||||||
private string GenerateToken(List<Claim> claims, string audience, DateTime expires)
|
private string GenerateToken(List<Claim> claims, string audience, DateTime expires)
|
||||||
{
|
{
|
||||||
if (claims.All(claim => claim.Type != JwtClaimTypes.JwtId))
|
if (claims.All(claim => claim.Type != JwtClaimTypes.JwtId))
|
||||||
|
@ -1111,7 +1111,9 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserLicense> GenerateLicenseAsync(User user, SubscriptionInfo subscriptionInfo = null,
|
public async Task<UserLicense> GenerateLicenseAsync(
|
||||||
|
User user,
|
||||||
|
SubscriptionInfo subscriptionInfo = null,
|
||||||
int? version = null)
|
int? version = null)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
@ -1124,8 +1126,13 @@ public class UserService : UserManager<User>, IUserService, IDisposable
|
|||||||
subscriptionInfo = await _paymentService.GetSubscriptionAsync(user);
|
subscriptionInfo = await _paymentService.GetSubscriptionAsync(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
return subscriptionInfo == null ? new UserLicense(user, _licenseService) :
|
var userLicense = subscriptionInfo == null
|
||||||
new UserLicense(user, subscriptionInfo, _licenseService);
|
? new UserLicense(user, _licenseService)
|
||||||
|
: new UserLicense(user, subscriptionInfo, _licenseService);
|
||||||
|
|
||||||
|
userLicense.Token = await _licenseService.CreateUserTokenAsync(user, subscriptionInfo);
|
||||||
|
|
||||||
|
return userLicense;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task<bool> CheckPasswordAsync(User user, string password)
|
public override async Task<bool> CheckPasswordAsync(User user, string password)
|
||||||
|
@ -58,4 +58,9 @@ public class NoopLicensingService : ILicensingService
|
|||||||
{
|
{
|
||||||
return Task.FromResult<string>(null);
|
return Task.FromResult<string>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<string> CreateUserTokenAsync(User user, SubscriptionInfo subscriptionInfo)
|
||||||
|
{
|
||||||
|
return Task.FromResult<string>(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user