mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 13:38:13 -05:00
verify and disable premium from license check
This commit is contained in:
parent
cdc5310fc3
commit
b14f6d080e
@ -18,17 +18,20 @@ namespace Bit.Core.IdentityServer
|
|||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly ILicensingService _licensingService;
|
||||||
private IdentityOptions _identityOptions;
|
private IdentityOptions _identityOptions;
|
||||||
|
|
||||||
public ProfileService(
|
public ProfileService(
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ILicensingService licensingService,
|
||||||
IOptions<IdentityOptions> identityOptionsAccessor)
|
IOptions<IdentityOptions> identityOptionsAccessor)
|
||||||
{
|
{
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
_userService = userService;
|
_userService = userService;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
_licensingService = licensingService;
|
||||||
_identityOptions = identityOptionsAccessor?.Value ?? new IdentityOptions();
|
_identityOptions = identityOptionsAccessor?.Value ?? new IdentityOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,9 +43,10 @@ namespace Bit.Core.IdentityServer
|
|||||||
var user = await _userService.GetUserByPrincipalAsync(context.Subject);
|
var user = await _userService.GetUserByPrincipalAsync(context.Subject);
|
||||||
if(user != null)
|
if(user != null)
|
||||||
{
|
{
|
||||||
|
var isPremium = await _licensingService.VerifyUserPremiumAsync(user);
|
||||||
newClaims.AddRange(new List<Claim>
|
newClaims.AddRange(new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim("premium", user.Premium ? "true" : "false", ClaimValueTypes.Boolean),
|
new Claim("premium", isPremium ? "true" : "false", ClaimValueTypes.Boolean),
|
||||||
new Claim(JwtClaimTypes.Email, user.Email),
|
new Claim(JwtClaimTypes.Email, user.Email),
|
||||||
new Claim(JwtClaimTypes.EmailVerified, user.EmailVerified ? "true" : "false", ClaimValueTypes.Boolean),
|
new Claim(JwtClaimTypes.EmailVerified, user.EmailVerified ? "true" : "false", ClaimValueTypes.Boolean),
|
||||||
new Claim(_identityOptions.ClaimsIdentity.SecurityStampClaimType, user.SecurityStamp)
|
new Claim(_identityOptions.ClaimsIdentity.SecurityStampClaimType, user.SecurityStamp)
|
||||||
|
@ -167,7 +167,7 @@ namespace Bit.Core.Models.Business
|
|||||||
if(Version == 1)
|
if(Version == 1)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
organization.LicenseKey.Equals(LicenseKey, StringComparison.InvariantCultureIgnoreCase) &&
|
organization.LicenseKey.Equals(LicenseKey) &&
|
||||||
organization.Enabled == Enabled &&
|
organization.Enabled == Enabled &&
|
||||||
organization.PlanType == PlanType &&
|
organization.PlanType == PlanType &&
|
||||||
organization.Seats == Seats &&
|
organization.Seats == Seats &&
|
||||||
|
@ -116,7 +116,7 @@ namespace Bit.Core.Models.Business
|
|||||||
if(Version == 1)
|
if(Version == 1)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
user.LicenseKey.Equals(LicenseKey, StringComparison.InvariantCultureIgnoreCase) &&
|
user.LicenseKey.Equals(LicenseKey) &&
|
||||||
user.Premium == Premium &&
|
user.Premium == Premium &&
|
||||||
user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
|
user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
public interface ILicensingService
|
public interface ILicensingService
|
||||||
{
|
{
|
||||||
bool VerifyOrganizationPlan(Organization organization);
|
bool VerifyOrganizationPlan(Organization organization);
|
||||||
bool VerifyUserPremium(User user);
|
Task<bool> VerifyUserPremiumAsync(User user);
|
||||||
bool VerifyLicense(ILicense license);
|
bool VerifyLicense(ILicense license);
|
||||||
byte[] SignLicense(ILicense license);
|
byte[] SignLicense(ILicense license);
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ namespace Bit.Core.Services
|
|||||||
Task CancelPremiumAsync(User user, bool endOfPeriod = false);
|
Task CancelPremiumAsync(User user, bool endOfPeriod = false);
|
||||||
Task ReinstatePremiumAsync(User user);
|
Task ReinstatePremiumAsync(User user);
|
||||||
Task DisablePremiumAsync(Guid userId, DateTime? expirationDate);
|
Task DisablePremiumAsync(Guid userId, DateTime? expirationDate);
|
||||||
|
Task DisablePremiumAsync(User user, DateTime? expirationDate);
|
||||||
Task UpdatePremiumExpirationAsync(Guid userId, DateTime? expirationDate);
|
Task UpdatePremiumExpirationAsync(Guid userId, DateTime? expirationDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@ -15,13 +16,17 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
private readonly X509Certificate2 _certificate;
|
private readonly X509Certificate2 _certificate;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private IDictionary<string, UserLicense> _userLicenseCache;
|
private IDictionary<Guid, DateTime> _userCheckCache = new Dictionary<Guid, DateTime>();
|
||||||
private IDictionary<string, OrganizationLicense> _organizationLicenseCache;
|
private IDictionary<Guid, DateTime> _organizationCheckCache = new Dictionary<Guid, DateTime>();
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public RsaLicensingService(
|
public RsaLicensingService(
|
||||||
|
IUserService userService,
|
||||||
IHostingEnvironment environment,
|
IHostingEnvironment environment,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
|
_userService = userService;
|
||||||
|
|
||||||
var certThumbprint = "207e64a231e8aa32aaf68a61037c075ebebd553f";
|
var certThumbprint = "207e64a231e8aa32aaf68a61037c075ebebd553f";
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
_certificate = !_globalSettings.SelfHosted ? CoreHelpers.GetCertificate(certThumbprint)
|
_certificate = !_globalSettings.SelfHosted ? CoreHelpers.GetCertificate(certThumbprint)
|
||||||
@ -54,7 +59,7 @@ namespace Bit.Core.Services
|
|||||||
return license != null && license.VerifyData(organization) && license.VerifySignature(_certificate);
|
return license != null && license.VerifyData(organization) && license.VerifySignature(_certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool VerifyUserPremium(User user)
|
public async Task<bool> VerifyUserPremiumAsync(User user)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted)
|
if(!_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
@ -66,8 +71,33 @@ namespace Bit.Core.Services
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only check once per day
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
if(_userCheckCache.ContainsKey(user.Id))
|
||||||
|
{
|
||||||
|
var lastCheck = _userCheckCache[user.Id];
|
||||||
|
if(lastCheck < now && now - lastCheck < TimeSpan.FromDays(1))
|
||||||
|
{
|
||||||
|
return user.Premium;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_userCheckCache[user.Id] = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_userCheckCache.Add(user.Id, now);
|
||||||
|
}
|
||||||
|
|
||||||
var license = ReadUserLicense(user);
|
var license = ReadUserLicense(user);
|
||||||
return license != null && license.VerifyData(user) && license.VerifySignature(_certificate);
|
var licensedForPremium = license != null && license.VerifyData(user) && license.VerifySignature(_certificate);
|
||||||
|
if(!licensedForPremium)
|
||||||
|
{
|
||||||
|
await _userService.DisablePremiumAsync(user, license.Expires);
|
||||||
|
}
|
||||||
|
|
||||||
|
return licensedForPremium;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool VerifyLicense(ILicense license)
|
public bool VerifyLicense(ILicense license)
|
||||||
@ -87,11 +117,6 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
private UserLicense ReadUserLicense(User user)
|
private UserLicense ReadUserLicense(User user)
|
||||||
{
|
{
|
||||||
if(_userLicenseCache != null && _userLicenseCache.ContainsKey(user.LicenseKey))
|
|
||||||
{
|
|
||||||
return _userLicenseCache[user.LicenseKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
var filePath = $"{_globalSettings.LicenseDirectory}/user/{user.Id}.json";
|
var filePath = $"{_globalSettings.LicenseDirectory}/user/{user.Id}.json";
|
||||||
if(!File.Exists(filePath))
|
if(!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
@ -99,22 +124,11 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
var data = File.ReadAllText(filePath, Encoding.UTF8);
|
var data = File.ReadAllText(filePath, Encoding.UTF8);
|
||||||
var obj = JsonConvert.DeserializeObject<UserLicense>(data);
|
return JsonConvert.DeserializeObject<UserLicense>(data);
|
||||||
if(_userLicenseCache == null)
|
|
||||||
{
|
|
||||||
_userLicenseCache = new Dictionary<string, UserLicense>();
|
|
||||||
}
|
|
||||||
_userLicenseCache.Add(obj.LicenseKey, obj);
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private OrganizationLicense ReadOrganiztionLicense(Organization organization)
|
private OrganizationLicense ReadOrganiztionLicense(Organization organization)
|
||||||
{
|
{
|
||||||
if(_organizationLicenseCache != null && _organizationLicenseCache.ContainsKey(organization.LicenseKey))
|
|
||||||
{
|
|
||||||
return _organizationLicenseCache[organization.LicenseKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
var filePath = $"{_globalSettings.LicenseDirectory}/organization/{organization.Id}.json";
|
var filePath = $"{_globalSettings.LicenseDirectory}/organization/{organization.Id}.json";
|
||||||
if(!File.Exists(filePath))
|
if(!File.Exists(filePath))
|
||||||
{
|
{
|
||||||
@ -122,13 +136,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
var data = File.ReadAllText(filePath, Encoding.UTF8);
|
var data = File.ReadAllText(filePath, Encoding.UTF8);
|
||||||
var obj = JsonConvert.DeserializeObject<OrganizationLicense>(data);
|
return JsonConvert.DeserializeObject<OrganizationLicense>(data);
|
||||||
if(_organizationLicenseCache == null)
|
|
||||||
{
|
|
||||||
_organizationLicenseCache = new Dictionary<string, OrganizationLicense>();
|
|
||||||
}
|
|
||||||
_organizationLicenseCache.Add(obj.LicenseKey, obj);
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -691,6 +691,11 @@ namespace Bit.Core.Services
|
|||||||
public async Task DisablePremiumAsync(Guid userId, DateTime? expirationDate)
|
public async Task DisablePremiumAsync(Guid userId, DateTime? expirationDate)
|
||||||
{
|
{
|
||||||
var user = await _userRepository.GetByIdAsync(userId);
|
var user = await _userRepository.GetByIdAsync(userId);
|
||||||
|
await DisablePremiumAsync(user, expirationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DisablePremiumAsync(User user, DateTime? expirationDate)
|
||||||
|
{
|
||||||
if(user != null && user.Premium)
|
if(user != null && user.Premium)
|
||||||
{
|
{
|
||||||
user.Premium = false;
|
user.Premium = false;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using System;
|
using System;
|
||||||
using Bit.Core.Models.Business;
|
using Bit.Core.Models.Business;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@ -27,9 +28,9 @@ namespace Bit.Core.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool VerifyUserPremium(User user)
|
public Task<bool> VerifyUserPremiumAsync(User user)
|
||||||
{
|
{
|
||||||
return user.Premium;
|
return Task.FromResult(user.Premium);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] SignLicense(ILicense license)
|
public byte[] SignLicense(ILicense license)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user