mirror of
https://github.com/bitwarden/server.git
synced 2025-02-09 00:41:37 +01:00
validate organization licenses
This commit is contained in:
parent
127ff2d361
commit
4585af5a85
@ -37,13 +37,15 @@ namespace Bit.Core.IdentityServer
|
|||||||
|
|
||||||
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||||
{
|
{
|
||||||
|
await _licensingService.ValidateOrganizationsAsync();
|
||||||
|
|
||||||
var existingClaims = context.Subject.Claims;
|
var existingClaims = context.Subject.Claims;
|
||||||
var newClaims = new List<Claim>();
|
var newClaims = new List<Claim>();
|
||||||
|
|
||||||
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);
|
var isPremium = await _licensingService.ValidateUserPremiumAsync(user);
|
||||||
newClaims.AddRange(new List<Claim>
|
newClaims.AddRange(new List<Claim>
|
||||||
{
|
{
|
||||||
new Claim("premium", isPremium ? "true" : "false", ClaimValueTypes.Boolean),
|
new Claim("premium", isPremium ? "true" : "false", ClaimValueTypes.Boolean),
|
||||||
|
@ -140,7 +140,7 @@ namespace Bit.Core.Models.Business
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanUse(Guid installationId)
|
public bool CanUse(GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
@ -149,7 +149,7 @@ namespace Bit.Core.Models.Business
|
|||||||
|
|
||||||
if(Version == 1)
|
if(Version == 1)
|
||||||
{
|
{
|
||||||
return InstallationId == installationId && SelfHost;
|
return InstallationId == globalSettings.Installation.Id && SelfHost;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -157,7 +157,7 @@ namespace Bit.Core.Models.Business
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool VerifyData(Organization organization)
|
public bool VerifyData(Organization organization, GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
@ -167,6 +167,7 @@ namespace Bit.Core.Models.Business
|
|||||||
if(Version == 1)
|
if(Version == 1)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
globalSettings.Installation.Id == InstallationId &&
|
||||||
organization.LicenseKey.Equals(LicenseKey) &&
|
organization.LicenseKey.Equals(LicenseKey) &&
|
||||||
organization.Enabled == Enabled &&
|
organization.Enabled == Enabled &&
|
||||||
organization.PlanType == PlanType &&
|
organization.PlanType == PlanType &&
|
||||||
@ -175,7 +176,8 @@ namespace Bit.Core.Models.Business
|
|||||||
organization.UseGroups == UseGroups &&
|
organization.UseGroups == UseGroups &&
|
||||||
organization.UseDirectory == UseDirectory &&
|
organization.UseDirectory == UseDirectory &&
|
||||||
organization.UseTotp == UseTotp &&
|
organization.UseTotp == UseTotp &&
|
||||||
organization.SelfHost == SelfHost;
|
organization.SelfHost == SelfHost &&
|
||||||
|
organization.Name.Equals(Name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ namespace Bit.Core.Repositories
|
|||||||
{
|
{
|
||||||
public interface IOrganizationRepository : IRepository<Organization, Guid>
|
public interface IOrganizationRepository : IRepository<Organization, Guid>
|
||||||
{
|
{
|
||||||
|
Task<ICollection<Organization>> GetManyAsync();
|
||||||
Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId);
|
Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId);
|
||||||
Task UpdateStorageAsync(Guid id);
|
Task UpdateStorageAsync(Guid id);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,18 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
: base(connectionString)
|
: base(connectionString)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
public async Task<ICollection<Organization>> GetManyAsync()
|
||||||
|
{
|
||||||
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.QueryAsync<Organization>(
|
||||||
|
"[dbo].[Organization_Read]",
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId)
|
public async Task<ICollection<Organization>> GetManyByUserIdAsync(Guid userId)
|
||||||
{
|
{
|
||||||
using(var connection = new SqlConnection(ConnectionString))
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
@ -6,8 +6,8 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
public interface ILicensingService
|
public interface ILicensingService
|
||||||
{
|
{
|
||||||
bool VerifyOrganizationPlan(Organization organization);
|
Task ValidateOrganizationsAsync();
|
||||||
Task<bool> VerifyUserPremiumAsync(User user);
|
Task<bool> ValidateUserPremiumAsync(User user);
|
||||||
bool VerifyLicense(ILicense license);
|
bool VerifyLicense(ILicense license);
|
||||||
byte[] SignLicense(ILicense license);
|
byte[] SignLicense(ILicense license);
|
||||||
}
|
}
|
||||||
|
@ -548,7 +548,7 @@ namespace Bit.Core.Services
|
|||||||
throw new BadRequestException("Invalid license.");
|
throw new BadRequestException("Invalid license.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!license.CanUse(_globalSettings.Installation.Id))
|
if(!license.CanUse(_globalSettings))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid license. Make sure your license allows for on-premise " +
|
throw new BadRequestException("Invalid license. Make sure your license allows for on-premise " +
|
||||||
"hosting of organizations and that the installation id matches your current installation.");
|
"hosting of organizations and that the installation id matches your current installation.");
|
||||||
@ -655,7 +655,7 @@ namespace Bit.Core.Services
|
|||||||
throw new BadRequestException("Invalid license.");
|
throw new BadRequestException("Invalid license.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!license.CanUse(_globalSettings.Installation.Id))
|
if(!license.CanUse(_globalSettings))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Invalid license. Make sure your license allows for on-premise " +
|
throw new BadRequestException("Invalid license. Make sure your license allows for on-premise " +
|
||||||
"hosting of organizations and that the installation id matches your current installation.");
|
"hosting of organizations and that the installation id matches your current installation.");
|
||||||
|
@ -7,6 +7,7 @@ using Newtonsoft.Json;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -17,16 +18,20 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
private readonly X509Certificate2 _certificate;
|
private readonly X509Certificate2 _certificate;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private IDictionary<Guid, DateTime> _userCheckCache = new Dictionary<Guid, DateTime>();
|
|
||||||
private IDictionary<Guid, DateTime> _organizationCheckCache = new Dictionary<Guid, DateTime>();
|
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
|
||||||
|
private IDictionary<Guid, DateTime> _userCheckCache = new Dictionary<Guid, DateTime>();
|
||||||
|
private DateTime? _organizationCheckCache = null;
|
||||||
|
|
||||||
public RsaLicensingService(
|
public RsaLicensingService(
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
IHostingEnvironment environment,
|
IHostingEnvironment environment,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
|
||||||
var certThumbprint = "207e64a231e8aa32aaf68a61037c075ebebd553f";
|
var certThumbprint = "207e64a231e8aa32aaf68a61037c075ebebd553f";
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
@ -44,23 +49,35 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool VerifyOrganizationPlan(Organization organization)
|
public async Task ValidateOrganizationsAsync()
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted)
|
if(!_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!organization.SelfHost)
|
var now = DateTime.UtcNow;
|
||||||
|
if(_organizationCheckCache.HasValue && now - _organizationCheckCache.Value < TimeSpan.FromDays(1))
|
||||||
{
|
{
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
_organizationCheckCache = now;
|
||||||
|
|
||||||
var license = ReadOrganiztionLicense(organization);
|
var orgs = await _organizationRepository.GetManyAsync();
|
||||||
return license != null && license.VerifyData(organization) && license.VerifySignature(_certificate);
|
foreach(var org in orgs.Where(o => o.Enabled))
|
||||||
|
{
|
||||||
|
var license = ReadOrganiztionLicense(org);
|
||||||
|
if(license == null || !license.VerifyData(org, _globalSettings) || !license.VerifySignature(_certificate))
|
||||||
|
{
|
||||||
|
org.Enabled = false;
|
||||||
|
org.ExpirationDate = license.Expires;
|
||||||
|
org.RevisionDate = DateTime.UtcNow;
|
||||||
|
await _organizationRepository.ReplaceAsync(org);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> VerifyUserPremiumAsync(User user)
|
public async Task<bool> ValidateUserPremiumAsync(User user)
|
||||||
{
|
{
|
||||||
if(!_globalSettings.SelfHosted)
|
if(!_globalSettings.SelfHosted)
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ namespace Bit.Core.Services
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> VerifyUserPremiumAsync(User user)
|
public Task<bool> ValidateUserPremiumAsync(User user)
|
||||||
{
|
{
|
||||||
return Task.FromResult(user.Premium);
|
return Task.FromResult(user.Premium);
|
||||||
}
|
}
|
||||||
|
11
src/Sql/dbo/Stored Procedures/Organization_Read.sql
Normal file
11
src/Sql/dbo/Stored Procedures/Organization_Read.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[Organization_Read]
|
||||||
|
@Id UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
*
|
||||||
|
FROM
|
||||||
|
[dbo].[OrganizationView]
|
||||||
|
END
|
Loading…
Reference in New Issue
Block a user