1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-06 02:22:49 -05:00

Turn on file scoped namespaces (#2225)

This commit is contained in:
Justin Baur
2022-08-29 14:53:16 -04:00
committed by GitHub
parent 7c4521e0b4
commit 34fb4cca2a
1206 changed files with 73816 additions and 75022 deletions

View File

@ -3,133 +3,132 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Utilities;
namespace Bit.Billing.Models
namespace Bit.Billing.Models;
public class AppleReceiptStatus
{
public class AppleReceiptStatus
[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()
{
[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; }
return LatestReceiptInfo?.LastOrDefault()?.OriginalTransactionId;
}
public string GetOriginalTransactionId()
{
return LatestReceiptInfo?.LastOrDefault()?.OriginalTransactionId;
}
public string GetLastTransactionId()
{
return LatestReceiptInfo?.LastOrDefault()?.TransactionId;
}
public string GetLastTransactionId()
{
return LatestReceiptInfo?.LastOrDefault()?.TransactionId;
}
public AppleTransaction GetLastTransaction()
{
return LatestReceiptInfo?.LastOrDefault();
}
public AppleTransaction GetLastTransaction()
{
return LatestReceiptInfo?.LastOrDefault();
}
public DateTime? GetLastExpiresDate()
{
return LatestReceiptInfo?.LastOrDefault()?.ExpiresDate;
}
public DateTime? GetLastExpiresDate()
{
return LatestReceiptInfo?.LastOrDefault()?.ExpiresDate;
}
public string GetReceiptData()
{
return LatestReceipt;
}
public string GetReceiptData()
{
return LatestReceipt;
}
public DateTime? GetLastCancellationDate()
{
return LatestReceiptInfo?.LastOrDefault()?.CancellationDate;
}
public DateTime? GetLastCancellationDate()
public bool IsRefunded()
{
var cancellationDate = GetLastCancellationDate();
var expiresDate = GetLastCancellationDate();
if (cancellationDate.HasValue && expiresDate.HasValue)
{
return LatestReceiptInfo?.LastOrDefault()?.CancellationDate;
return cancellationDate.Value <= expiresDate.Value;
}
return false;
}
public bool IsRefunded()
public Transaction BuildTransactionFromLastTransaction(decimal amount, Guid userId)
{
return new Transaction
{
var cancellationDate = GetLastCancellationDate();
var expiresDate = GetLastCancellationDate();
if (cancellationDate.HasValue && expiresDate.HasValue)
{
return cancellationDate.Value <= expiresDate.Value;
}
return false;
}
Amount = amount,
CreationDate = GetLastTransaction().PurchaseDate,
Gateway = GatewayType.AppStore,
GatewayId = GetLastTransactionId(),
UserId = userId,
PaymentMethodType = PaymentMethodType.AppleInApp,
Details = GetLastTransactionId()
};
}
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 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 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; }
}
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; }
}
}

View File

@ -2,155 +2,154 @@
using Bit.Core.Enums;
using Stripe;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class BillingInfo
{
public class BillingInfo
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 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() { }
public class BillingSource
public BillingSource(PaymentMethod method)
{
public BillingSource() { }
public BillingSource(PaymentMethod method)
if (method.Card != null)
{
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;
}
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(IPaymentSource source)
public BillingSource(IPaymentSource source)
{
if (source is BankAccount bankAccount)
{
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;
}
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";
}
public BillingSource(Braintree.PaymentMethod method)
else if (source is Card card)
{
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.");
}
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.UsBankAccountDetails bank)
public BillingSource(Braintree.PaymentMethod method)
{
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}";
}
public BillingSource(Braintree.PayPalDetails paypal)
else
{
Type = PaymentMethodType.PayPal;
Description = paypal.PayerEmail;
throw new NotSupportedException("Method not supported.");
}
public PaymentMethodType Type { get; set; }
public string CardBrand { get; set; }
public string Description { get; set; }
public bool NeedsVerification { get; set; }
}
public class BillingTransaction
public BillingSource(Braintree.UsBankAccountDetails bank)
{
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; }
Type = PaymentMethodType.BankAccount;
Description = $"{bank.BankName}, *{bank.Last4}";
}
public class BillingInvoice
public BillingSource(Braintree.PayPalDetails paypal)
{
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; }
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 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; }
}
}

