1
0
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:
Kyle Spearrin 2017-08-16 17:08:20 -04:00
parent cdc5310fc3
commit b14f6d080e
8 changed files with 54 additions and 34 deletions

View File

@ -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)

View File

@ -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 &&

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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);
} }
} }

View File

@ -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;
} }
} }
} }

View File

@ -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;

View File

@ -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)