mirror of
https://github.com/bitwarden/server.git
synced 2025-02-27 03:41:30 +01:00
license hash
This commit is contained in:
parent
92ab3db4c4
commit
d9cd7880a6
@ -11,8 +11,11 @@ namespace Bit.Core.Models.Business
|
|||||||
DateTime? Refresh { get; set; }
|
DateTime? Refresh { get; set; }
|
||||||
DateTime? Expires { get; set; }
|
DateTime? Expires { get; set; }
|
||||||
bool Trial { get; set; }
|
bool Trial { get; set; }
|
||||||
|
string Hash { get; set; }
|
||||||
string Signature { get; set; }
|
string Signature { get; set; }
|
||||||
byte[] GetSignatureData();
|
byte[] SignatureBytes { get; }
|
||||||
|
byte[] GetDataBytes(bool forHash = false);
|
||||||
|
byte[] ComputeHash();
|
||||||
bool VerifySignature(X509Certificate2 certificate);
|
bool VerifySignature(X509Certificate2 certificate);
|
||||||
byte[] Sign(X509Certificate2 certificate);
|
byte[] Sign(X509Certificate2 certificate);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ namespace Bit.Core.Models.Business
|
|||||||
Trial = false;
|
Trial = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Hash = Convert.ToBase64String(ComputeHash());
|
||||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,18 +96,29 @@ namespace Bit.Core.Models.Business
|
|||||||
public DateTime? Refresh { get; set; }
|
public DateTime? Refresh { get; set; }
|
||||||
public DateTime? Expires { get; set; }
|
public DateTime? Expires { get; set; }
|
||||||
public bool Trial { get; set; }
|
public bool Trial { get; set; }
|
||||||
|
public string Hash { get; set; }
|
||||||
public string Signature { get; set; }
|
public string Signature { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
||||||
|
|
||||||
public byte[] GetSignatureData()
|
public byte[] GetDataBytes(bool forHash = false)
|
||||||
{
|
{
|
||||||
string data = null;
|
string data = null;
|
||||||
if(Version == 1)
|
if(Version == 1)
|
||||||
{
|
{
|
||||||
var props = typeof(OrganizationLicense)
|
var props = typeof(OrganizationLicense)
|
||||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.Where(p => !p.Name.Equals(nameof(Signature)) && !p.Name.Equals(nameof(SignatureBytes)))
|
.Where(p =>
|
||||||
|
!p.Name.Equals(nameof(Signature)) &&
|
||||||
|
!p.Name.Equals(nameof(SignatureBytes)) &&
|
||||||
|
(
|
||||||
|
!forHash ||
|
||||||
|
(
|
||||||
|
!p.Name.Equals(nameof(Hash)) &&
|
||||||
|
!p.Name.Equals(nameof(Issued)) &&
|
||||||
|
!p.Name.Equals(nameof(Refresh))
|
||||||
|
)
|
||||||
|
))
|
||||||
.OrderBy(p => p.Name)
|
.OrderBy(p => p.Name)
|
||||||
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
||||||
.Aggregate((c, n) => $"{c}|{n}");
|
.Aggregate((c, n) => $"{c}|{n}");
|
||||||
@ -120,6 +132,14 @@ namespace Bit.Core.Models.Business
|
|||||||
return Encoding.UTF8.GetBytes(data);
|
return Encoding.UTF8.GetBytes(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] ComputeHash()
|
||||||
|
{
|
||||||
|
using(var alg = SHA256.Create())
|
||||||
|
{
|
||||||
|
return alg.ComputeHash(GetDataBytes(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanUse(Guid installationId)
|
public bool CanUse(Guid installationId)
|
||||||
{
|
{
|
||||||
if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
if(!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||||
@ -167,7 +187,7 @@ namespace Bit.Core.Models.Business
|
|||||||
{
|
{
|
||||||
using(var rsa = certificate.GetRSAPublicKey())
|
using(var rsa = certificate.GetRSAPublicKey())
|
||||||
{
|
{
|
||||||
return rsa.VerifyData(GetSignatureData(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +200,7 @@ namespace Bit.Core.Models.Business
|
|||||||
|
|
||||||
using(var rsa = certificate.GetRSAPrivateKey())
|
using(var rsa = certificate.GetRSAPrivateKey())
|
||||||
{
|
{
|
||||||
return rsa.SignData(GetSignatureData(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ namespace Bit.Core.Models.Business
|
|||||||
Refresh = billingInfo?.UpcomingInvoice?.Date;
|
Refresh = billingInfo?.UpcomingInvoice?.Date;
|
||||||
Trial = (billingInfo?.Subscription?.TrialEndDate.HasValue ?? false) &&
|
Trial = (billingInfo?.Subscription?.TrialEndDate.HasValue ?? false) &&
|
||||||
billingInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow;
|
billingInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow;
|
||||||
|
|
||||||
|
Hash = Convert.ToBase64String(ComputeHash());
|
||||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,18 +45,29 @@ namespace Bit.Core.Models.Business
|
|||||||
public DateTime? Refresh { get; set; }
|
public DateTime? Refresh { get; set; }
|
||||||
public DateTime? Expires { get; set; }
|
public DateTime? Expires { get; set; }
|
||||||
public bool Trial { get; set; }
|
public bool Trial { get; set; }
|
||||||
|
public string Hash { get; set; }
|
||||||
public string Signature { get; set; }
|
public string Signature { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
||||||
|
|
||||||
public byte[] GetSignatureData()
|
public byte[] GetDataBytes(bool forHash = false)
|
||||||
{
|
{
|
||||||
string data = null;
|
string data = null;
|
||||||
if(Version == 1)
|
if(Version == 1)
|
||||||
{
|
{
|
||||||
var props = typeof(UserLicense)
|
var props = typeof(UserLicense)
|
||||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.Where(p => !p.Name.Equals(nameof(Signature)) && !p.Name.Equals(nameof(SignatureBytes)))
|
.Where(p =>
|
||||||
|
!p.Name.Equals(nameof(Signature)) &&
|
||||||
|
!p.Name.Equals(nameof(SignatureBytes)) &&
|
||||||
|
(
|
||||||
|
!forHash ||
|
||||||
|
(
|
||||||
|
!p.Name.Equals(nameof(Hash)) &&
|
||||||
|
!p.Name.Equals(nameof(Issued)) &&
|
||||||
|
!p.Name.Equals(nameof(Refresh))
|
||||||
|
)
|
||||||
|
))
|
||||||
.OrderBy(p => p.Name)
|
.OrderBy(p => p.Name)
|
||||||
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
||||||
.Aggregate((c, n) => $"{c}|{n}");
|
.Aggregate((c, n) => $"{c}|{n}");
|
||||||
@ -68,6 +81,14 @@ namespace Bit.Core.Models.Business
|
|||||||
return Encoding.UTF8.GetBytes(data);
|
return Encoding.UTF8.GetBytes(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] ComputeHash()
|
||||||
|
{
|
||||||
|
using(var alg = SHA256.Create())
|
||||||
|
{
|
||||||
|
return alg.ComputeHash(GetDataBytes(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool CanUse(User user)
|
public bool CanUse(User user)
|
||||||
{
|
{
|
||||||
if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
if(Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||||
@ -109,7 +130,7 @@ namespace Bit.Core.Models.Business
|
|||||||
{
|
{
|
||||||
using(var rsa = certificate.GetRSAPublicKey())
|
using(var rsa = certificate.GetRSAPublicKey())
|
||||||
{
|
{
|
||||||
return rsa.VerifyData(GetSignatureData(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +143,7 @@ namespace Bit.Core.Models.Business
|
|||||||
|
|
||||||
using(var rsa = certificate.GetRSAPrivateKey())
|
using(var rsa = certificate.GetRSAPrivateKey())
|
||||||
{
|
{
|
||||||
return rsa.SignData(GetSignatureData(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ namespace Bit.Core.Services
|
|||||||
if(!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats)
|
if(!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats)
|
||||||
{
|
{
|
||||||
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id);
|
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id);
|
||||||
if(userCount >= newPlanSeats)
|
if(userCount > newPlanSeats)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Your organization currently has {userCount} seats filled. Your new plan " +
|
throw new BadRequestException($"Your organization currently has {userCount} seats filled. Your new plan " +
|
||||||
$"only has ({newPlanSeats}) seats. Remove some users.");
|
$"only has ({newPlanSeats}) seats. Remove some users.");
|
||||||
@ -651,7 +651,7 @@ namespace Bit.Core.Services
|
|||||||
if(license.Seats.HasValue && (!organization.Seats.HasValue || organization.Seats.Value > license.Seats.Value))
|
if(license.Seats.HasValue && (!organization.Seats.HasValue || organization.Seats.Value > license.Seats.Value))
|
||||||
{
|
{
|
||||||
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id);
|
var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id);
|
||||||
if(userCount >= license.Seats.Value)
|
if(userCount > license.Seats.Value)
|
||||||
{
|
{
|
||||||
throw new BadRequestException($"Your organization currently has {userCount} seats filled. " +
|
throw new BadRequestException($"Your organization currently has {userCount} seats filled. " +
|
||||||
$"Your new license only has ({ license.Seats.Value}) seats. Remove some users.");
|
$"Your new license only has ({ license.Seats.Value}) seats. Remove some users.");
|
||||||
|
Loading…
Reference in New Issue
Block a user