View File

@ -1,10 +1,9 @@
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class CaptchaResponse
{
public class CaptchaResponse
{
public bool Success { get; set; }
public bool MaybeBot { get; set; }
public bool IsBot { get; set; }
public double Score { get; set; }
}
public bool Success { get; set; }
public bool MaybeBot { get; set; }
public bool IsBot { get; set; }
public double Score { get; set; }
}

View File

@ -1,14 +1,13 @@
namespace Bit.Core.Models.Business
{
public class ExpiringToken
{
public readonly string Token;
public readonly DateTime ExpirationDate;
namespace Bit.Core.Models.Business;
public ExpiringToken(string token, DateTime expirationDate)
{
Token = token;
ExpirationDate = expirationDate;
}
public class ExpiringToken
{
public readonly string Token;
public readonly DateTime ExpirationDate;
public ExpiringToken(string token, DateTime expirationDate)
{
Token = token;
ExpirationDate = expirationDate;
}
}

View File

@ -1,21 +1,20 @@
using System.Security.Cryptography.X509Certificates;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public interface ILicense
{
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);
}
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);
}

View File

@ -1,10 +1,9 @@
using Bit.Core.Entities;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class ImportedGroup
{
public class ImportedGroup
{
public Group Group { get; set; }
public HashSet<string> ExternalUserIds { get; set; }
}
public Group Group { get; set; }
public HashSet<string> ExternalUserIds { get; set; }
}

View File

@ -1,8 +1,7 @@
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class ImportedOrganizationUser
{
public class ImportedOrganizationUser
{
public string Email { get; set; }
public string ExternalId { get; set; }
}
public string Email { get; set; }
public string ExternalId { get; set; }
}

View File

@ -8,304 +8,303 @@ using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Settings;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class OrganizationLicense : ILicense
{
public class OrganizationLicense : ILicense
public OrganizationLicense()
{ }
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
ILicensingService licenseService, int? version = null)
{
public OrganizationLicense()
{ }
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(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
ILicensingService licenseService, int? version = null)
if (subscriptionInfo?.Subscription == null)
{
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)
if (org.PlanType == PlanType.Custom && org.ExpirationDate.HasValue)
{
if (org.PlanType == PlanType.Custom && org.ExpirationDate.HasValue)
{
Expires = Refresh = org.ExpirationDate.Value;
Trial = false;
}
else
{
Expires = Refresh = Issued.AddDays(7);
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)
{
// 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;
}
Expires = Refresh = org.ExpirationDate.Value;
Trial = false;
}
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)
else
{
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))) &&
Expires = Refresh = Issued.AddDays(7);
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)
{
// 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;
}
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)
{
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 ||
(
!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);
!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.");
}
public byte[] ComputeHash()
return Encoding.UTF8.GetBytes(data);
}
public byte[] ComputeHash()
{
using (var alg = SHA256.Create())
{
using (var alg = SHA256.Create())
{
return alg.ComputeHash(GetDataBytes(true));
}
return alg.ComputeHash(GetDataBytes(true));
}
}
public bool CanUse(IGlobalSettings globalSettings)
{
if (!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
{
return false;
}
public bool CanUse(IGlobalSettings globalSettings)
if (ValidLicenseVersion)
{
if (!Enabled || Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
{
return false;
}
return InstallationId == globalSettings.Installation.Id && SelfHost;
}
else
{
throw new NotSupportedException($"Version {Version} is not supported.");
}
}
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;
}
public bool VerifyData(Organization organization, IGlobalSettings globalSettings)
if (ValidLicenseVersion)
{
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
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)
{
return false;
valid = organization.UsersGetPremium == UsersGetPremium;
}
if (ValidLicenseVersion)
if (valid && Version >= 3)
{
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;
valid = organization.UseEvents == UseEvents;
}
else
if (valid && Version >= 4)
{
throw new NotSupportedException($"Version {Version} is not supported.");
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!");
}
public bool VerifySignature(X509Certificate2 certificate)
using (var rsa = certificate.GetRSAPrivateKey())
{
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);
}
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
}

