mirror of
https://github.com/bitwarden/server.git
synced 2025-07-06 02:22:49 -05:00
Revert filescoped (#2227)
* Revert "Add git blame entry (#2226)" This reverts commit239286737d
. * Revert "Turn on file scoped namespaces (#2225)" This reverts commit34fb4cca2a
.
This commit is contained in:
@ -3,132 +3,133 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Billing.Models;
|
||||
|
||||
public class AppleReceiptStatus
|
||||
namespace Bit.Billing.Models
|
||||
{
|
||||
[JsonPropertyName("status")]
|
||||
public int? Status { get; set; }
|
||||
[JsonPropertyName("environment")]
|
||||
public string Environment { get; set; }
|
||||
[JsonPropertyName("latest_receipt")]
|
||||
public string LatestReceipt { get; set; }
|
||||
[JsonPropertyName("receipt")]
|
||||
public AppleReceipt Receipt { get; set; }
|
||||
[JsonPropertyName("latest_receipt_info")]
|
||||
public List<AppleTransaction> LatestReceiptInfo { get; set; }
|
||||
[JsonPropertyName("pending_renewal_info")]
|
||||
public List<AppleRenewalInfo> PendingRenewalInfo { get; set; }
|
||||
|
||||
public string GetOriginalTransactionId()
|
||||
public class AppleReceiptStatus
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault()?.OriginalTransactionId;
|
||||
}
|
||||
[JsonPropertyName("status")]
|
||||
public int? Status { get; set; }
|
||||
[JsonPropertyName("environment")]
|
||||
public string Environment { get; set; }
|
||||
[JsonPropertyName("latest_receipt")]
|
||||
public string LatestReceipt { get; set; }
|
||||
[JsonPropertyName("receipt")]
|
||||
public AppleReceipt Receipt { get; set; }
|
||||
[JsonPropertyName("latest_receipt_info")]
|
||||
public List<AppleTransaction> LatestReceiptInfo { get; set; }
|
||||
[JsonPropertyName("pending_renewal_info")]
|
||||
public List<AppleRenewalInfo> PendingRenewalInfo { get; set; }
|
||||
|
||||
public string GetLastTransactionId()
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault()?.TransactionId;
|
||||
}
|
||||
|
||||
public AppleTransaction GetLastTransaction()
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault();
|
||||
}
|
||||
|
||||
public DateTime? GetLastExpiresDate()
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault()?.ExpiresDate;
|
||||
}
|
||||
|
||||
public string GetReceiptData()
|
||||
{
|
||||
return LatestReceipt;
|
||||
}
|
||||
|
||||
public DateTime? GetLastCancellationDate()
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault()?.CancellationDate;
|
||||
}
|
||||
|
||||
public bool IsRefunded()
|
||||
{
|
||||
var cancellationDate = GetLastCancellationDate();
|
||||
var expiresDate = GetLastCancellationDate();
|
||||
if (cancellationDate.HasValue && expiresDate.HasValue)
|
||||
public string GetOriginalTransactionId()
|
||||
{
|
||||
return cancellationDate.Value <= expiresDate.Value;
|
||||
return LatestReceiptInfo?.LastOrDefault()?.OriginalTransactionId;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Transaction BuildTransactionFromLastTransaction(decimal amount, Guid userId)
|
||||
{
|
||||
return new Transaction
|
||||
public string GetLastTransactionId()
|
||||
{
|
||||
Amount = amount,
|
||||
CreationDate = GetLastTransaction().PurchaseDate,
|
||||
Gateway = GatewayType.AppStore,
|
||||
GatewayId = GetLastTransactionId(),
|
||||
UserId = userId,
|
||||
PaymentMethodType = PaymentMethodType.AppleInApp,
|
||||
Details = GetLastTransactionId()
|
||||
};
|
||||
}
|
||||
return LatestReceiptInfo?.LastOrDefault()?.TransactionId;
|
||||
}
|
||||
|
||||
public class AppleReceipt
|
||||
{
|
||||
[JsonPropertyName("receipt_type")]
|
||||
public string ReceiptType { get; set; }
|
||||
[JsonPropertyName("bundle_id")]
|
||||
public string BundleId { get; set; }
|
||||
[JsonPropertyName("receipt_creation_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime ReceiptCreationDate { get; set; }
|
||||
[JsonPropertyName("in_app")]
|
||||
public List<AppleTransaction> InApp { get; set; }
|
||||
}
|
||||
public AppleTransaction GetLastTransaction()
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault();
|
||||
}
|
||||
|
||||
public class AppleRenewalInfo
|
||||
{
|
||||
[JsonPropertyName("expiration_intent")]
|
||||
public string ExpirationIntent { get; set; }
|
||||
[JsonPropertyName("auto_renew_product_id")]
|
||||
public string AutoRenewProductId { get; set; }
|
||||
[JsonPropertyName("original_transaction_id")]
|
||||
public string OriginalTransactionId { get; set; }
|
||||
[JsonPropertyName("is_in_billing_retry_period")]
|
||||
public string IsInBillingRetryPeriod { get; set; }
|
||||
[JsonPropertyName("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
[JsonPropertyName("auto_renew_status")]
|
||||
public string AutoRenewStatus { get; set; }
|
||||
}
|
||||
public DateTime? GetLastExpiresDate()
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault()?.ExpiresDate;
|
||||
}
|
||||
|
||||
public class AppleTransaction
|
||||
{
|
||||
[JsonPropertyName("quantity")]
|
||||
public string Quantity { get; set; }
|
||||
[JsonPropertyName("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
[JsonPropertyName("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
[JsonPropertyName("original_transaction_id")]
|
||||
public string OriginalTransactionId { get; set; }
|
||||
[JsonPropertyName("purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime PurchaseDate { get; set; }
|
||||
[JsonPropertyName("original_purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime OriginalPurchaseDate { get; set; }
|
||||
[JsonPropertyName("expires_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime ExpiresDate { get; set; }
|
||||
[JsonPropertyName("cancellation_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime? CancellationDate { get; set; }
|
||||
[JsonPropertyName("web_order_line_item_id")]
|
||||
public string WebOrderLineItemId { get; set; }
|
||||
[JsonPropertyName("cancellation_reason")]
|
||||
public string CancellationReason { get; set; }
|
||||
public string GetReceiptData()
|
||||
{
|
||||
return LatestReceipt;
|
||||
}
|
||||
|
||||
public DateTime? GetLastCancellationDate()
|
||||
{
|
||||
return LatestReceiptInfo?.LastOrDefault()?.CancellationDate;
|
||||
}
|
||||
|
||||
public bool IsRefunded()
|
||||
{
|
||||
var cancellationDate = GetLastCancellationDate();
|
||||
var expiresDate = GetLastCancellationDate();
|
||||
if (cancellationDate.HasValue && expiresDate.HasValue)
|
||||
{
|
||||
return cancellationDate.Value <= expiresDate.Value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Transaction BuildTransactionFromLastTransaction(decimal amount, Guid userId)
|
||||
{
|
||||
return new Transaction
|
||||
{
|
||||
Amount = amount,
|
||||
CreationDate = GetLastTransaction().PurchaseDate,
|
||||
Gateway = GatewayType.AppStore,
|
||||
GatewayId = GetLastTransactionId(),
|
||||
UserId = userId,
|
||||
PaymentMethodType = PaymentMethodType.AppleInApp,
|
||||
Details = GetLastTransactionId()
|
||||
};
|
||||
}
|
||||
|
||||
public class AppleReceipt
|
||||
{
|
||||
[JsonPropertyName("receipt_type")]
|
||||
public string ReceiptType { get; set; }
|
||||
[JsonPropertyName("bundle_id")]
|
||||
public string BundleId { get; set; }
|
||||
[JsonPropertyName("receipt_creation_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime ReceiptCreationDate { get; set; }
|
||||
[JsonPropertyName("in_app")]
|
||||
public List<AppleTransaction> InApp { get; set; }
|
||||
}
|
||||
|
||||
public class AppleRenewalInfo
|
||||
{
|
||||
[JsonPropertyName("expiration_intent")]
|
||||
public string ExpirationIntent { get; set; }
|
||||
[JsonPropertyName("auto_renew_product_id")]
|
||||
public string AutoRenewProductId { get; set; }
|
||||
[JsonPropertyName("original_transaction_id")]
|
||||
public string OriginalTransactionId { get; set; }
|
||||
[JsonPropertyName("is_in_billing_retry_period")]
|
||||
public string IsInBillingRetryPeriod { get; set; }
|
||||
[JsonPropertyName("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
[JsonPropertyName("auto_renew_status")]
|
||||
public string AutoRenewStatus { get; set; }
|
||||
}
|
||||
|
||||
public class AppleTransaction
|
||||
{
|
||||
[JsonPropertyName("quantity")]
|
||||
public string Quantity { get; set; }
|
||||
[JsonPropertyName("product_id")]
|
||||
public string ProductId { get; set; }
|
||||
[JsonPropertyName("transaction_id")]
|
||||
public string TransactionId { get; set; }
|
||||
[JsonPropertyName("original_transaction_id")]
|
||||
public string OriginalTransactionId { get; set; }
|
||||
[JsonPropertyName("purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime PurchaseDate { get; set; }
|
||||
[JsonPropertyName("original_purchase_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime OriginalPurchaseDate { get; set; }
|
||||
[JsonPropertyName("expires_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime ExpiresDate { get; set; }
|
||||
[JsonPropertyName("cancellation_date_ms")]
|
||||
[JsonConverter(typeof(MsEpochConverter))]
|
||||
public DateTime? CancellationDate { get; set; }
|
||||
[JsonPropertyName("web_order_line_item_id")]
|
||||
public string WebOrderLineItemId { get; set; }
|
||||
[JsonPropertyName("cancellation_reason")]
|
||||
public string CancellationReason { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,154 +2,155 @@
|
||||
using Bit.Core.Enums;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class BillingInfo
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public decimal Balance { get; set; }
|
||||
public BillingSource PaymentSource { get; set; }
|
||||
public IEnumerable<BillingInvoice> Invoices { get; set; } = new List<BillingInvoice>();
|
||||
public IEnumerable<BillingTransaction> Transactions { get; set; } = new List<BillingTransaction>();
|
||||
|
||||
public class BillingSource
|
||||
public class BillingInfo
|
||||
{
|
||||
public BillingSource() { }
|
||||
public decimal Balance { get; set; }
|
||||
public BillingSource PaymentSource { get; set; }
|
||||
public IEnumerable<BillingInvoice> Invoices { get; set; } = new List<BillingInvoice>();
|
||||
public IEnumerable<BillingTransaction> Transactions { get; set; } = new List<BillingTransaction>();
|
||||
|
||||
public BillingSource(PaymentMethod method)
|
||||
public class BillingSource
|
||||
{
|
||||
if (method.Card != null)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{method.Card.Brand?.ToUpperInvariant()}, *{method.Card.Last4}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(method.Card.ExpMonth < 10 ?
|
||||
"0" : string.Empty, method.Card.ExpMonth),
|
||||
method.Card.ExpYear);
|
||||
CardBrand = method.Card.Brand;
|
||||
}
|
||||
}
|
||||
public BillingSource() { }
|
||||
|
||||
public BillingSource(IPaymentSource source)
|
||||
{
|
||||
if (source is BankAccount bankAccount)
|
||||
public BillingSource(PaymentMethod method)
|
||||
{
|
||||
Type = PaymentMethodType.BankAccount;
|
||||
Description = $"{bankAccount.BankName}, *{bankAccount.Last4} - " +
|
||||
(bankAccount.Status == "verified" ? "verified" :
|
||||
bankAccount.Status == "errored" ? "invalid" :
|
||||
bankAccount.Status == "verification_failed" ? "verification failed" : "unverified");
|
||||
NeedsVerification = bankAccount.Status == "new" || bankAccount.Status == "validated";
|
||||
if (method.Card != null)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{method.Card.Brand?.ToUpperInvariant()}, *{method.Card.Last4}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(method.Card.ExpMonth < 10 ?
|
||||
"0" : string.Empty, method.Card.ExpMonth),
|
||||
method.Card.ExpYear);
|
||||
CardBrand = method.Card.Brand;
|
||||
}
|
||||
}
|
||||
else if (source is Card card)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{card.Brand}, *{card.Last4}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(card.ExpMonth < 10 ?
|
||||
"0" : string.Empty, card.ExpMonth),
|
||||
card.ExpYear);
|
||||
CardBrand = card.Brand;
|
||||
}
|
||||
else if (source is Source src && src.Card != null)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{src.Card.Brand}, *{src.Card.Last4}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(src.Card.ExpMonth < 10 ?
|
||||
"0" : string.Empty, src.Card.ExpMonth),
|
||||
src.Card.ExpYear);
|
||||
CardBrand = src.Card.Brand;
|
||||
}
|
||||
}
|
||||
|
||||
public BillingSource(Braintree.PaymentMethod method)
|
||||
{
|
||||
if (method is Braintree.PayPalAccount paypal)
|
||||
public BillingSource(IPaymentSource source)
|
||||
{
|
||||
Type = PaymentMethodType.PayPal;
|
||||
Description = paypal.Email;
|
||||
if (source is BankAccount bankAccount)
|
||||
{
|
||||
Type = PaymentMethodType.BankAccount;
|
||||
Description = $"{bankAccount.BankName}, *{bankAccount.Last4} - " +
|
||||
(bankAccount.Status == "verified" ? "verified" :
|
||||
bankAccount.Status == "errored" ? "invalid" :
|
||||
bankAccount.Status == "verification_failed" ? "verification failed" : "unverified");
|
||||
NeedsVerification = bankAccount.Status == "new" || bankAccount.Status == "validated";
|
||||
}
|
||||
else if (source is Card card)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{card.Brand}, *{card.Last4}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(card.ExpMonth < 10 ?
|
||||
"0" : string.Empty, card.ExpMonth),
|
||||
card.ExpYear);
|
||||
CardBrand = card.Brand;
|
||||
}
|
||||
else if (source is Source src && src.Card != null)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{src.Card.Brand}, *{src.Card.Last4}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(src.Card.ExpMonth < 10 ?
|
||||
"0" : string.Empty, src.Card.ExpMonth),
|
||||
src.Card.ExpYear);
|
||||
CardBrand = src.Card.Brand;
|
||||
}
|
||||
}
|
||||
else if (method is Braintree.CreditCard card)
|
||||
|
||||
public BillingSource(Braintree.PaymentMethod method)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{card.CardType.ToString()}, *{card.LastFour}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(card.ExpirationMonth.Length == 1 ?
|
||||
"0" : string.Empty, card.ExpirationMonth),
|
||||
card.ExpirationYear);
|
||||
CardBrand = card.CardType.ToString();
|
||||
if (method is Braintree.PayPalAccount paypal)
|
||||
{
|
||||
Type = PaymentMethodType.PayPal;
|
||||
Description = paypal.Email;
|
||||
}
|
||||
else if (method is Braintree.CreditCard card)
|
||||
{
|
||||
Type = PaymentMethodType.Card;
|
||||
Description = $"{card.CardType.ToString()}, *{card.LastFour}, " +
|
||||
string.Format("{0}/{1}",
|
||||
string.Concat(card.ExpirationMonth.Length == 1 ?
|
||||
"0" : string.Empty, card.ExpirationMonth),
|
||||
card.ExpirationYear);
|
||||
CardBrand = card.CardType.ToString();
|
||||
}
|
||||
else if (method is Braintree.UsBankAccount bank)
|
||||
{
|
||||
Type = PaymentMethodType.BankAccount;
|
||||
Description = $"{bank.BankName}, *{bank.Last4}";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Method not supported.");
|
||||
}
|
||||
}
|
||||
else if (method is Braintree.UsBankAccount bank)
|
||||
|
||||
public BillingSource(Braintree.UsBankAccountDetails bank)
|
||||
{
|
||||
Type = PaymentMethodType.BankAccount;
|
||||
Description = $"{bank.BankName}, *{bank.Last4}";
|
||||
}
|
||||
else
|
||||
|
||||
public BillingSource(Braintree.PayPalDetails paypal)
|
||||
{
|
||||
throw new NotSupportedException("Method not supported.");
|
||||
Type = PaymentMethodType.PayPal;
|
||||
Description = paypal.PayerEmail;
|
||||
}
|
||||
|
||||
public PaymentMethodType Type { get; set; }
|
||||
public string CardBrand { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool NeedsVerification { get; set; }
|
||||
}
|
||||
|
||||
public BillingSource(Braintree.UsBankAccountDetails bank)
|
||||
public class BillingTransaction
|
||||
{
|
||||
Type = PaymentMethodType.BankAccount;
|
||||
Description = $"{bank.BankName}, *{bank.Last4}";
|
||||
public BillingTransaction(Transaction transaction)
|
||||
{
|
||||
Id = transaction.Id;
|
||||
CreatedDate = transaction.CreationDate;
|
||||
Refunded = transaction.Refunded;
|
||||
Type = transaction.Type;
|
||||
PaymentMethodType = transaction.PaymentMethodType;
|
||||
Details = transaction.Details;
|
||||
Amount = transaction.Amount;
|
||||
RefundedAmount = transaction.RefundedAmount;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public bool? Refunded { get; set; }
|
||||
public bool? PartiallyRefunded => !Refunded.GetValueOrDefault() && RefundedAmount.GetValueOrDefault() > 0;
|
||||
public decimal? RefundedAmount { get; set; }
|
||||
public TransactionType Type { get; set; }
|
||||
public PaymentMethodType? PaymentMethodType { get; set; }
|
||||
public string Details { get; set; }
|
||||
}
|
||||
|
||||
public BillingSource(Braintree.PayPalDetails paypal)
|
||||
public class BillingInvoice
|
||||
{
|
||||
Type = PaymentMethodType.PayPal;
|
||||
Description = paypal.PayerEmail;
|
||||
public BillingInvoice(Invoice inv)
|
||||
{
|
||||
Date = inv.Created;
|
||||
Url = inv.HostedInvoiceUrl;
|
||||
PdfUrl = inv.InvoicePdf;
|
||||
Number = inv.Number;
|
||||
Paid = inv.Paid;
|
||||
Amount = inv.Total / 100M;
|
||||
}
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
public DateTime? Date { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string PdfUrl { get; set; }
|
||||
public string Number { get; set; }
|
||||
public bool Paid { get; set; }
|
||||
}
|
||||
|
||||
public PaymentMethodType Type { get; set; }
|
||||
public string CardBrand { get; set; }
|
||||
public string Description { get; set; }
|
||||
public bool NeedsVerification { get; set; }
|
||||
}
|
||||
|
||||
public class BillingTransaction
|
||||
{
|
||||
public BillingTransaction(Transaction transaction)
|
||||
{
|
||||
Id = transaction.Id;
|
||||
CreatedDate = transaction.CreationDate;
|
||||
Refunded = transaction.Refunded;
|
||||
Type = transaction.Type;
|
||||
PaymentMethodType = transaction.PaymentMethodType;
|
||||
Details = transaction.Details;
|
||||
Amount = transaction.Amount;
|
||||
RefundedAmount = transaction.RefundedAmount;
|
||||
}
|
||||
|
||||
public Guid Id { get; set; }
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public bool? Refunded { get; set; }
|
||||
public bool? PartiallyRefunded => !Refunded.GetValueOrDefault() && RefundedAmount.GetValueOrDefault() > 0;
|
||||
public decimal? RefundedAmount { get; set; }
|
||||
public TransactionType Type { get; set; }
|
||||
public PaymentMethodType? PaymentMethodType { get; set; }
|
||||
public string Details { get; set; }
|
||||
}
|
||||
|
||||
public class BillingInvoice
|
||||
{
|
||||
public BillingInvoice(Invoice inv)
|
||||
{
|
||||
Date = inv.Created;
|
||||
Url = inv.HostedInvoiceUrl;
|
||||
PdfUrl = inv.InvoicePdf;
|
||||
Number = inv.Number;
|
||||
Paid = inv.Paid;
|
||||
Amount = inv.Total / 100M;
|
||||
}
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
public DateTime? Date { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string PdfUrl { get; set; }
|
||||
public string Number { get; set; }
|
||||
public bool Paid { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class CaptchaResponse
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public bool MaybeBot { get; set; }
|
||||
public bool IsBot { get; set; }
|
||||
public double Score { get; set; }
|
||||
public class CaptchaResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public bool MaybeBot { get; set; }
|
||||
public bool IsBot { get; set; }
|
||||
public double Score { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class ExpiringToken
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public readonly string Token;
|
||||
public readonly DateTime ExpirationDate;
|
||||
|
||||
public ExpiringToken(string token, DateTime expirationDate)
|
||||
public class ExpiringToken
|
||||
{
|
||||
Token = token;
|
||||
ExpirationDate = expirationDate;
|
||||
public readonly string Token;
|
||||
public readonly DateTime ExpirationDate;
|
||||
|
||||
public ExpiringToken(string token, DateTime expirationDate)
|
||||
{
|
||||
Token = token;
|
||||
ExpirationDate = expirationDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,21 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public interface ILicense
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
string LicenseKey { get; set; }
|
||||
int Version { get; set; }
|
||||
DateTime Issued { get; set; }
|
||||
DateTime? Refresh { get; set; }
|
||||
DateTime? Expires { get; set; }
|
||||
bool Trial { get; set; }
|
||||
string Hash { get; set; }
|
||||
string Signature { get; set; }
|
||||
byte[] SignatureBytes { get; }
|
||||
byte[] GetDataBytes(bool forHash = false);
|
||||
byte[] ComputeHash();
|
||||
bool VerifySignature(X509Certificate2 certificate);
|
||||
byte[] Sign(X509Certificate2 certificate);
|
||||
public interface ILicense
|
||||
{
|
||||
string LicenseKey { get; set; }
|
||||
int Version { get; set; }
|
||||
DateTime Issued { get; set; }
|
||||
DateTime? Refresh { get; set; }
|
||||
DateTime? Expires { get; set; }
|
||||
bool Trial { get; set; }
|
||||
string Hash { get; set; }
|
||||
string Signature { get; set; }
|
||||
byte[] SignatureBytes { get; }
|
||||
byte[] GetDataBytes(bool forHash = false);
|
||||
byte[] ComputeHash();
|
||||
bool VerifySignature(X509Certificate2 certificate);
|
||||
byte[] Sign(X509Certificate2 certificate);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class ImportedGroup
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public Group Group { get; set; }
|
||||
public HashSet<string> ExternalUserIds { get; set; }
|
||||
public class ImportedGroup
|
||||
{
|
||||
public Group Group { get; set; }
|
||||
public HashSet<string> ExternalUserIds { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class ImportedOrganizationUser
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string ExternalId { get; set; }
|
||||
public class ImportedOrganizationUser
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string ExternalId { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,303 +8,304 @@ using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class OrganizationLicense : ILicense
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public OrganizationLicense()
|
||||
{ }
|
||||
|
||||
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
|
||||
ILicensingService licenseService, int? version = null)
|
||||
public class OrganizationLicense : ILicense
|
||||
{
|
||||
Version = version.GetValueOrDefault(CURRENT_LICENSE_FILE_VERSION); // TODO: Remember to change the constant
|
||||
LicenseType = Enums.LicenseType.Organization;
|
||||
LicenseKey = org.LicenseKey;
|
||||
InstallationId = installationId;
|
||||
Id = org.Id;
|
||||
Name = org.Name;
|
||||
BillingEmail = org.BillingEmail;
|
||||
BusinessName = org.BusinessName;
|
||||
Enabled = org.Enabled;
|
||||
Plan = org.Plan;
|
||||
PlanType = org.PlanType;
|
||||
Seats = org.Seats;
|
||||
MaxCollections = org.MaxCollections;
|
||||
UsePolicies = org.UsePolicies;
|
||||
UseSso = org.UseSso;
|
||||
UseKeyConnector = org.UseKeyConnector;
|
||||
UseScim = org.UseScim;
|
||||
UseGroups = org.UseGroups;
|
||||
UseEvents = org.UseEvents;
|
||||
UseDirectory = org.UseDirectory;
|
||||
UseTotp = org.UseTotp;
|
||||
Use2fa = org.Use2fa;
|
||||
UseApi = org.UseApi;
|
||||
UseResetPassword = org.UseResetPassword;
|
||||
MaxStorageGb = org.MaxStorageGb;
|
||||
SelfHost = org.SelfHost;
|
||||
UsersGetPremium = org.UsersGetPremium;
|
||||
Issued = DateTime.UtcNow;
|
||||
public OrganizationLicense()
|
||||
{ }
|
||||
|
||||
if (subscriptionInfo?.Subscription == null)
|
||||
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
|
||||
ILicensingService licenseService, int? version = null)
|
||||
{
|
||||
if (org.PlanType == PlanType.Custom && org.ExpirationDate.HasValue)
|
||||
Version = version.GetValueOrDefault(CURRENT_LICENSE_FILE_VERSION); // TODO: Remember to change the constant
|
||||
LicenseType = Enums.LicenseType.Organization;
|
||||
LicenseKey = org.LicenseKey;
|
||||
InstallationId = installationId;
|
||||
Id = org.Id;
|
||||
Name = org.Name;
|
||||
BillingEmail = org.BillingEmail;
|
||||
BusinessName = org.BusinessName;
|
||||
Enabled = org.Enabled;
|
||||
Plan = org.Plan;
|
||||
PlanType = org.PlanType;
|
||||
Seats = org.Seats;
|
||||
MaxCollections = org.MaxCollections;
|
||||
UsePolicies = org.UsePolicies;
|
||||
UseSso = org.UseSso;
|
||||
UseKeyConnector = org.UseKeyConnector;
|
||||
UseScim = org.UseScim;
|
||||
UseGroups = org.UseGroups;
|
||||
UseEvents = org.UseEvents;
|
||||
UseDirectory = org.UseDirectory;
|
||||
UseTotp = org.UseTotp;
|
||||
Use2fa = org.Use2fa;
|
||||
UseApi = org.UseApi;
|
||||
UseResetPassword = org.UseResetPassword;
|
||||
MaxStorageGb = org.MaxStorageGb;
|
||||
SelfHost = org.SelfHost;
|
||||
UsersGetPremium = org.UsersGetPremium;
|
||||
Issued = DateTime.UtcNow;
|
||||
|
||||
if (subscriptionInfo?.Subscription == null)
|
||||
{
|
||||
Expires = Refresh = org.ExpirationDate.Value;
|
||||
Trial = false;
|
||||
if (org.PlanType == PlanType.Custom && org.ExpirationDate.HasValue)
|
||||
{
|
||||
Expires = Refresh = org.ExpirationDate.Value;
|
||||
Trial = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Expires = Refresh = Issued.AddDays(7);
|
||||
Trial = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (subscriptionInfo.Subscription.TrialEndDate.HasValue &&
|
||||
subscriptionInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow)
|
||||
{
|
||||
Expires = Refresh = Issued.AddDays(7);
|
||||
Expires = Refresh = subscriptionInfo.Subscription.TrialEndDate.Value;
|
||||
Trial = true;
|
||||
}
|
||||
}
|
||||
else if (subscriptionInfo.Subscription.TrialEndDate.HasValue &&
|
||||
subscriptionInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow)
|
||||
{
|
||||
Expires = Refresh = subscriptionInfo.Subscription.TrialEndDate.Value;
|
||||
Trial = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (org.ExpirationDate.HasValue && org.ExpirationDate.Value < DateTime.UtcNow)
|
||||
else
|
||||
{
|
||||
// expired
|
||||
Expires = Refresh = org.ExpirationDate.Value;
|
||||
if (org.ExpirationDate.HasValue && org.ExpirationDate.Value < DateTime.UtcNow)
|
||||
{
|
||||
// expired
|
||||
Expires = Refresh = org.ExpirationDate.Value;
|
||||
}
|
||||
else if (subscriptionInfo?.Subscription?.PeriodDuration != null &&
|
||||
subscriptionInfo.Subscription.PeriodDuration > TimeSpan.FromDays(180))
|
||||
{
|
||||
Refresh = DateTime.UtcNow.AddDays(30);
|
||||
Expires = subscriptionInfo?.Subscription.PeriodEndDate.Value.AddDays(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
Expires = org.ExpirationDate.HasValue ? org.ExpirationDate.Value.AddMonths(11) : Issued.AddYears(1);
|
||||
Refresh = DateTime.UtcNow - Expires > TimeSpan.FromDays(30) ? DateTime.UtcNow.AddDays(30) : Expires;
|
||||
}
|
||||
|
||||
Trial = false;
|
||||
}
|
||||
else if (subscriptionInfo?.Subscription?.PeriodDuration != null &&
|
||||
subscriptionInfo.Subscription.PeriodDuration > TimeSpan.FromDays(180))
|
||||
|
||||
Hash = Convert.ToBase64String(ComputeHash());
|
||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||
}
|
||||
|
||||
public string LicenseKey { get; set; }
|
||||
public Guid InstallationId { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string BillingEmail { get; set; }
|
||||
public string BusinessName { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public string Plan { get; set; }
|
||||
public PlanType PlanType { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
public short? MaxCollections { get; set; }
|
||||
public bool UsePolicies { get; set; }
|
||||
public bool UseSso { get; set; }
|
||||
public bool UseKeyConnector { get; set; }
|
||||
public bool UseScim { get; set; }
|
||||
public bool UseGroups { get; set; }
|
||||
public bool UseEvents { get; set; }
|
||||
public bool UseDirectory { get; set; }
|
||||
public bool UseTotp { get; set; }
|
||||
public bool Use2fa { get; set; }
|
||||
public bool UseApi { get; set; }
|
||||
public bool UseResetPassword { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public bool SelfHost { get; set; }
|
||||
public bool UsersGetPremium { get; set; }
|
||||
public int Version { get; set; }
|
||||
public DateTime Issued { get; set; }
|
||||
public DateTime? Refresh { get; set; }
|
||||
public DateTime? Expires { get; set; }
|
||||
public bool Trial { get; set; }
|
||||
public LicenseType? LicenseType { get; set; }
|
||||
public string Hash { get; set; }
|
||||
public string Signature { get; set; }
|
||||
[JsonIgnore]
|
||||
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the current version of the license format. Should be updated whenever new fields are added.
|
||||
/// </summary>
|
||||
private const int CURRENT_LICENSE_FILE_VERSION = 9;
|
||||
private bool ValidLicenseVersion
|
||||
{
|
||||
get => Version is >= 1 and <= 10;
|
||||
}
|
||||
|
||||
public byte[] GetDataBytes(bool forHash = false)
|
||||
{
|
||||
string data = null;
|
||||
if (ValidLicenseVersion)
|
||||
{
|
||||
Refresh = DateTime.UtcNow.AddDays(30);
|
||||
Expires = subscriptionInfo?.Subscription.PeriodEndDate.Value.AddDays(60);
|
||||
var props = typeof(OrganizationLicense)
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(p =>
|
||||
!p.Name.Equals(nameof(Signature)) &&
|
||||
!p.Name.Equals(nameof(SignatureBytes)) &&
|
||||
!p.Name.Equals(nameof(LicenseType)) &&
|
||||
// UsersGetPremium was added in Version 2
|
||||
(Version >= 2 || !p.Name.Equals(nameof(UsersGetPremium))) &&
|
||||
// UseEvents was added in Version 3
|
||||
(Version >= 3 || !p.Name.Equals(nameof(UseEvents))) &&
|
||||
// Use2fa was added in Version 4
|
||||
(Version >= 4 || !p.Name.Equals(nameof(Use2fa))) &&
|
||||
// UseApi was added in Version 5
|
||||
(Version >= 5 || !p.Name.Equals(nameof(UseApi))) &&
|
||||
// UsePolicies was added in Version 6
|
||||
(Version >= 6 || !p.Name.Equals(nameof(UsePolicies))) &&
|
||||
// UseSso was added in Version 7
|
||||
(Version >= 7 || !p.Name.Equals(nameof(UseSso))) &&
|
||||
// UseResetPassword was added in Version 8
|
||||
(Version >= 8 || !p.Name.Equals(nameof(UseResetPassword))) &&
|
||||
// UseKeyConnector was added in Version 9
|
||||
(Version >= 9 || !p.Name.Equals(nameof(UseKeyConnector))) &&
|
||||
// UseScim was added in Version 10
|
||||
(Version >= 10 || !p.Name.Equals(nameof(UseScim))) &&
|
||||
(
|
||||
!forHash ||
|
||||
(
|
||||
!p.Name.Equals(nameof(Hash)) &&
|
||||
!p.Name.Equals(nameof(Issued)) &&
|
||||
!p.Name.Equals(nameof(Refresh))
|
||||
)
|
||||
))
|
||||
.OrderBy(p => p.Name)
|
||||
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
||||
.Aggregate((c, n) => $"{c}|{n}");
|
||||
data = $"license:organization|{props}";
|
||||
}
|
||||
else
|
||||
{
|
||||
Expires = org.ExpirationDate.HasValue ? org.ExpirationDate.Value.AddMonths(11) : Issued.AddYears(1);
|
||||
Refresh = DateTime.UtcNow - Expires > TimeSpan.FromDays(30) ? DateTime.UtcNow.AddDays(30) : Expires;
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
|
||||
Trial = false;
|
||||
return Encoding.UTF8.GetBytes(data);
|
||||
}
|
||||
|
||||
Hash = Convert.ToBase64String(ComputeHash());
|
||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||
}
|
||||
|
||||
public string LicenseKey { get; set; }
|
||||
public Guid InstallationId { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string BillingEmail { get; set; }
|
||||
public string BusinessName { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
public string Plan { get; set; }
|
||||
public PlanType PlanType { get; set; }
|
||||
public int? Seats { get; set; }
|
||||
public short? MaxCollections { get; set; }
|
||||
public bool UsePolicies { get; set; }
|
||||
public bool UseSso { get; set; }
|
||||
public bool UseKeyConnector { get; set; }
|
||||
public bool UseScim { get; set; }
|
||||
public bool UseGroups { get; set; }
|
||||
public bool UseEvents { get; set; }
|
||||
public bool UseDirectory { get; set; }
|
||||
public bool UseTotp { get; set; }
|
||||
public bool Use2fa { get; set; }
|
||||
public bool UseApi { get; set; }
|
||||
public bool UseResetPassword { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public bool SelfHost { get; set; }
|
||||
public bool UsersGetPremium { get; set; }
|
||||
public int Version { get; set; }
|
||||
public DateTime Issued { get; set; }
|
||||
public DateTime? Refresh { get; set; }
|
||||
public DateTime? Expires { get; set; }
|
||||
public bool Trial { get; set; }
|
||||
public LicenseType? LicenseType { get; set; }
|
||||
public string Hash { get; set; }
|
||||
public string Signature { get; set; }
|
||||
[JsonIgnore]
|
||||
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
||||
|
||||
/// <summary>
|
||||
/// Represents the current version of the license format. Should be updated whenever new fields are added.
|
||||
/// </summary>
|
||||
private const int CURRENT_LICENSE_FILE_VERSION = 9;
|
||||
private bool ValidLicenseVersion
|
||||
{
|
||||
get => Version is >= 1 and <= 10;
|
||||
}
|
||||
|
||||
public byte[] GetDataBytes(bool forHash = false)
|
||||
{
|
||||
string data = null;
|
||||
if (ValidLicenseVersion)
|
||||
public byte[] ComputeHash()
|
||||
{
|
||||
var props = typeof(OrganizationLicense)
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(p =>
|
||||
!p.Name.Equals(nameof(Signature)) &&
|
||||
!p.Name.Equals(nameof(SignatureBytes)) &&
|
||||
!p.Name.Equals(nameof(LicenseType)) &&
|
||||
// UsersGetPremium was added in Version 2
|
||||
(Version >= 2 || !p.Name.Equals(nameof(UsersGetPremium))) &&
|
||||
// UseEvents was added in Version 3
|
||||
(Version >= 3 || !p.Name.Equals(nameof(UseEvents))) &&
|
||||
// Use2fa was added in Version 4
|
||||
(Version >= 4 || !p.Name.Equals(nameof(Use2fa))) &&
|
||||
// UseApi was added in Version 5
|
||||
(Version >= 5 || !p.Name.Equals(nameof(UseApi))) &&
|
||||
// UsePolicies was added in Version 6
|
||||
(Version >= 6 || !p.Name.Equals(nameof(UsePolicies))) &&
|
||||
// UseSso was added in Version 7
|
||||
(Version >= 7 || !p.Name.Equals(nameof(UseSso))) &&
|
||||
// UseResetPassword was added in Version 8
|
||||
(Version >= 8 || !p.Name.Equals(nameof(UseResetPassword))) &&
|
||||
// UseKeyConnector was added in Version 9
|
||||
(Version >= 9 || !p.Name.Equals(nameof(UseKeyConnector))) &&
|
||||
// UseScim was added in Version 10
|
||||
(Version >= 10 || !p.Name.Equals(nameof(UseScim))) &&
|
||||
(
|
||||
!forHash ||
|
||||
(
|
||||
!p.Name.Equals(nameof(Hash)) &&
|
||||
!p.Name.Equals(nameof(Issued)) &&
|
||||
!p.Name.Equals(nameof(Refresh))
|
||||
)
|
||||
))
|
||||
.OrderBy(p => p.Name)
|
||||
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
||||
.Aggregate((c, n) => $"{c}|{n}");
|
||||
data = $"license:organization|{props}";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetBytes(data);
|
||||
}
|
||||
|
||||
public byte[] ComputeHash()
|
||||
{
|
||||
using (var alg = SHA256.Create())
|
||||
{
|
||||
return alg.ComputeHash(GetDataBytes(true));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanUse(IGlobalSettings globalSettings)
|
||||
{
|
||||
if (!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ValidLicenseVersion)
|
||||
{
|
||||
return InstallationId == globalSettings.Installation.Id && SelfHost;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool VerifyData(Organization organization, IGlobalSettings globalSettings)
|
||||
{
|
||||
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ValidLicenseVersion)
|
||||
{
|
||||
var valid =
|
||||
globalSettings.Installation.Id == InstallationId &&
|
||||
organization.LicenseKey != null && organization.LicenseKey.Equals(LicenseKey) &&
|
||||
organization.Enabled == Enabled &&
|
||||
organization.PlanType == PlanType &&
|
||||
organization.Seats == Seats &&
|
||||
organization.MaxCollections == MaxCollections &&
|
||||
organization.UseGroups == UseGroups &&
|
||||
organization.UseDirectory == UseDirectory &&
|
||||
organization.UseTotp == UseTotp &&
|
||||
organization.SelfHost == SelfHost &&
|
||||
organization.Name.Equals(Name);
|
||||
|
||||
if (valid && Version >= 2)
|
||||
using (var alg = SHA256.Create())
|
||||
{
|
||||
valid = organization.UsersGetPremium == UsersGetPremium;
|
||||
return alg.ComputeHash(GetDataBytes(true));
|
||||
}
|
||||
|
||||
if (valid && Version >= 3)
|
||||
{
|
||||
valid = organization.UseEvents == UseEvents;
|
||||
}
|
||||
|
||||
if (valid && Version >= 4)
|
||||
{
|
||||
valid = organization.Use2fa == Use2fa;
|
||||
}
|
||||
|
||||
if (valid && Version >= 5)
|
||||
{
|
||||
valid = organization.UseApi == UseApi;
|
||||
}
|
||||
|
||||
if (valid && Version >= 6)
|
||||
{
|
||||
valid = organization.UsePolicies == UsePolicies;
|
||||
}
|
||||
|
||||
if (valid && Version >= 7)
|
||||
{
|
||||
valid = organization.UseSso == UseSso;
|
||||
}
|
||||
|
||||
if (valid && Version >= 8)
|
||||
{
|
||||
valid = organization.UseResetPassword == UseResetPassword;
|
||||
}
|
||||
|
||||
if (valid && Version >= 9)
|
||||
{
|
||||
valid = organization.UseKeyConnector == UseKeyConnector;
|
||||
}
|
||||
|
||||
if (valid && Version >= 10)
|
||||
{
|
||||
valid = organization.UseScim == UseScim;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool VerifySignature(X509Certificate2 certificate)
|
||||
{
|
||||
using (var rsa = certificate.GetRSAPublicKey())
|
||||
{
|
||||
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Sign(X509Certificate2 certificate)
|
||||
{
|
||||
if (!certificate.HasPrivateKey)
|
||||
{
|
||||
throw new InvalidOperationException("You don't have the private key!");
|
||||
}
|
||||
|
||||
using (var rsa = certificate.GetRSAPrivateKey())
|
||||
public bool CanUse(IGlobalSettings globalSettings)
|
||||
{
|
||||
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
if (!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ValidLicenseVersion)
|
||||
{
|
||||
return InstallationId == globalSettings.Installation.Id && SelfHost;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool VerifyData(Organization organization, IGlobalSettings globalSettings)
|
||||
{
|
||||
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ValidLicenseVersion)
|
||||
{
|
||||
var valid =
|
||||
globalSettings.Installation.Id == InstallationId &&
|
||||
organization.LicenseKey != null && organization.LicenseKey.Equals(LicenseKey) &&
|
||||
organization.Enabled == Enabled &&
|
||||
organization.PlanType == PlanType &&
|
||||
organization.Seats == Seats &&
|
||||
organization.MaxCollections == MaxCollections &&
|
||||
organization.UseGroups == UseGroups &&
|
||||
organization.UseDirectory == UseDirectory &&
|
||||
organization.UseTotp == UseTotp &&
|
||||
organization.SelfHost == SelfHost &&
|
||||
organization.Name.Equals(Name);
|
||||
|
||||
if (valid && Version >= 2)
|
||||
{
|
||||
valid = organization.UsersGetPremium == UsersGetPremium;
|
||||
}
|
||||
|
||||
if (valid && Version >= 3)
|
||||
{
|
||||
valid = organization.UseEvents == UseEvents;
|
||||
}
|
||||
|
||||
if (valid && Version >= 4)
|
||||
{
|
||||
valid = organization.Use2fa == Use2fa;
|
||||
}
|
||||
|
||||
if (valid && Version >= 5)
|
||||
{
|
||||
valid = organization.UseApi == UseApi;
|
||||
}
|
||||
|
||||
if (valid && Version >= 6)
|
||||
{
|
||||
valid = organization.UsePolicies == UsePolicies;
|
||||
}
|
||||
|
||||
if (valid && Version >= 7)
|
||||
{
|
||||
valid = organization.UseSso == UseSso;
|
||||
}
|
||||
|
||||
if (valid && Version >= 8)
|
||||
{
|
||||
valid = organization.UseResetPassword == UseResetPassword;
|
||||
}
|
||||
|
||||
if (valid && Version >= 9)
|
||||
{
|
||||
valid = organization.UseKeyConnector == UseKeyConnector;
|
||||
}
|
||||
|
||||
if (valid && Version >= 10)
|
||||
{
|
||||
valid = organization.UseScim == UseScim;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public bool VerifySignature(X509Certificate2 certificate)
|
||||
{
|
||||
using (var rsa = certificate.GetRSAPublicKey())
|
||||
{
|
||||
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Sign(X509Certificate2 certificate)
|
||||
{
|
||||
if (!certificate.HasPrivateKey)
|
||||
{
|
||||
throw new InvalidOperationException("You don't have the private key!");
|
||||
}
|
||||
|
||||
using (var rsa = certificate.GetRSAPrivateKey())
|
||||
{
|
||||
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class OrganizationSignup : OrganizationUpgrade
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string BillingEmail { get; set; }
|
||||
public User Owner { get; set; }
|
||||
public string OwnerKey { get; set; }
|
||||
public string CollectionName { get; set; }
|
||||
public PaymentMethodType? PaymentMethodType { get; set; }
|
||||
public string PaymentToken { get; set; }
|
||||
public int? MaxAutoscaleSeats { get; set; } = null;
|
||||
public class OrganizationSignup : OrganizationUpgrade
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string BillingEmail { get; set; }
|
||||
public User Owner { get; set; }
|
||||
public string OwnerKey { get; set; }
|
||||
public string CollectionName { get; set; }
|
||||
public PaymentMethodType? PaymentMethodType { get; set; }
|
||||
public string PaymentToken { get; set; }
|
||||
public int? MaxAutoscaleSeats { get; set; } = null;
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,16 @@
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class OrganizationUpgrade
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public string BusinessName { get; set; }
|
||||
public PlanType Plan { get; set; }
|
||||
public int AdditionalSeats { get; set; }
|
||||
public short AdditionalStorageGb { get; set; }
|
||||
public bool PremiumAccessAddon { get; set; }
|
||||
public TaxInfo TaxInfo { get; set; }
|
||||
public string PublicKey { get; set; }
|
||||
public string PrivateKey { get; set; }
|
||||
public class OrganizationUpgrade
|
||||
{
|
||||
public string BusinessName { get; set; }
|
||||
public PlanType Plan { get; set; }
|
||||
public int AdditionalSeats { get; set; }
|
||||
public short AdditionalStorageGb { get; set; }
|
||||
public bool PremiumAccessAddon { get; set; }
|
||||
public TaxInfo TaxInfo { get; set; }
|
||||
public string PublicKey { get; set; }
|
||||
public string PrivateKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,25 @@
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class OrganizationUserInvite
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public IEnumerable<string> Emails { get; set; }
|
||||
public Enums.OrganizationUserType? Type { get; set; }
|
||||
public bool AccessAll { get; set; }
|
||||
public Permissions Permissions { get; set; }
|
||||
public IEnumerable<SelectionReadOnly> Collections { get; set; }
|
||||
|
||||
public OrganizationUserInvite() { }
|
||||
|
||||
public OrganizationUserInvite(OrganizationUserInviteData requestModel)
|
||||
public class OrganizationUserInvite
|
||||
{
|
||||
Emails = requestModel.Emails;
|
||||
Type = requestModel.Type;
|
||||
AccessAll = requestModel.AccessAll;
|
||||
Collections = requestModel.Collections;
|
||||
Permissions = requestModel.Permissions;
|
||||
public IEnumerable<string> Emails { get; set; }
|
||||
public Enums.OrganizationUserType? Type { get; set; }
|
||||
public bool AccessAll { get; set; }
|
||||
public Permissions Permissions { get; set; }
|
||||
public IEnumerable<SelectionReadOnly> Collections { get; set; }
|
||||
|
||||
public OrganizationUserInvite() { }
|
||||
|
||||
public OrganizationUserInvite(OrganizationUserInviteData requestModel)
|
||||
{
|
||||
Emails = requestModel.Emails;
|
||||
Type = requestModel.Type;
|
||||
AccessAll = requestModel.AccessAll;
|
||||
Collections = requestModel.Collections;
|
||||
Permissions = requestModel.Permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,36 @@
|
||||
using Bit.Core.Enums.Provider;
|
||||
|
||||
namespace Bit.Core.Models.Business.Provider;
|
||||
|
||||
public class ProviderUserInvite<T>
|
||||
namespace Bit.Core.Models.Business.Provider
|
||||
{
|
||||
public IEnumerable<T> UserIdentifiers { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public Guid InvitingUserId { get; set; }
|
||||
public Guid ProviderId { get; set; }
|
||||
}
|
||||
|
||||
public static class ProviderUserInviteFactory
|
||||
{
|
||||
public static ProviderUserInvite<string> CreateIntialInvite(IEnumerable<string> inviteeEmails, ProviderUserType type, Guid invitingUserId, Guid providerId)
|
||||
public class ProviderUserInvite<T>
|
||||
{
|
||||
return new ProviderUserInvite<string>
|
||||
{
|
||||
UserIdentifiers = inviteeEmails,
|
||||
Type = type,
|
||||
InvitingUserId = invitingUserId,
|
||||
ProviderId = providerId
|
||||
};
|
||||
public IEnumerable<T> UserIdentifiers { get; set; }
|
||||
public ProviderUserType Type { get; set; }
|
||||
public Guid InvitingUserId { get; set; }
|
||||
public Guid ProviderId { get; set; }
|
||||
}
|
||||
|
||||
public static ProviderUserInvite<Guid> CreateReinvite(IEnumerable<Guid> inviteeUserIds, Guid invitingUserId, Guid providerId)
|
||||
public static class ProviderUserInviteFactory
|
||||
{
|
||||
return new ProviderUserInvite<Guid>
|
||||
public static ProviderUserInvite<string> CreateIntialInvite(IEnumerable<string> inviteeEmails, ProviderUserType type, Guid invitingUserId, Guid providerId)
|
||||
{
|
||||
UserIdentifiers = inviteeUserIds,
|
||||
InvitingUserId = invitingUserId,
|
||||
ProviderId = providerId
|
||||
};
|
||||
return new ProviderUserInvite<string>
|
||||
{
|
||||
UserIdentifiers = inviteeEmails,
|
||||
Type = type,
|
||||
InvitingUserId = invitingUserId,
|
||||
ProviderId = providerId
|
||||
};
|
||||
}
|
||||
|
||||
public static ProviderUserInvite<Guid> CreateReinvite(IEnumerable<Guid> inviteeUserIds, Guid invitingUserId, Guid providerId)
|
||||
{
|
||||
return new ProviderUserInvite<Guid>
|
||||
{
|
||||
UserIdentifiers = inviteeUserIds,
|
||||
InvitingUserId = invitingUserId,
|
||||
ProviderId = providerId
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,60 +2,61 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class ReferenceEvent
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public ReferenceEvent() { }
|
||||
|
||||
public ReferenceEvent(ReferenceEventType type, IReferenceable source)
|
||||
public class ReferenceEvent
|
||||
{
|
||||
Type = type;
|
||||
if (source != null)
|
||||
public ReferenceEvent() { }
|
||||
|
||||
public ReferenceEvent(ReferenceEventType type, IReferenceable source)
|
||||
{
|
||||
Source = source.IsUser() ? ReferenceEventSource.User : ReferenceEventSource.Organization;
|
||||
Id = source.Id;
|
||||
ReferenceData = source.ReferenceData;
|
||||
Type = type;
|
||||
if (source != null)
|
||||
{
|
||||
Source = source.IsUser() ? ReferenceEventSource.User : ReferenceEventSource.Organization;
|
||||
Id = source.Id;
|
||||
ReferenceData = source.ReferenceData;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ReferenceEventType Type { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ReferenceEventSource Source { get; set; }
|
||||
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string ReferenceData { get; set; }
|
||||
|
||||
public DateTime EventDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public int? Users { get; set; }
|
||||
|
||||
public bool? EndOfPeriod { get; set; }
|
||||
|
||||
public string PlanName { get; set; }
|
||||
|
||||
public PlanType? PlanType { get; set; }
|
||||
|
||||
public string OldPlanName { get; set; }
|
||||
|
||||
public PlanType? OldPlanType { get; set; }
|
||||
|
||||
public int? Seats { get; set; }
|
||||
public int? PreviousSeats { get; set; }
|
||||
|
||||
public short? Storage { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SendType? SendType { get; set; }
|
||||
|
||||
public int? MaxAccessCount { get; set; }
|
||||
|
||||
public bool? HasPassword { get; set; }
|
||||
|
||||
public string EventRaisedByUser { get; set; }
|
||||
|
||||
public bool? SalesAssistedTrialStarted { get; set; }
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ReferenceEventType Type { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public ReferenceEventSource Source { get; set; }
|
||||
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string ReferenceData { get; set; }
|
||||
|
||||
public DateTime EventDate { get; set; } = DateTime.UtcNow;
|
||||
|
||||
public int? Users { get; set; }
|
||||
|
||||
public bool? EndOfPeriod { get; set; }
|
||||
|
||||
public string PlanName { get; set; }
|
||||
|
||||
public PlanType? PlanType { get; set; }
|
||||
|
||||
public string OldPlanName { get; set; }
|
||||
|
||||
public PlanType? OldPlanType { get; set; }
|
||||
|
||||
public int? Seats { get; set; }
|
||||
public int? PreviousSeats { get; set; }
|
||||
|
||||
public short? Storage { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SendType? SendType { get; set; }
|
||||
|
||||
public int? MaxAccessCount { get; set; }
|
||||
|
||||
public bool? HasPassword { get; set; }
|
||||
|
||||
public string EventRaisedByUser { get; set; }
|
||||
|
||||
public bool? SalesAssistedTrialStarted { get; set; }
|
||||
}
|
||||
|
@ -1,83 +1,84 @@
|
||||
using Bit.Core.Entities;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats, int additionalStorageGb, bool premiumAccessAddon)
|
||||
public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions
|
||||
{
|
||||
Items = new List<SubscriptionItemOptions>();
|
||||
Metadata = new Dictionary<string, string>
|
||||
public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats, int additionalStorageGb, bool premiumAccessAddon)
|
||||
{
|
||||
[org.GatewayIdField()] = org.Id.ToString()
|
||||
};
|
||||
|
||||
if (plan.StripePlanId != null)
|
||||
{
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
Items = new List<SubscriptionItemOptions>();
|
||||
Metadata = new Dictionary<string, string>
|
||||
{
|
||||
Plan = plan.StripePlanId,
|
||||
Quantity = 1
|
||||
});
|
||||
}
|
||||
[org.GatewayIdField()] = org.Id.ToString()
|
||||
};
|
||||
|
||||
if (additionalSeats > 0 && plan.StripeSeatPlanId != null)
|
||||
{
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
if (plan.StripePlanId != null)
|
||||
{
|
||||
Plan = plan.StripeSeatPlanId,
|
||||
Quantity = additionalSeats
|
||||
});
|
||||
}
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Plan = plan.StripePlanId,
|
||||
Quantity = 1
|
||||
});
|
||||
}
|
||||
|
||||
if (additionalStorageGb > 0)
|
||||
{
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
if (additionalSeats > 0 && plan.StripeSeatPlanId != null)
|
||||
{
|
||||
Plan = plan.StripeStoragePlanId,
|
||||
Quantity = additionalStorageGb
|
||||
});
|
||||
}
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Plan = plan.StripeSeatPlanId,
|
||||
Quantity = additionalSeats
|
||||
});
|
||||
}
|
||||
|
||||
if (premiumAccessAddon && plan.StripePremiumAccessPlanId != null)
|
||||
{
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
if (additionalStorageGb > 0)
|
||||
{
|
||||
Plan = plan.StripePremiumAccessPlanId,
|
||||
Quantity = 1
|
||||
});
|
||||
}
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Plan = plan.StripeStoragePlanId,
|
||||
Quantity = additionalStorageGb
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(taxInfo?.StripeTaxRateId))
|
||||
if (premiumAccessAddon && plan.StripePremiumAccessPlanId != null)
|
||||
{
|
||||
Items.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Plan = plan.StripePremiumAccessPlanId,
|
||||
Quantity = 1
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(taxInfo?.StripeTaxRateId))
|
||||
{
|
||||
DefaultTaxRates = new List<string> { taxInfo.StripeTaxRateId };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OrganizationPurchaseSubscriptionOptions : OrganizationSubscriptionOptionsBase
|
||||
{
|
||||
public OrganizationPurchaseSubscriptionOptions(
|
||||
Organization org, StaticStore.Plan plan,
|
||||
TaxInfo taxInfo, int additionalSeats = 0,
|
||||
int additionalStorageGb = 0, bool premiumAccessAddon = false) :
|
||||
base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon)
|
||||
{
|
||||
DefaultTaxRates = new List<string> { taxInfo.StripeTaxRateId };
|
||||
OffSession = true;
|
||||
TrialPeriodDays = plan.TrialPeriodDays;
|
||||
}
|
||||
}
|
||||
|
||||
public class OrganizationUpgradeSubscriptionOptions : OrganizationSubscriptionOptionsBase
|
||||
{
|
||||
public OrganizationUpgradeSubscriptionOptions(
|
||||
string customerId, Organization org,
|
||||
StaticStore.Plan plan, TaxInfo taxInfo,
|
||||
int additionalSeats = 0, int additionalStorageGb = 0,
|
||||
bool premiumAccessAddon = false) :
|
||||
base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon)
|
||||
{
|
||||
Customer = customerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OrganizationPurchaseSubscriptionOptions : OrganizationSubscriptionOptionsBase
|
||||
{
|
||||
public OrganizationPurchaseSubscriptionOptions(
|
||||
Organization org, StaticStore.Plan plan,
|
||||
TaxInfo taxInfo, int additionalSeats = 0,
|
||||
int additionalStorageGb = 0, bool premiumAccessAddon = false) :
|
||||
base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon)
|
||||
{
|
||||
OffSession = true;
|
||||
TrialPeriodDays = plan.TrialPeriodDays;
|
||||
}
|
||||
}
|
||||
|
||||
public class OrganizationUpgradeSubscriptionOptions : OrganizationSubscriptionOptionsBase
|
||||
{
|
||||
public OrganizationUpgradeSubscriptionOptions(
|
||||
string customerId, Organization org,
|
||||
StaticStore.Plan plan, TaxInfo taxInfo,
|
||||
int additionalSeats = 0, int additionalStorageGb = 0,
|
||||
bool premiumAccessAddon = false) :
|
||||
base(org, plan, taxInfo, additionalSeats, additionalStorageGb, premiumAccessAddon)
|
||||
{
|
||||
Customer = customerId;
|
||||
}
|
||||
}
|
||||
|
@ -1,86 +1,87 @@
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class SubscriptionInfo
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public BillingSubscription Subscription { get; set; }
|
||||
public BillingUpcomingInvoice UpcomingInvoice { get; set; }
|
||||
public bool UsingInAppPurchase { get; set; }
|
||||
|
||||
public class BillingSubscription
|
||||
public class SubscriptionInfo
|
||||
{
|
||||
public BillingSubscription(Subscription sub)
|
||||
{
|
||||
Status = sub.Status;
|
||||
TrialStartDate = sub.TrialStart;
|
||||
TrialEndDate = sub.TrialEnd;
|
||||
PeriodStartDate = sub.CurrentPeriodStart;
|
||||
PeriodEndDate = sub.CurrentPeriodEnd;
|
||||
CancelledDate = sub.CanceledAt;
|
||||
CancelAtEndDate = sub.CancelAtPeriodEnd;
|
||||
Cancelled = sub.Status == "canceled" || sub.Status == "unpaid" || sub.Status == "incomplete_expired";
|
||||
if (sub.Items?.Data != null)
|
||||
{
|
||||
Items = sub.Items.Data.Select(i => new BillingSubscriptionItem(i));
|
||||
}
|
||||
}
|
||||
public BillingSubscription Subscription { get; set; }
|
||||
public BillingUpcomingInvoice UpcomingInvoice { get; set; }
|
||||
public bool UsingInAppPurchase { get; set; }
|
||||
|
||||
public DateTime? TrialStartDate { get; set; }
|
||||
public DateTime? TrialEndDate { get; set; }
|
||||
public DateTime? PeriodStartDate { get; set; }
|
||||
public DateTime? PeriodEndDate { get; set; }
|
||||
public TimeSpan? PeriodDuration => PeriodEndDate - PeriodStartDate;
|
||||
public DateTime? CancelledDate { get; set; }
|
||||
public bool CancelAtEndDate { get; set; }
|
||||
public string Status { get; set; }
|
||||
public bool Cancelled { get; set; }
|
||||
public IEnumerable<BillingSubscriptionItem> Items { get; set; } = new List<BillingSubscriptionItem>();
|
||||
|
||||
public class BillingSubscriptionItem
|
||||
public class BillingSubscription
|
||||
{
|
||||
public BillingSubscriptionItem(SubscriptionItem item)
|
||||
public BillingSubscription(Subscription sub)
|
||||
{
|
||||
if (item.Plan != null)
|
||||
Status = sub.Status;
|
||||
TrialStartDate = sub.TrialStart;
|
||||
TrialEndDate = sub.TrialEnd;
|
||||
PeriodStartDate = sub.CurrentPeriodStart;
|
||||
PeriodEndDate = sub.CurrentPeriodEnd;
|
||||
CancelledDate = sub.CanceledAt;
|
||||
CancelAtEndDate = sub.CancelAtPeriodEnd;
|
||||
Cancelled = sub.Status == "canceled" || sub.Status == "unpaid" || sub.Status == "incomplete_expired";
|
||||
if (sub.Items?.Data != null)
|
||||
{
|
||||
Name = item.Plan.Nickname;
|
||||
Amount = item.Plan.Amount.GetValueOrDefault() / 100M;
|
||||
Interval = item.Plan.Interval;
|
||||
Items = sub.Items.Data.Select(i => new BillingSubscriptionItem(i));
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime? TrialStartDate { get; set; }
|
||||
public DateTime? TrialEndDate { get; set; }
|
||||
public DateTime? PeriodStartDate { get; set; }
|
||||
public DateTime? PeriodEndDate { get; set; }
|
||||
public TimeSpan? PeriodDuration => PeriodEndDate - PeriodStartDate;
|
||||
public DateTime? CancelledDate { get; set; }
|
||||
public bool CancelAtEndDate { get; set; }
|
||||
public string Status { get; set; }
|
||||
public bool Cancelled { get; set; }
|
||||
public IEnumerable<BillingSubscriptionItem> Items { get; set; } = new List<BillingSubscriptionItem>();
|
||||
|
||||
public class BillingSubscriptionItem
|
||||
{
|
||||
public BillingSubscriptionItem(SubscriptionItem item)
|
||||
{
|
||||
if (item.Plan != null)
|
||||
{
|
||||
Name = item.Plan.Nickname;
|
||||
Amount = item.Plan.Amount.GetValueOrDefault() / 100M;
|
||||
Interval = item.Plan.Interval;
|
||||
}
|
||||
|
||||
Quantity = (int)item.Quantity;
|
||||
SponsoredSubscriptionItem = Utilities.StaticStore.SponsoredPlans.Any(p => p.StripePlanId == item.Plan.Id);
|
||||
}
|
||||
|
||||
Quantity = (int)item.Quantity;
|
||||
SponsoredSubscriptionItem = Utilities.StaticStore.SponsoredPlans.Any(p => p.StripePlanId == item.Plan.Id);
|
||||
public string Name { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public string Interval { get; set; }
|
||||
public bool SponsoredSubscriptionItem { get; set; }
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public string Interval { get; set; }
|
||||
public bool SponsoredSubscriptionItem { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public class BillingUpcomingInvoice
|
||||
{
|
||||
public BillingUpcomingInvoice() { }
|
||||
|
||||
public BillingUpcomingInvoice(Invoice inv)
|
||||
{
|
||||
Amount = inv.AmountDue / 100M;
|
||||
Date = inv.Created;
|
||||
}
|
||||
|
||||
public BillingUpcomingInvoice(Braintree.Subscription sub)
|
||||
public class BillingUpcomingInvoice
|
||||
{
|
||||
Amount = sub.NextBillAmount.GetValueOrDefault() + sub.Balance.GetValueOrDefault();
|
||||
if (Amount < 0)
|
||||
public BillingUpcomingInvoice() { }
|
||||
|
||||
public BillingUpcomingInvoice(Invoice inv)
|
||||
{
|
||||
Amount = 0;
|
||||
Amount = inv.AmountDue / 100M;
|
||||
Date = inv.Created;
|
||||
}
|
||||
Date = sub.NextBillingDate;
|
||||
}
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
public DateTime? Date { get; set; }
|
||||
public BillingUpcomingInvoice(Braintree.Subscription sub)
|
||||
{
|
||||
Amount = sub.NextBillAmount.GetValueOrDefault() + sub.Balance.GetValueOrDefault();
|
||||
if (Amount < 0)
|
||||
{
|
||||
Amount = 0;
|
||||
}
|
||||
Date = sub.NextBillingDate;
|
||||
}
|
||||
|
||||
public decimal Amount { get; set; }
|
||||
public DateTime? Date { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,209 +1,210 @@
|
||||
using Bit.Core.Entities;
|
||||
using Stripe;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public abstract class SubscriptionUpdate
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
protected abstract List<string> PlanIds { get; }
|
||||
|
||||
public abstract List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription);
|
||||
public abstract List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription);
|
||||
|
||||
public bool UpdateNeeded(Subscription subscription)
|
||||
public abstract class SubscriptionUpdate
|
||||
{
|
||||
var upgradeItemsOptions = UpgradeItemsOptions(subscription);
|
||||
foreach (var upgradeItemOptions in upgradeItemsOptions)
|
||||
protected abstract List<string> PlanIds { get; }
|
||||
|
||||
public abstract List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription);
|
||||
public abstract List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription);
|
||||
|
||||
public bool UpdateNeeded(Subscription subscription)
|
||||
{
|
||||
var upgradeQuantity = upgradeItemOptions.Quantity ?? 0;
|
||||
var existingQuantity = SubscriptionItem(subscription, upgradeItemOptions.Plan)?.Quantity ?? 0;
|
||||
if (upgradeQuantity != existingQuantity)
|
||||
var upgradeItemsOptions = UpgradeItemsOptions(subscription);
|
||||
foreach (var upgradeItemOptions in upgradeItemsOptions)
|
||||
{
|
||||
return true;
|
||||
var upgradeQuantity = upgradeItemOptions.Quantity ?? 0;
|
||||
var existingQuantity = SubscriptionItem(subscription, upgradeItemOptions.Plan)?.Quantity ?? 0;
|
||||
if (upgradeQuantity != existingQuantity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
protected static SubscriptionItem SubscriptionItem(Subscription subscription, string planId) =>
|
||||
planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId);
|
||||
}
|
||||
|
||||
protected static SubscriptionItem SubscriptionItem(Subscription subscription, string planId) =>
|
||||
planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId);
|
||||
}
|
||||
|
||||
|
||||
public class SeatSubscriptionUpdate : SubscriptionUpdate
|
||||
{
|
||||
private readonly int _previousSeats;
|
||||
private readonly StaticStore.Plan _plan;
|
||||
private readonly long? _additionalSeats;
|
||||
protected override List<string> PlanIds => new() { _plan.StripeSeatPlanId };
|
||||
|
||||
|
||||
public SeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats)
|
||||
public class SeatSubscriptionUpdate : SubscriptionUpdate
|
||||
{
|
||||
_plan = plan;
|
||||
_additionalSeats = additionalSeats;
|
||||
_previousSeats = organization.Seats ?? 0;
|
||||
}
|
||||
private readonly int _previousSeats;
|
||||
private readonly StaticStore.Plan _plan;
|
||||
private readonly long? _additionalSeats;
|
||||
protected override List<string> PlanIds => new() { _plan.StripeSeatPlanId };
|
||||
|
||||
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
||||
{
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
return new()
|
||||
|
||||
public SeatSubscriptionUpdate(Organization organization, StaticStore.Plan plan, long? additionalSeats)
|
||||
{
|
||||
new SubscriptionItemOptions
|
||||
_plan = plan;
|
||||
_additionalSeats = additionalSeats;
|
||||
_previousSeats = organization.Seats ?? 0;
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
||||
{
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
return new()
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = PlanIds.Single(),
|
||||
Quantity = _additionalSeats,
|
||||
Deleted = (item?.Id != null && _additionalSeats == 0) ? true : (bool?)null,
|
||||
}
|
||||
};
|
||||
new SubscriptionItemOptions
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = PlanIds.Single(),
|
||||
Quantity = _additionalSeats,
|
||||
Deleted = (item?.Id != null && _additionalSeats == 0) ? true : (bool?)null,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
|
||||
{
|
||||
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
return new()
|
||||
{
|
||||
new SubscriptionItemOptions
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = PlanIds.Single(),
|
||||
Quantity = _previousSeats,
|
||||
Deleted = _previousSeats == 0 ? true : (bool?)null,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
|
||||
public class StorageSubscriptionUpdate : SubscriptionUpdate
|
||||
{
|
||||
private long? _prevStorage;
|
||||
private readonly string _plan;
|
||||
private readonly long? _additionalStorage;
|
||||
protected override List<string> PlanIds => new() { _plan };
|
||||
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
return new()
|
||||
public StorageSubscriptionUpdate(string plan, long? additionalStorage)
|
||||
{
|
||||
new SubscriptionItemOptions
|
||||
_plan = plan;
|
||||
_additionalStorage = additionalStorage;
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
||||
{
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
_prevStorage = item?.Quantity ?? 0;
|
||||
return new()
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = PlanIds.Single(),
|
||||
Quantity = _previousSeats,
|
||||
Deleted = _previousSeats == 0 ? true : (bool?)null,
|
||||
new SubscriptionItemOptions
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = _plan,
|
||||
Quantity = _additionalStorage,
|
||||
Deleted = (item?.Id != null && _additionalStorage == 0) ? true : (bool?)null,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
|
||||
{
|
||||
if (!_prevStorage.HasValue)
|
||||
{
|
||||
throw new Exception("Unknown previous value, must first call UpgradeItemsOptions");
|
||||
}
|
||||
};
|
||||
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
return new()
|
||||
{
|
||||
new SubscriptionItemOptions
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = _plan,
|
||||
Quantity = _prevStorage.Value,
|
||||
Deleted = _prevStorage.Value == 0 ? true : (bool?)null,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class SponsorOrganizationSubscriptionUpdate : SubscriptionUpdate
|
||||
{
|
||||
private readonly string _existingPlanStripeId;
|
||||
private readonly string _sponsoredPlanStripeId;
|
||||
private readonly bool _applySponsorship;
|
||||
protected override List<string> PlanIds => new() { _existingPlanStripeId, _sponsoredPlanStripeId };
|
||||
|
||||
public SponsorOrganizationSubscriptionUpdate(StaticStore.Plan existingPlan, StaticStore.SponsoredPlan sponsoredPlan, bool applySponsorship)
|
||||
{
|
||||
_existingPlanStripeId = existingPlan.StripePlanId;
|
||||
_sponsoredPlanStripeId = sponsoredPlan?.StripePlanId;
|
||||
_applySponsorship = applySponsorship;
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
|
||||
{
|
||||
var result = new List<SubscriptionItemOptions>();
|
||||
if (!string.IsNullOrWhiteSpace(AddStripePlanId))
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = AddStripeItem(subscription)?.Id,
|
||||
Plan = AddStripePlanId,
|
||||
Quantity = 0,
|
||||
Deleted = true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(RemoveStripePlanId))
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = RemoveStripeItem(subscription)?.Id,
|
||||
Plan = RemoveStripePlanId,
|
||||
Quantity = 1,
|
||||
Deleted = false,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
||||
{
|
||||
var result = new List<SubscriptionItemOptions>();
|
||||
if (RemoveStripeItem(subscription) != null)
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = RemoveStripeItem(subscription)?.Id,
|
||||
Plan = RemoveStripePlanId,
|
||||
Quantity = 0,
|
||||
Deleted = true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(AddStripePlanId))
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = AddStripeItem(subscription)?.Id,
|
||||
Plan = AddStripePlanId,
|
||||
Quantity = 1,
|
||||
Deleted = false,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private string RemoveStripePlanId => _applySponsorship ? _existingPlanStripeId : _sponsoredPlanStripeId;
|
||||
private string AddStripePlanId => _applySponsorship ? _sponsoredPlanStripeId : _existingPlanStripeId;
|
||||
private Stripe.SubscriptionItem RemoveStripeItem(Subscription subscription) =>
|
||||
_applySponsorship ?
|
||||
SubscriptionItem(subscription, _existingPlanStripeId) :
|
||||
SubscriptionItem(subscription, _sponsoredPlanStripeId);
|
||||
private Stripe.SubscriptionItem AddStripeItem(Subscription subscription) =>
|
||||
_applySponsorship ?
|
||||
SubscriptionItem(subscription, _sponsoredPlanStripeId) :
|
||||
SubscriptionItem(subscription, _existingPlanStripeId);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class StorageSubscriptionUpdate : SubscriptionUpdate
|
||||
{
|
||||
private long? _prevStorage;
|
||||
private readonly string _plan;
|
||||
private readonly long? _additionalStorage;
|
||||
protected override List<string> PlanIds => new() { _plan };
|
||||
|
||||
public StorageSubscriptionUpdate(string plan, long? additionalStorage)
|
||||
{
|
||||
_plan = plan;
|
||||
_additionalStorage = additionalStorage;
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
||||
{
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
_prevStorage = item?.Quantity ?? 0;
|
||||
return new()
|
||||
{
|
||||
new SubscriptionItemOptions
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = _plan,
|
||||
Quantity = _additionalStorage,
|
||||
Deleted = (item?.Id != null && _additionalStorage == 0) ? true : (bool?)null,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
|
||||
{
|
||||
if (!_prevStorage.HasValue)
|
||||
{
|
||||
throw new Exception("Unknown previous value, must first call UpgradeItemsOptions");
|
||||
}
|
||||
|
||||
var item = SubscriptionItem(subscription, PlanIds.Single());
|
||||
return new()
|
||||
{
|
||||
new SubscriptionItemOptions
|
||||
{
|
||||
Id = item?.Id,
|
||||
Plan = _plan,
|
||||
Quantity = _prevStorage.Value,
|
||||
Deleted = _prevStorage.Value == 0 ? true : (bool?)null,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class SponsorOrganizationSubscriptionUpdate : SubscriptionUpdate
|
||||
{
|
||||
private readonly string _existingPlanStripeId;
|
||||
private readonly string _sponsoredPlanStripeId;
|
||||
private readonly bool _applySponsorship;
|
||||
protected override List<string> PlanIds => new() { _existingPlanStripeId, _sponsoredPlanStripeId };
|
||||
|
||||
public SponsorOrganizationSubscriptionUpdate(StaticStore.Plan existingPlan, StaticStore.SponsoredPlan sponsoredPlan, bool applySponsorship)
|
||||
{
|
||||
_existingPlanStripeId = existingPlan.StripePlanId;
|
||||
_sponsoredPlanStripeId = sponsoredPlan?.StripePlanId;
|
||||
_applySponsorship = applySponsorship;
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
|
||||
{
|
||||
var result = new List<SubscriptionItemOptions>();
|
||||
if (!string.IsNullOrWhiteSpace(AddStripePlanId))
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = AddStripeItem(subscription)?.Id,
|
||||
Plan = AddStripePlanId,
|
||||
Quantity = 0,
|
||||
Deleted = true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(RemoveStripePlanId))
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = RemoveStripeItem(subscription)?.Id,
|
||||
Plan = RemoveStripePlanId,
|
||||
Quantity = 1,
|
||||
Deleted = false,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
|
||||
{
|
||||
var result = new List<SubscriptionItemOptions>();
|
||||
if (RemoveStripeItem(subscription) != null)
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = RemoveStripeItem(subscription)?.Id,
|
||||
Plan = RemoveStripePlanId,
|
||||
Quantity = 0,
|
||||
Deleted = true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(AddStripePlanId))
|
||||
{
|
||||
result.Add(new SubscriptionItemOptions
|
||||
{
|
||||
Id = AddStripeItem(subscription)?.Id,
|
||||
Plan = AddStripePlanId,
|
||||
Quantity = 1,
|
||||
Deleted = false,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private string RemoveStripePlanId => _applySponsorship ? _existingPlanStripeId : _sponsoredPlanStripeId;
|
||||
private string AddStripePlanId => _applySponsorship ? _sponsoredPlanStripeId : _existingPlanStripeId;
|
||||
private Stripe.SubscriptionItem RemoveStripeItem(Subscription subscription) =>
|
||||
_applySponsorship ?
|
||||
SubscriptionItem(subscription, _existingPlanStripeId) :
|
||||
SubscriptionItem(subscription, _sponsoredPlanStripeId);
|
||||
private Stripe.SubscriptionItem AddStripeItem(Subscription subscription) =>
|
||||
_applySponsorship ?
|
||||
SubscriptionItem(subscription, _sponsoredPlanStripeId) :
|
||||
SubscriptionItem(subscription, _existingPlanStripeId);
|
||||
|
||||
}
|
||||
|
@ -1,153 +1,154 @@
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class TaxInfo
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
private string _taxIdNumber = null;
|
||||
private string _taxIdType = null;
|
||||
public class TaxInfo
|
||||
{
|
||||
private string _taxIdNumber = null;
|
||||
private string _taxIdType = null;
|
||||
|
||||
public string TaxIdNumber
|
||||
{
|
||||
get => _taxIdNumber;
|
||||
set
|
||||
public string TaxIdNumber
|
||||
{
|
||||
_taxIdNumber = value;
|
||||
_taxIdType = null;
|
||||
}
|
||||
}
|
||||
public string StripeTaxRateId { get; set; }
|
||||
public string BillingAddressLine1 { get; set; }
|
||||
public string BillingAddressLine2 { get; set; }
|
||||
public string BillingAddressCity { get; set; }
|
||||
public string BillingAddressState { get; set; }
|
||||
public string BillingAddressPostalCode { get; set; }
|
||||
public string BillingAddressCountry { get; set; } = "US";
|
||||
public string TaxIdType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(BillingAddressCountry) ||
|
||||
string.IsNullOrWhiteSpace(TaxIdNumber))
|
||||
get => _taxIdNumber;
|
||||
set
|
||||
{
|
||||
return null;
|
||||
_taxIdNumber = value;
|
||||
_taxIdType = null;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(_taxIdType))
|
||||
}
|
||||
public string StripeTaxRateId { get; set; }
|
||||
public string BillingAddressLine1 { get; set; }
|
||||
public string BillingAddressLine2 { get; set; }
|
||||
public string BillingAddressCity { get; set; }
|
||||
public string BillingAddressState { get; set; }
|
||||
public string BillingAddressPostalCode { get; set; }
|
||||
public string BillingAddressCountry { get; set; } = "US";
|
||||
public string TaxIdType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(BillingAddressCountry) ||
|
||||
string.IsNullOrWhiteSpace(TaxIdNumber))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(_taxIdType))
|
||||
{
|
||||
return _taxIdType;
|
||||
}
|
||||
|
||||
switch (BillingAddressCountry)
|
||||
{
|
||||
case "AE":
|
||||
_taxIdType = "ae_trn";
|
||||
break;
|
||||
case "AU":
|
||||
_taxIdType = "au_abn";
|
||||
break;
|
||||
case "BR":
|
||||
_taxIdType = "br_cnpj";
|
||||
break;
|
||||
case "CA":
|
||||
// May break for those in Québec given the assumption of QST
|
||||
if (BillingAddressState?.Contains("bec") ?? false)
|
||||
{
|
||||
_taxIdType = "ca_qst";
|
||||
break;
|
||||
}
|
||||
_taxIdType = "ca_bn";
|
||||
break;
|
||||
case "CL":
|
||||
_taxIdType = "cl_tin";
|
||||
break;
|
||||
case "AT":
|
||||
case "BE":
|
||||
case "BG":
|
||||
case "CY":
|
||||
case "CZ":
|
||||
case "DE":
|
||||
case "DK":
|
||||
case "EE":
|
||||
case "ES":
|
||||
case "FI":
|
||||
case "FR":
|
||||
case "GB":
|
||||
case "GR":
|
||||
case "HR":
|
||||
case "HU":
|
||||
case "IE":
|
||||
case "IT":
|
||||
case "LT":
|
||||
case "LU":
|
||||
case "LV":
|
||||
case "MT":
|
||||
case "NL":
|
||||
case "PL":
|
||||
case "PT":
|
||||
case "RO":
|
||||
case "SE":
|
||||
case "SI":
|
||||
case "SK":
|
||||
_taxIdType = "eu_vat";
|
||||
break;
|
||||
case "HK":
|
||||
_taxIdType = "hk_br";
|
||||
break;
|
||||
case "IN":
|
||||
_taxIdType = "in_gst";
|
||||
break;
|
||||
case "JP":
|
||||
_taxIdType = "jp_cn";
|
||||
break;
|
||||
case "KR":
|
||||
_taxIdType = "kr_brn";
|
||||
break;
|
||||
case "LI":
|
||||
_taxIdType = "li_uid";
|
||||
break;
|
||||
case "MX":
|
||||
_taxIdType = "mx_rfc";
|
||||
break;
|
||||
case "MY":
|
||||
_taxIdType = "my_sst";
|
||||
break;
|
||||
case "NO":
|
||||
_taxIdType = "no_vat";
|
||||
break;
|
||||
case "NZ":
|
||||
_taxIdType = "nz_gst";
|
||||
break;
|
||||
case "RU":
|
||||
_taxIdType = "ru_inn";
|
||||
break;
|
||||
case "SA":
|
||||
_taxIdType = "sa_vat";
|
||||
break;
|
||||
case "SG":
|
||||
_taxIdType = "sg_gst";
|
||||
break;
|
||||
case "TH":
|
||||
_taxIdType = "th_vat";
|
||||
break;
|
||||
case "TW":
|
||||
_taxIdType = "tw_vat";
|
||||
break;
|
||||
case "US":
|
||||
_taxIdType = "us_ein";
|
||||
break;
|
||||
case "ZA":
|
||||
_taxIdType = "za_vat";
|
||||
break;
|
||||
default:
|
||||
_taxIdType = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return _taxIdType;
|
||||
}
|
||||
}
|
||||
|
||||
switch (BillingAddressCountry)
|
||||
{
|
||||
case "AE":
|
||||
_taxIdType = "ae_trn";
|
||||
break;
|
||||
case "AU":
|
||||
_taxIdType = "au_abn";
|
||||
break;
|
||||
case "BR":
|
||||
_taxIdType = "br_cnpj";
|
||||
break;
|
||||
case "CA":
|
||||
// May break for those in Québec given the assumption of QST
|
||||
if (BillingAddressState?.Contains("bec") ?? false)
|
||||
{
|
||||
_taxIdType = "ca_qst";
|
||||
break;
|
||||
}
|
||||
_taxIdType = "ca_bn";
|
||||
break;
|
||||
case "CL":
|
||||
_taxIdType = "cl_tin";
|
||||
break;
|
||||
case "AT":
|
||||
case "BE":
|
||||
case "BG":
|
||||
case "CY":
|
||||
case "CZ":
|
||||
case "DE":
|
||||
case "DK":
|
||||
case "EE":
|
||||
case "ES":
|
||||
case "FI":
|
||||
case "FR":
|
||||
case "GB":
|
||||
case "GR":
|
||||
case "HR":
|
||||
case "HU":
|
||||
case "IE":
|
||||
case "IT":
|
||||
case "LT":
|
||||
case "LU":
|
||||
case "LV":
|
||||
case "MT":
|
||||
case "NL":
|
||||
case "PL":
|
||||
case "PT":
|
||||
case "RO":
|
||||
case "SE":
|
||||
case "SI":
|
||||
case "SK":
|
||||
_taxIdType = "eu_vat";
|
||||
break;
|
||||
case "HK":
|
||||
_taxIdType = "hk_br";
|
||||
break;
|
||||
case "IN":
|
||||
_taxIdType = "in_gst";
|
||||
break;
|
||||
case "JP":
|
||||
_taxIdType = "jp_cn";
|
||||
break;
|
||||
case "KR":
|
||||
_taxIdType = "kr_brn";
|
||||
break;
|
||||
case "LI":
|
||||
_taxIdType = "li_uid";
|
||||
break;
|
||||
case "MX":
|
||||
_taxIdType = "mx_rfc";
|
||||
break;
|
||||
case "MY":
|
||||
_taxIdType = "my_sst";
|
||||
break;
|
||||
case "NO":
|
||||
_taxIdType = "no_vat";
|
||||
break;
|
||||
case "NZ":
|
||||
_taxIdType = "nz_gst";
|
||||
break;
|
||||
case "RU":
|
||||
_taxIdType = "ru_inn";
|
||||
break;
|
||||
case "SA":
|
||||
_taxIdType = "sa_vat";
|
||||
break;
|
||||
case "SG":
|
||||
_taxIdType = "sg_gst";
|
||||
break;
|
||||
case "TH":
|
||||
_taxIdType = "th_vat";
|
||||
break;
|
||||
case "TW":
|
||||
_taxIdType = "tw_vat";
|
||||
break;
|
||||
case "US":
|
||||
_taxIdType = "us_ein";
|
||||
break;
|
||||
case "ZA":
|
||||
_taxIdType = "za_vat";
|
||||
break;
|
||||
default:
|
||||
_taxIdType = null;
|
||||
break;
|
||||
}
|
||||
|
||||
return _taxIdType;
|
||||
public bool HasTaxId
|
||||
{
|
||||
get => !string.IsNullOrWhiteSpace(TaxIdNumber) &&
|
||||
!string.IsNullOrWhiteSpace(TaxIdType);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasTaxId
|
||||
{
|
||||
get => !string.IsNullOrWhiteSpace(TaxIdNumber) &&
|
||||
!string.IsNullOrWhiteSpace(TaxIdType);
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,36 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Bit.Core.Entities;
|
||||
|
||||
namespace Bit.Core.Models.Business.Tokenables;
|
||||
|
||||
public class EmergencyAccessInviteTokenable : Tokens.ExpiringTokenable
|
||||
namespace Bit.Core.Models.Business.Tokenables
|
||||
{
|
||||
public const string ClearTextPrefix = "";
|
||||
public const string DataProtectorPurpose = "EmergencyAccessServiceDataProtector";
|
||||
public const string TokenIdentifier = "EmergencyAccessInvite";
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
public EmergencyAccessInviteTokenable(DateTime expirationDate)
|
||||
public class EmergencyAccessInviteTokenable : Tokens.ExpiringTokenable
|
||||
{
|
||||
ExpirationDate = expirationDate;
|
||||
}
|
||||
public const string ClearTextPrefix = "";
|
||||
public const string DataProtectorPurpose = "EmergencyAccessServiceDataProtector";
|
||||
public const string TokenIdentifier = "EmergencyAccessInvite";
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
public EmergencyAccessInviteTokenable(EmergencyAccess user, int hoursTillExpiration)
|
||||
{
|
||||
Id = user.Id;
|
||||
Email = user.Email;
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(hoursTillExpiration);
|
||||
}
|
||||
[JsonConstructor]
|
||||
public EmergencyAccessInviteTokenable(DateTime expirationDate)
|
||||
{
|
||||
ExpirationDate = expirationDate;
|
||||
}
|
||||
|
||||
public bool IsValid(Guid id, string email)
|
||||
{
|
||||
return Id == id &&
|
||||
Email.Equals(email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
public EmergencyAccessInviteTokenable(EmergencyAccess user, int hoursTillExpiration)
|
||||
{
|
||||
Id = user.Id;
|
||||
Email = user.Email;
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(hoursTillExpiration);
|
||||
}
|
||||
|
||||
protected override bool TokenIsValid() => Identifier == TokenIdentifier && Id != default && !string.IsNullOrWhiteSpace(Email);
|
||||
public bool IsValid(Guid id, string email)
|
||||
{
|
||||
return Id == id &&
|
||||
Email.Equals(email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
protected override bool TokenIsValid() => Identifier == TokenIdentifier && Id != default && !string.IsNullOrWhiteSpace(Email);
|
||||
}
|
||||
}
|
||||
|
@ -2,42 +2,43 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Tokens;
|
||||
|
||||
namespace Bit.Core.Models.Business.Tokenables;
|
||||
|
||||
public class HCaptchaTokenable : ExpiringTokenable
|
||||
namespace Bit.Core.Models.Business.Tokenables
|
||||
{
|
||||
private const double _tokenLifetimeInHours = (double)5 / 60; // 5 minutes
|
||||
public const string ClearTextPrefix = "BWCaptchaBypass_";
|
||||
public const string DataProtectorPurpose = "CaptchaServiceDataProtector";
|
||||
public const string TokenIdentifier = "CaptchaBypassToken";
|
||||
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
[JsonConstructor]
|
||||
public HCaptchaTokenable()
|
||||
public class HCaptchaTokenable : ExpiringTokenable
|
||||
{
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(_tokenLifetimeInHours);
|
||||
}
|
||||
private const double _tokenLifetimeInHours = (double)5 / 60; // 5 minutes
|
||||
public const string ClearTextPrefix = "BWCaptchaBypass_";
|
||||
public const string DataProtectorPurpose = "CaptchaServiceDataProtector";
|
||||
public const string TokenIdentifier = "CaptchaBypassToken";
|
||||
|
||||
public HCaptchaTokenable(User user) : this()
|
||||
{
|
||||
Id = user?.Id ?? default;
|
||||
Email = user?.Email;
|
||||
}
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
public Guid Id { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
public bool TokenIsValid(User user)
|
||||
{
|
||||
if (Id == default || Email == default || user == null)
|
||||
[JsonConstructor]
|
||||
public HCaptchaTokenable()
|
||||
{
|
||||
return false;
|
||||
ExpirationDate = DateTime.UtcNow.AddHours(_tokenLifetimeInHours);
|
||||
}
|
||||
|
||||
return Id == user.Id &&
|
||||
Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
public HCaptchaTokenable(User user) : this()
|
||||
{
|
||||
Id = user?.Id ?? default;
|
||||
Email = user?.Email;
|
||||
}
|
||||
|
||||
// Validates deserialized
|
||||
protected override bool TokenIsValid() => Identifier == TokenIdentifier && Id != default && !string.IsNullOrWhiteSpace(Email);
|
||||
public bool TokenIsValid(User user)
|
||||
{
|
||||
if (Id == default || Email == default || user == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Id == user.Id &&
|
||||
Email.Equals(user.Email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
// Validates deserialized
|
||||
protected override bool TokenIsValid() => Identifier == TokenIdentifier && Id != default && !string.IsNullOrWhiteSpace(Email);
|
||||
}
|
||||
}
|
||||
|
@ -3,54 +3,55 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Tokens;
|
||||
|
||||
namespace Bit.Core.Models.Business.Tokenables;
|
||||
|
||||
public class OrganizationSponsorshipOfferTokenable : Tokenable
|
||||
namespace Bit.Core.Models.Business.Tokenables
|
||||
{
|
||||
public const string ClearTextPrefix = "BWOrganizationSponsorship_";
|
||||
public const string DataProtectorPurpose = "OrganizationSponsorshipDataProtector";
|
||||
public const string TokenIdentifier = "OrganizationSponsorshipOfferToken";
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
public Guid Id { get; set; }
|
||||
public PlanSponsorshipType SponsorshipType { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
public override bool Valid => !string.IsNullOrWhiteSpace(Email) &&
|
||||
Identifier == TokenIdentifier &&
|
||||
Id != default;
|
||||
|
||||
|
||||
[JsonConstructor]
|
||||
public OrganizationSponsorshipOfferTokenable() { }
|
||||
|
||||
public OrganizationSponsorshipOfferTokenable(OrganizationSponsorship sponsorship)
|
||||
public class OrganizationSponsorshipOfferTokenable : Tokenable
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sponsorship.OfferedToEmail))
|
||||
{
|
||||
throw new ArgumentException("Invalid OrganizationSponsorship to create a token, OfferedToEmail is required", nameof(sponsorship));
|
||||
}
|
||||
Email = sponsorship.OfferedToEmail;
|
||||
public const string ClearTextPrefix = "BWOrganizationSponsorship_";
|
||||
public const string DataProtectorPurpose = "OrganizationSponsorshipDataProtector";
|
||||
public const string TokenIdentifier = "OrganizationSponsorshipOfferToken";
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
public Guid Id { get; set; }
|
||||
public PlanSponsorshipType SponsorshipType { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
if (!sponsorship.PlanSponsorshipType.HasValue)
|
||||
{
|
||||
throw new ArgumentException("Invalid OrganizationSponsorship to create a token, PlanSponsorshipType is required", nameof(sponsorship));
|
||||
}
|
||||
SponsorshipType = sponsorship.PlanSponsorshipType.Value;
|
||||
public override bool Valid => !string.IsNullOrWhiteSpace(Email) &&
|
||||
Identifier == TokenIdentifier &&
|
||||
Id != default;
|
||||
|
||||
if (sponsorship.Id == default)
|
||||
|
||||
[JsonConstructor]
|
||||
public OrganizationSponsorshipOfferTokenable() { }
|
||||
|
||||
public OrganizationSponsorshipOfferTokenable(OrganizationSponsorship sponsorship)
|
||||
{
|
||||
throw new ArgumentException("Invalid OrganizationSponsorship to create a token, Id is required", nameof(sponsorship));
|
||||
if (string.IsNullOrWhiteSpace(sponsorship.OfferedToEmail))
|
||||
{
|
||||
throw new ArgumentException("Invalid OrganizationSponsorship to create a token, OfferedToEmail is required", nameof(sponsorship));
|
||||
}
|
||||
Email = sponsorship.OfferedToEmail;
|
||||
|
||||
if (!sponsorship.PlanSponsorshipType.HasValue)
|
||||
{
|
||||
throw new ArgumentException("Invalid OrganizationSponsorship to create a token, PlanSponsorshipType is required", nameof(sponsorship));
|
||||
}
|
||||
SponsorshipType = sponsorship.PlanSponsorshipType.Value;
|
||||
|
||||
if (sponsorship.Id == default)
|
||||
{
|
||||
throw new ArgumentException("Invalid OrganizationSponsorship to create a token, Id is required", nameof(sponsorship));
|
||||
}
|
||||
Id = sponsorship.Id;
|
||||
}
|
||||
Id = sponsorship.Id;
|
||||
|
||||
public bool IsValid(OrganizationSponsorship sponsorship, string currentUserEmail) =>
|
||||
sponsorship != null &&
|
||||
sponsorship.PlanSponsorshipType.HasValue &&
|
||||
SponsorshipType == sponsorship.PlanSponsorshipType.Value &&
|
||||
Id == sponsorship.Id &&
|
||||
!string.IsNullOrWhiteSpace(sponsorship.OfferedToEmail) &&
|
||||
Email.Equals(currentUserEmail, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
Email.Equals(sponsorship.OfferedToEmail, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
}
|
||||
|
||||
public bool IsValid(OrganizationSponsorship sponsorship, string currentUserEmail) =>
|
||||
sponsorship != null &&
|
||||
sponsorship.PlanSponsorshipType.HasValue &&
|
||||
SponsorshipType == sponsorship.PlanSponsorshipType.Value &&
|
||||
Id == sponsorship.Id &&
|
||||
!string.IsNullOrWhiteSpace(sponsorship.OfferedToEmail) &&
|
||||
Email.Equals(currentUserEmail, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
Email.Equals(sponsorship.OfferedToEmail, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
}
|
||||
|
@ -2,42 +2,43 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Tokens;
|
||||
|
||||
namespace Bit.Core.Models.Business.Tokenables;
|
||||
|
||||
public class SsoTokenable : ExpiringTokenable
|
||||
namespace Bit.Core.Models.Business.Tokenables
|
||||
{
|
||||
public const string ClearTextPrefix = "BWUserPrefix_";
|
||||
public const string DataProtectorPurpose = "SsoTokenDataProtector";
|
||||
public const string TokenIdentifier = "ssoToken";
|
||||
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string DomainHint { get; set; }
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
|
||||
[JsonConstructor]
|
||||
public SsoTokenable() { }
|
||||
|
||||
public SsoTokenable(Organization organization, double tokenLifetimeInSeconds) : this()
|
||||
public class SsoTokenable : ExpiringTokenable
|
||||
{
|
||||
OrganizationId = organization?.Id ?? default;
|
||||
DomainHint = organization?.Identifier;
|
||||
ExpirationDate = DateTime.UtcNow.AddSeconds(tokenLifetimeInSeconds);
|
||||
}
|
||||
public const string ClearTextPrefix = "BWUserPrefix_";
|
||||
public const string DataProtectorPurpose = "SsoTokenDataProtector";
|
||||
public const string TokenIdentifier = "ssoToken";
|
||||
|
||||
public bool TokenIsValid(Organization organization)
|
||||
{
|
||||
if (OrganizationId == default || DomainHint == default || organization == null || !Valid)
|
||||
public Guid OrganizationId { get; set; }
|
||||
public string DomainHint { get; set; }
|
||||
public string Identifier { get; set; } = TokenIdentifier;
|
||||
|
||||
[JsonConstructor]
|
||||
public SsoTokenable() { }
|
||||
|
||||
public SsoTokenable(Organization organization, double tokenLifetimeInSeconds) : this()
|
||||
{
|
||||
return false;
|
||||
OrganizationId = organization?.Id ?? default;
|
||||
DomainHint = organization?.Identifier;
|
||||
ExpirationDate = DateTime.UtcNow.AddSeconds(tokenLifetimeInSeconds);
|
||||
}
|
||||
|
||||
return organization.Identifier.Equals(DomainHint, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& organization.Id.Equals(OrganizationId);
|
||||
}
|
||||
public bool TokenIsValid(Organization organization)
|
||||
{
|
||||
if (OrganizationId == default || DomainHint == default || organization == null || !Valid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validates deserialized
|
||||
protected override bool TokenIsValid() =>
|
||||
Identifier == TokenIdentifier
|
||||
&& OrganizationId != default
|
||||
&& !string.IsNullOrWhiteSpace(DomainHint);
|
||||
return organization.Identifier.Equals(DomainHint, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& organization.Id.Equals(OrganizationId);
|
||||
}
|
||||
|
||||
// Validates deserialized
|
||||
protected override bool TokenIsValid() =>
|
||||
Identifier == TokenIdentifier
|
||||
&& OrganizationId != default
|
||||
&& !string.IsNullOrWhiteSpace(DomainHint);
|
||||
}
|
||||
}
|
||||
|
@ -7,167 +7,168 @@ using Bit.Core.Entities;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Services;
|
||||
|
||||
namespace Bit.Core.Models.Business;
|
||||
|
||||
public class UserLicense : ILicense
|
||||
namespace Bit.Core.Models.Business
|
||||
{
|
||||
public UserLicense()
|
||||
{ }
|
||||
|
||||
public UserLicense(User user, SubscriptionInfo subscriptionInfo, ILicensingService licenseService,
|
||||
int? version = null)
|
||||
public class UserLicense : ILicense
|
||||
{
|
||||
LicenseType = Enums.LicenseType.User;
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
Version = version.GetValueOrDefault(1);
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Issued = DateTime.UtcNow;
|
||||
Expires = subscriptionInfo?.UpcomingInvoice?.Date != null ?
|
||||
subscriptionInfo.UpcomingInvoice.Date.Value.AddDays(7) :
|
||||
user.PremiumExpirationDate?.AddDays(7);
|
||||
Refresh = subscriptionInfo?.UpcomingInvoice?.Date;
|
||||
Trial = (subscriptionInfo?.Subscription?.TrialEndDate.HasValue ?? false) &&
|
||||
subscriptionInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow;
|
||||
public UserLicense()
|
||||
{ }
|
||||
|
||||
Hash = Convert.ToBase64String(ComputeHash());
|
||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||
}
|
||||
|
||||
public UserLicense(User user, ILicensingService licenseService, int? version = null)
|
||||
{
|
||||
LicenseType = Enums.LicenseType.User;
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
Version = version.GetValueOrDefault(1);
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Issued = DateTime.UtcNow;
|
||||
Expires = user.PremiumExpirationDate?.AddDays(7);
|
||||
Refresh = user.PremiumExpirationDate?.Date;
|
||||
Trial = false;
|
||||
|
||||
Hash = Convert.ToBase64String(ComputeHash());
|
||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||
}
|
||||
|
||||
public string LicenseKey { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public bool Premium { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public int Version { get; set; }
|
||||
public DateTime Issued { get; set; }
|
||||
public DateTime? Refresh { get; set; }
|
||||
public DateTime? Expires { get; set; }
|
||||
public bool Trial { get; set; }
|
||||
public LicenseType? LicenseType { get; set; }
|
||||
public string Hash { get; set; }
|
||||
public string Signature { get; set; }
|
||||
[JsonIgnore]
|
||||
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
||||
|
||||
public byte[] GetDataBytes(bool forHash = false)
|
||||
{
|
||||
string data = null;
|
||||
if (Version == 1)
|
||||
public UserLicense(User user, SubscriptionInfo subscriptionInfo, ILicensingService licenseService,
|
||||
int? version = null)
|
||||
{
|
||||
var props = typeof(UserLicense)
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(p =>
|
||||
!p.Name.Equals(nameof(Signature)) &&
|
||||
!p.Name.Equals(nameof(SignatureBytes)) &&
|
||||
!p.Name.Equals(nameof(LicenseType)) &&
|
||||
(
|
||||
!forHash ||
|
||||
LicenseType = Enums.LicenseType.User;
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
Version = version.GetValueOrDefault(1);
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Issued = DateTime.UtcNow;
|
||||
Expires = subscriptionInfo?.UpcomingInvoice?.Date != null ?
|
||||
subscriptionInfo.UpcomingInvoice.Date.Value.AddDays(7) :
|
||||
user.PremiumExpirationDate?.AddDays(7);
|
||||
Refresh = subscriptionInfo?.UpcomingInvoice?.Date;
|
||||
Trial = (subscriptionInfo?.Subscription?.TrialEndDate.HasValue ?? false) &&
|
||||
subscriptionInfo.Subscription.TrialEndDate.Value > DateTime.UtcNow;
|
||||
|
||||
Hash = Convert.ToBase64String(ComputeHash());
|
||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||
}
|
||||
|
||||
public UserLicense(User user, ILicensingService licenseService, int? version = null)
|
||||
{
|
||||
LicenseType = Enums.LicenseType.User;
|
||||
LicenseKey = user.LicenseKey;
|
||||
Id = user.Id;
|
||||
Name = user.Name;
|
||||
Email = user.Email;
|
||||
Version = version.GetValueOrDefault(1);
|
||||
Premium = user.Premium;
|
||||
MaxStorageGb = user.MaxStorageGb;
|
||||
Issued = DateTime.UtcNow;
|
||||
Expires = user.PremiumExpirationDate?.AddDays(7);
|
||||
Refresh = user.PremiumExpirationDate?.Date;
|
||||
Trial = false;
|
||||
|
||||
Hash = Convert.ToBase64String(ComputeHash());
|
||||
Signature = Convert.ToBase64String(licenseService.SignLicense(this));
|
||||
}
|
||||
|
||||
public string LicenseKey { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
public bool Premium { get; set; }
|
||||
public short? MaxStorageGb { get; set; }
|
||||
public int Version { get; set; }
|
||||
public DateTime Issued { get; set; }
|
||||
public DateTime? Refresh { get; set; }
|
||||
public DateTime? Expires { get; set; }
|
||||
public bool Trial { get; set; }
|
||||
public LicenseType? LicenseType { get; set; }
|
||||
public string Hash { get; set; }
|
||||
public string Signature { get; set; }
|
||||
[JsonIgnore]
|
||||
public byte[] SignatureBytes => Convert.FromBase64String(Signature);
|
||||
|
||||
public byte[] GetDataBytes(bool forHash = false)
|
||||
{
|
||||
string data = null;
|
||||
if (Version == 1)
|
||||
{
|
||||
var props = typeof(UserLicense)
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(p =>
|
||||
!p.Name.Equals(nameof(Signature)) &&
|
||||
!p.Name.Equals(nameof(SignatureBytes)) &&
|
||||
!p.Name.Equals(nameof(LicenseType)) &&
|
||||
(
|
||||
!p.Name.Equals(nameof(Hash)) &&
|
||||
!p.Name.Equals(nameof(Issued)) &&
|
||||
!p.Name.Equals(nameof(Refresh))
|
||||
)
|
||||
))
|
||||
.OrderBy(p => p.Name)
|
||||
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
||||
.Aggregate((c, n) => $"{c}|{n}");
|
||||
data = $"license:user|{props}";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
!forHash ||
|
||||
(
|
||||
!p.Name.Equals(nameof(Hash)) &&
|
||||
!p.Name.Equals(nameof(Issued)) &&
|
||||
!p.Name.Equals(nameof(Refresh))
|
||||
)
|
||||
))
|
||||
.OrderBy(p => p.Name)
|
||||
.Select(p => $"{p.Name}:{Utilities.CoreHelpers.FormatLicenseSignatureValue(p.GetValue(this, null))}")
|
||||
.Aggregate((c, n) => $"{c}|{n}");
|
||||
data = $"license:user|{props}";
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetBytes(data);
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetBytes(data);
|
||||
}
|
||||
|
||||
public byte[] ComputeHash()
|
||||
{
|
||||
using (var alg = SHA256.Create())
|
||||
public byte[] ComputeHash()
|
||||
{
|
||||
return alg.ComputeHash(GetDataBytes(true));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanUse(User user)
|
||||
{
|
||||
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
using (var alg = SHA256.Create())
|
||||
{
|
||||
return alg.ComputeHash(GetDataBytes(true));
|
||||
}
|
||||
}
|
||||
|
||||
if (Version == 1)
|
||||
public bool CanUse(User user)
|
||||
{
|
||||
return user.EmailVerified && user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool VerifyData(User user)
|
||||
{
|
||||
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
if (Version == 1)
|
||||
{
|
||||
return user.EmailVerified && user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
if (Version == 1)
|
||||
public bool VerifyData(User user)
|
||||
{
|
||||
return
|
||||
user.LicenseKey != null && user.LicenseKey.Equals(LicenseKey) &&
|
||||
user.Premium == Premium &&
|
||||
user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool VerifySignature(X509Certificate2 certificate)
|
||||
{
|
||||
using (var rsa = certificate.GetRSAPublicKey())
|
||||
{
|
||||
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Sign(X509Certificate2 certificate)
|
||||
{
|
||||
if (!certificate.HasPrivateKey)
|
||||
{
|
||||
throw new InvalidOperationException("You don't have the private key!");
|
||||
if (Version == 1)
|
||||
{
|
||||
return
|
||||
user.LicenseKey != null && user.LicenseKey.Equals(LicenseKey) &&
|
||||
user.Premium == Premium &&
|
||||
user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Version {Version} is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
using (var rsa = certificate.GetRSAPrivateKey())
|
||||
public bool VerifySignature(X509Certificate2 certificate)
|
||||
{
|
||||
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
using (var rsa = certificate.GetRSAPublicKey())
|
||||
{
|
||||
return rsa.VerifyData(GetDataBytes(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Sign(X509Certificate2 certificate)
|
||||
{
|
||||
if (!certificate.HasPrivateKey)
|
||||
{
|
||||
throw new InvalidOperationException("You don't have the private key!");
|
||||
}
|
||||
|
||||
using (var rsa = certificate.GetRSAPrivateKey())
|
||||
{
|
||||
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user