View File

@ -1,17 +1,16 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class OrganizationSignup : OrganizationUpgrade
{
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;
}
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;
}

View File

@ -1,16 +1,15 @@
using Bit.Core.Enums;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class OrganizationUpgrade
{
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; }
}
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; }
}

View File

@ -1,25 +1,24 @@
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class OrganizationUserInvite
{
public class OrganizationUserInvite
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 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;
}
Emails = requestModel.Emails;
Type = requestModel.Type;
AccessAll = requestModel.AccessAll;
Collections = requestModel.Collections;
Permissions = requestModel.Permissions;
}
}

View File

@ -1,36 +1,35 @@
using Bit.Core.Enums.Provider;
namespace Bit.Core.Models.Business.Provider
namespace Bit.Core.Models.Business.Provider;
public class ProviderUserInvite<T>
{
public class ProviderUserInvite<T>
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 IEnumerable<T> UserIdentifiers { get; set; }
public ProviderUserType Type { get; set; }
public Guid InvitingUserId { get; set; }
public Guid ProviderId { get; set; }
return new ProviderUserInvite<string>
{
UserIdentifiers = inviteeEmails,
Type = type,
InvitingUserId = invitingUserId,
ProviderId = providerId
};
}
public static class ProviderUserInviteFactory
public static ProviderUserInvite<Guid> CreateReinvite(IEnumerable<Guid> inviteeUserIds, Guid invitingUserId, Guid providerId)
{
public static ProviderUserInvite<string> CreateIntialInvite(IEnumerable<string> inviteeEmails, ProviderUserType type, Guid invitingUserId, Guid providerId)
return new ProviderUserInvite<Guid>
{
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
};
}
UserIdentifiers = inviteeUserIds,
InvitingUserId = invitingUserId,
ProviderId = providerId
};
}
}

View File

@ -2,61 +2,60 @@
using Bit.Core.Entities;
using Bit.Core.Enums;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class ReferenceEvent
{
public class ReferenceEvent
public ReferenceEvent() { }
public ReferenceEvent(ReferenceEventType type, IReferenceable source)
{
public ReferenceEvent() { }
public ReferenceEvent(ReferenceEventType type, IReferenceable source)
Type = type;
if (source != null)
{
Type = type;
if (source != null)
{
Source = source.IsUser() ? ReferenceEventSource.User : ReferenceEventSource.Organization;
Id = source.Id;
ReferenceData = source.ReferenceData;
}
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; }
}

View File

@ -1,84 +1,83 @@
using Bit.Core.Entities;
using Stripe;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions
{
public class OrganizationSubscriptionOptionsBase : Stripe.SubscriptionCreateOptions
public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats, int additionalStorageGb, bool premiumAccessAddon)
{
public OrganizationSubscriptionOptionsBase(Organization org, StaticStore.Plan plan, TaxInfo taxInfo, int additionalSeats, int additionalStorageGb, bool premiumAccessAddon)
Items = new List<SubscriptionItemOptions>();
Metadata = new Dictionary<string, string>
{
Items = new List<SubscriptionItemOptions>();
Metadata = new Dictionary<string, string>
{
[org.GatewayIdField()] = org.Id.ToString()
};
[org.GatewayIdField()] = org.Id.ToString()
};
if (plan.StripePlanId != null)
if (plan.StripePlanId != null)
{
Items.Add(new SubscriptionItemOptions
{
Items.Add(new SubscriptionItemOptions
{
Plan = plan.StripePlanId,
Quantity = 1
});
}
if (additionalSeats > 0 && plan.StripeSeatPlanId != null)
{
Items.Add(new SubscriptionItemOptions
{
Plan = plan.StripeSeatPlanId,
Quantity = additionalSeats
});
}
if (additionalStorageGb > 0)
{
Items.Add(new SubscriptionItemOptions
{
Plan = plan.StripeStoragePlanId,
Quantity = additionalStorageGb
});
}
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 };
}
Plan = plan.StripePlanId,
Quantity = 1
});
}
}
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)
if (additionalSeats > 0 && plan.StripeSeatPlanId != null)
{
OffSession = true;
TrialPeriodDays = plan.TrialPeriodDays;
Items.Add(new SubscriptionItemOptions
{
Plan = plan.StripeSeatPlanId,
Quantity = additionalSeats
});
}
}
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)
if (additionalStorageGb > 0)
{
Customer = customerId;
Items.Add(new SubscriptionItemOptions
{
Plan = plan.StripeStoragePlanId,
Quantity = additionalStorageGb
});
}
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)
{
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;
}
}

View File

@ -1,87 +1,86 @@
using Stripe;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class SubscriptionInfo
{
public class SubscriptionInfo
public BillingSubscription Subscription { get; set; }
public BillingUpcomingInvoice UpcomingInvoice { get; set; }
public bool UsingInAppPurchase { get; set; }
public class BillingSubscription
{
public BillingSubscription Subscription { get; set; }
public BillingUpcomingInvoice UpcomingInvoice { get; set; }
public bool UsingInAppPurchase { get; set; }
public class BillingSubscription
public BillingSubscription(Subscription sub)
{
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)
{
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 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);
}
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; }
Items = sub.Items.Data.Select(i => new BillingSubscriptionItem(i));
}
}
public class BillingUpcomingInvoice
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 BillingUpcomingInvoice() { }
public BillingUpcomingInvoice(Invoice inv)
public BillingSubscriptionItem(SubscriptionItem item)
{
Amount = inv.AmountDue / 100M;
Date = inv.Created;
}
public BillingUpcomingInvoice(Braintree.Subscription sub)
{
Amount = sub.NextBillAmount.GetValueOrDefault() + sub.Balance.GetValueOrDefault();
if (Amount < 0)
if (item.Plan != null)
{
Amount = 0;
Name = item.Plan.Nickname;
Amount = item.Plan.Amount.GetValueOrDefault() / 100M;
Interval = item.Plan.Interval;
}
Date = sub.NextBillingDate;
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 DateTime? Date { 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)
{
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; }
}
}

View File

@ -1,210 +1,209 @@
using Bit.Core.Entities;
using Stripe;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public abstract class SubscriptionUpdate
{
public abstract class SubscriptionUpdate
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)
{
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 upgradeItemsOptions = UpgradeItemsOptions(subscription);
foreach (var upgradeItemOptions in upgradeItemsOptions)
{
var upgradeItemsOptions = UpgradeItemsOptions(subscription);
foreach (var upgradeItemOptions in upgradeItemsOptions)
var upgradeQuantity = upgradeItemOptions.Quantity ?? 0;
var existingQuantity = SubscriptionItem(subscription, upgradeItemOptions.Plan)?.Quantity ?? 0;
if (upgradeQuantity != existingQuantity)
{
var upgradeQuantity = upgradeItemOptions.Quantity ?? 0;
var existingQuantity = SubscriptionItem(subscription, upgradeItemOptions.Plan)?.Quantity ?? 0;
if (upgradeQuantity != existingQuantity)
{
return true;
}
return true;
}
return false;
}
protected static SubscriptionItem SubscriptionItem(Subscription subscription, string planId) =>
planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId);
return false;
}
protected static SubscriptionItem SubscriptionItem(Subscription subscription, string planId) =>
planId == null ? null : subscription.Items?.Data?.FirstOrDefault(i => i.Plan.Id == planId);
}
public class SeatSubscriptionUpdate : SubscriptionUpdate
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)
{
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)
{
_plan = plan;
_additionalSeats = additionalSeats;
_previousSeats = organization.Seats ?? 0;
}
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
{
var item = SubscriptionItem(subscription, PlanIds.Single());
return new()
{
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,
}
};
}
_plan = plan;
_additionalSeats = additionalSeats;
_previousSeats = organization.Seats ?? 0;
}
public class StorageSubscriptionUpdate : SubscriptionUpdate
public override List<SubscriptionItemOptions> UpgradeItemsOptions(Subscription subscription)
{
private long? _prevStorage;
private readonly string _plan;
private readonly long? _additionalStorage;
protected override List<string> PlanIds => new() { _plan };
public StorageSubscriptionUpdate(string plan, long? additionalStorage)
var item = SubscriptionItem(subscription, PlanIds.Single());
return new()
{
_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
{
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");
Id = item?.Id,
Plan = PlanIds.Single(),
Quantity = _additionalSeats,
Deleted = (item?.Id != null && _additionalSeats == 0) ? true : (bool?)null,
}
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
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
{
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)
var item = SubscriptionItem(subscription, PlanIds.Single());
return new()
{
_existingPlanStripeId = existingPlan.StripePlanId;
_sponsoredPlanStripeId = sponsoredPlan?.StripePlanId;
_applySponsorship = applySponsorship;
}
public override List<SubscriptionItemOptions> RevertItemsOptions(Subscription subscription)
{
var result = new List<SubscriptionItemOptions>();
if (!string.IsNullOrWhiteSpace(AddStripePlanId))
new SubscriptionItemOptions
{
result.Add(new SubscriptionItemOptions
{
Id = AddStripeItem(subscription)?.Id,
Plan = AddStripePlanId,
Quantity = 0,
Deleted = true,
});
Id = item?.Id,
Plan = PlanIds.Single(),
Quantity = _previousSeats,
Deleted = _previousSeats == 0 ? true : (bool?)null,
}
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);
}

View File

@ -1,154 +1,153 @@
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class TaxInfo
{
public class TaxInfo
private string _taxIdNumber = null;
private string _taxIdType = null;
public string TaxIdNumber
{
private string _taxIdNumber = null;
private string _taxIdType = null;
public string TaxIdNumber
get => _taxIdNumber;
set
{
get => _taxIdNumber;
set
{
_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))
{
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;
}
}
public bool HasTaxId
{
get => !string.IsNullOrWhiteSpace(TaxIdNumber) &&
!string.IsNullOrWhiteSpace(TaxIdType);
_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))
{
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;
}
}
public bool HasTaxId
{
get => !string.IsNullOrWhiteSpace(TaxIdNumber) &&
!string.IsNullOrWhiteSpace(TaxIdType);
}
}

View File

@ -1,36 +1,35 @@
using System.Text.Json.Serialization;
using Bit.Core.Entities;
namespace Bit.Core.Models.Business.Tokenables
namespace Bit.Core.Models.Business.Tokenables;
public class EmergencyAccessInviteTokenable : Tokens.ExpiringTokenable
{
public class EmergencyAccessInviteTokenable : Tokens.ExpiringTokenable
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 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)
{
ExpirationDate = expirationDate;
}
public EmergencyAccessInviteTokenable(EmergencyAccess user, int hoursTillExpiration)
{
Id = user.Id;
Email = user.Email;
ExpirationDate = DateTime.UtcNow.AddHours(hoursTillExpiration);
}
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);
ExpirationDate = expirationDate;
}
public EmergencyAccessInviteTokenable(EmergencyAccess user, int hoursTillExpiration)
{
Id = user.Id;
Email = user.Email;
ExpirationDate = DateTime.UtcNow.AddHours(hoursTillExpiration);
}
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);
}

View File

@ -2,43 +2,42 @@
using Bit.Core.Entities;
using Bit.Core.Tokens;
namespace Bit.Core.Models.Business.Tokenables
namespace Bit.Core.Models.Business.Tokenables;
public class HCaptchaTokenable : ExpiringTokenable
{
public class HCaptchaTokenable : ExpiringTokenable
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()
{
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()
{
ExpirationDate = DateTime.UtcNow.AddHours(_tokenLifetimeInHours);
}
public HCaptchaTokenable(User user) : this()
{
Id = user?.Id ?? default;
Email = user?.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);
ExpirationDate = DateTime.UtcNow.AddHours(_tokenLifetimeInHours);
}
public HCaptchaTokenable(User user) : this()
{
Id = user?.Id ?? default;
Email = user?.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);
}

View File

@ -3,55 +3,54 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Tokens;
namespace Bit.Core.Models.Business.Tokenables
namespace Bit.Core.Models.Business.Tokenables;
public class OrganizationSponsorshipOfferTokenable : Tokenable
{
public class OrganizationSponsorshipOfferTokenable : Tokenable
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 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)
if (string.IsNullOrWhiteSpace(sponsorship.OfferedToEmail))
{
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;
throw new ArgumentException("Invalid OrganizationSponsorship to create a token, OfferedToEmail is required", nameof(sponsorship));
}
Email = sponsorship.OfferedToEmail;
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);
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;
}
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);
}

View File

@ -2,43 +2,42 @@
using Bit.Core.Entities;
using Bit.Core.Tokens;
namespace Bit.Core.Models.Business.Tokenables
namespace Bit.Core.Models.Business.Tokenables;
public class SsoTokenable : ExpiringTokenable
{
public class SsoTokenable : ExpiringTokenable
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 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()
{
OrganizationId = organization?.Id ?? default;
DomainHint = organization?.Identifier;
ExpirationDate = DateTime.UtcNow.AddSeconds(tokenLifetimeInSeconds);
}
public bool TokenIsValid(Organization organization)
{
if (OrganizationId == default || DomainHint == default || organization == null || !Valid)
{
return false;
}
return organization.Identifier.Equals(DomainHint, StringComparison.InvariantCultureIgnoreCase)
&& organization.Id.Equals(OrganizationId);
}
// Validates deserialized
protected override bool TokenIsValid() =>
Identifier == TokenIdentifier
&& OrganizationId != default
&& !string.IsNullOrWhiteSpace(DomainHint);
OrganizationId = organization?.Id ?? default;
DomainHint = organization?.Identifier;
ExpirationDate = DateTime.UtcNow.AddSeconds(tokenLifetimeInSeconds);
}
public bool TokenIsValid(Organization organization)
{
if (OrganizationId == default || DomainHint == default || organization == null || !Valid)
{
return false;
}
return organization.Identifier.Equals(DomainHint, StringComparison.InvariantCultureIgnoreCase)
&& organization.Id.Equals(OrganizationId);
}
// Validates deserialized
protected override bool TokenIsValid() =>
Identifier == TokenIdentifier
&& OrganizationId != default
&& !string.IsNullOrWhiteSpace(DomainHint);
}

View File

@ -7,168 +7,167 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Services;
namespace Bit.Core.Models.Business
namespace Bit.Core.Models.Business;
public class UserLicense : ILicense
{
public class UserLicense : ILicense
public UserLicense()
{ }
public UserLicense(User user, SubscriptionInfo subscriptionInfo, ILicensingService licenseService,
int? version = null)
{
public UserLicense()
{ }
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(User user, SubscriptionInfo subscriptionInfo, ILicensingService licenseService,
int? version = null)
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)
{
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)) &&
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 ||
(
!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);
!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.");
}
public byte[] ComputeHash()
return Encoding.UTF8.GetBytes(data);
}
public byte[] ComputeHash()
{
using (var alg = SHA256.Create())
{
using (var alg = SHA256.Create())
{
return alg.ComputeHash(GetDataBytes(true));
}
return alg.ComputeHash(GetDataBytes(true));
}
}
public bool CanUse(User user)
{
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
{
return false;
}
public bool CanUse(User user)
if (Version == 1)
{
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
{
return false;
}
return user.EmailVerified && user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
}
else
{
throw new NotSupportedException($"Version {Version} is not supported.");
}
}
if (Version == 1)
{
return user.EmailVerified && user.Email.Equals(Email, StringComparison.InvariantCultureIgnoreCase);
}
else
{
throw new NotSupportedException($"Version {Version} is not supported.");
}
public bool VerifyData(User user)
{
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
{
return false;
}
public bool VerifyData(User user)
if (Version == 1)
{
if (Issued > DateTime.UtcNow || Expires < DateTime.UtcNow)
{
return false;
}
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 (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.");
}
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!");
}
public bool VerifySignature(X509Certificate2 certificate)
using (var rsa = certificate.GetRSAPrivateKey())
{
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);
}
return rsa.SignData(GetDataBytes(), HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
}
}