1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-23 03:01:23 +01:00

[Reset Password] Organization Key Pair (#1292)

* [Reset Password] Organization Key Pair

* Fixed type in Organization_ReadAbilites sproc

* Fixed broken unit test by making sure premium addon was false

* Updated PublicKey decorator and removed unecessary validation
This commit is contained in:
Vincent Salucci 2021-05-06 14:53:12 -05:00 committed by GitHub
parent cae204cb7c
commit 70ab5b25a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 535 additions and 12 deletions

View File

@ -206,7 +206,8 @@ namespace Bit.Api.Controllers
"which has a policy that prohibits you from being a member of any other organization."); "which has a policy that prohibits you from being a member of any other organization.");
} }
var result = await _organizationService.SignUpAsync(license, user, model.Key, model.CollectionName); var result = await _organizationService.SignUpAsync(license, user, model.Key,
model.CollectionName, model.Keys?.PublicKey, model.Keys?.EncryptedPrivateKey);
return new OrganizationResponseModel(result.Item1); return new OrganizationResponseModel(result.Item1);
} }

View File

@ -20,6 +20,7 @@ namespace Bit.Core.Models.Api.Public
Type = user.Type; Type = user.Type;
AccessAll = user.AccessAll; AccessAll = user.AccessAll;
ExternalId = user.ExternalId; ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
} }
public MemberBaseModel(OrganizationUserUserDetails user) public MemberBaseModel(OrganizationUserUserDetails user)
@ -32,6 +33,7 @@ namespace Bit.Core.Models.Api.Public
Type = user.Type; Type = user.Type;
AccessAll = user.AccessAll; AccessAll = user.AccessAll;
ExternalId = user.ExternalId; ExternalId = user.ExternalId;
ResetPasswordEnrolled = user.ResetPasswordKey != null;
} }
/// <summary> /// <summary>
@ -51,5 +53,10 @@ namespace Bit.Core.Models.Api.Public
/// <example>external_id_123456</example> /// <example>external_id_123456</example>
[StringLength(300)] [StringLength(300)]
public string ExternalId { get; set; } public string ExternalId { get; set; }
/// <summary>
/// Returns <c>true</c> if the member has enrolled in Password Reset assistance within the organization
/// </summary>
[Required]
public bool ResetPasswordEnrolled { get; set; }
} }
} }

View File

@ -10,5 +10,6 @@ namespace Bit.Core.Models.Api
[EncryptedString] [EncryptedString]
[EncryptedStringLength(1000)] [EncryptedStringLength(1000)]
public string CollectionName { get; set; } public string CollectionName { get; set; }
public OrganizationKeysRequestModel Keys { get; set; }
} }
} }

View File

@ -21,6 +21,7 @@ namespace Bit.Core.Models.Api
public PlanType PlanType { get; set; } public PlanType PlanType { get; set; }
[Required] [Required]
public string Key { get; set; } public string Key { get; set; }
public OrganizationKeysRequestModel Keys { get; set; }
public PaymentMethodType? PaymentMethodType { get; set; } public PaymentMethodType? PaymentMethodType { get; set; }
public string PaymentToken { get; set; } public string PaymentToken { get; set; }
[Range(0, double.MaxValue)] [Range(0, double.MaxValue)]
@ -42,7 +43,7 @@ namespace Bit.Core.Models.Api
public virtual OrganizationSignup ToOrganizationSignup(User user) public virtual OrganizationSignup ToOrganizationSignup(User user)
{ {
return new OrganizationSignup var orgSignup = new OrganizationSignup
{ {
Owner = user, Owner = user,
OwnerKey = Key, OwnerKey = Key,
@ -67,6 +68,10 @@ namespace Bit.Core.Models.Api
BillingAddressCountry = BillingAddressCountry, BillingAddressCountry = BillingAddressCountry,
}, },
}; };
Keys?.ToOrganizationSignup(orgSignup);
return orgSignup;
} }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)

View File

@ -0,0 +1,59 @@
using Bit.Core.Models.Table;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Models.Business;
namespace Bit.Core.Models.Api
{
public class OrganizationKeysRequestModel
{
[Required]
public string PublicKey { get; set; }
[Required]
public string EncryptedPrivateKey { get; set; }
public OrganizationSignup ToOrganizationSignup(OrganizationSignup existingSignup)
{
if (string.IsNullOrWhiteSpace(existingSignup.PublicKey))
{
existingSignup.PublicKey = PublicKey;
}
if (string.IsNullOrWhiteSpace(existingSignup.PrivateKey))
{
existingSignup.PrivateKey = EncryptedPrivateKey;
}
return existingSignup;
}
public OrganizationUpgrade ToOrganizationUpgrade(OrganizationUpgrade existingUpgrade)
{
if (string.IsNullOrWhiteSpace(existingUpgrade.PublicKey))
{
existingUpgrade.PublicKey = PublicKey;
}
if (string.IsNullOrWhiteSpace(existingUpgrade.PrivateKey))
{
existingUpgrade.PrivateKey = EncryptedPrivateKey;
}
return existingUpgrade;
}
public Organization ToOrganization(Organization existingOrg)
{
if (string.IsNullOrWhiteSpace(existingOrg.PublicKey))
{
existingOrg.PublicKey = PublicKey;
}
if (string.IsNullOrWhiteSpace(existingOrg.PrivateKey))
{
existingOrg.PrivateKey = EncryptedPrivateKey;
}
return existingOrg;
}
}
}

View File

@ -18,8 +18,8 @@ namespace Bit.Core.Models.Api
[Required] [Required]
[StringLength(256)] [StringLength(256)]
public string BillingEmail { get; set; } public string BillingEmail { get; set; }
public Permissions Permissions { get; set; } public Permissions Permissions { get; set; }
public OrganizationKeysRequestModel Keys { get; set; }
public virtual Organization ToOrganization(Organization existingOrganization, GlobalSettings globalSettings) public virtual Organization ToOrganization(Organization existingOrganization, GlobalSettings globalSettings)
{ {
@ -31,6 +31,7 @@ namespace Bit.Core.Models.Api
existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim(); existingOrganization.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
} }
existingOrganization.Identifier = Identifier; existingOrganization.Identifier = Identifier;
Keys?.ToOrganization(existingOrganization);
return existingOrganization; return existingOrganization;
} }
} }

View File

@ -16,10 +16,11 @@ namespace Bit.Core.Models.Api
public bool PremiumAccessAddon { get; set; } public bool PremiumAccessAddon { get; set; }
public string BillingAddressCountry { get; set; } public string BillingAddressCountry { get; set; }
public string BillingAddressPostalCode { get; set; } public string BillingAddressPostalCode { get; set; }
public OrganizationKeysRequestModel Keys { get; set; }
public OrganizationUpgrade ToOrganizationUpgrade() public OrganizationUpgrade ToOrganizationUpgrade()
{ {
return new OrganizationUpgrade var orgUpgrade = new OrganizationUpgrade
{ {
AdditionalSeats = AdditionalSeats, AdditionalSeats = AdditionalSeats,
AdditionalStorageGb = AdditionalStorageGb.GetValueOrDefault(), AdditionalStorageGb = AdditionalStorageGb.GetValueOrDefault(),
@ -32,6 +33,10 @@ namespace Bit.Core.Models.Api
BillingAddressPostalCode = BillingAddressPostalCode BillingAddressPostalCode = BillingAddressPostalCode
} }
}; };
Keys.ToOrganizationUpgrade(orgUpgrade);
return orgUpgrade;
} }
} }
} }

View File

@ -40,8 +40,10 @@ namespace Bit.Core.Models.Api
UseTotp = organization.UseTotp; UseTotp = organization.UseTotp;
Use2fa = organization.Use2fa; Use2fa = organization.Use2fa;
UseApi = organization.UseApi; UseApi = organization.UseApi;
UseResetPassword = organization.UseResetPassword;
UsersGetPremium = organization.UsersGetPremium; UsersGetPremium = organization.UsersGetPremium;
SelfHost = organization.SelfHost; SelfHost = organization.SelfHost;
HasPublicAndPrivateKeys = organization.PublicKey != null && organization.PrivateKey != null;
} }
public string Id { get; set; } public string Id { get; set; }
@ -67,8 +69,10 @@ namespace Bit.Core.Models.Api
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi { get; set; } public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public bool HasPublicAndPrivateKeys { get; set; }
} }
public class OrganizationSubscriptionResponseModel : OrganizationResponseModel public class OrganizationSubscriptionResponseModel : OrganizationResponseModel

View File

@ -39,6 +39,7 @@ namespace Bit.Core.Models.Api
HasTotp = plan.HasTotp; HasTotp = plan.HasTotp;
Has2fa = plan.Has2fa; Has2fa = plan.Has2fa;
HasSso = plan.HasSso; HasSso = plan.HasSso;
HasResetPassword = plan.HasResetPassword;
UsersGetPremium = plan.UsersGetPremium; UsersGetPremium = plan.UsersGetPremium;
UpgradeSortOrder = plan.UpgradeSortOrder; UpgradeSortOrder = plan.UpgradeSortOrder;
DisplaySortOrder = plan.DisplaySortOrder; DisplaySortOrder = plan.DisplaySortOrder;
@ -81,6 +82,7 @@ namespace Bit.Core.Models.Api
public bool Has2fa { get; set; } public bool Has2fa { get; set; }
public bool HasApi { get; set; } public bool HasApi { get; set; }
public bool HasSso { get; set; } public bool HasSso { get; set; }
public bool HasResetPassword { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public int UpgradeSortOrder { get; set; } public int UpgradeSortOrder { get; set; }

View File

@ -18,12 +18,14 @@ namespace Bit.Core.Models.Api
UseTotp = organization.UseTotp; UseTotp = organization.UseTotp;
Use2fa = organization.Use2fa; Use2fa = organization.Use2fa;
UseApi = organization.UseApi; UseApi = organization.UseApi;
UseResetPassword = organization.UseResetPassword;
UsersGetPremium = organization.UsersGetPremium; UsersGetPremium = organization.UsersGetPremium;
SelfHost = organization.SelfHost; SelfHost = organization.SelfHost;
Seats = organization.Seats; Seats = organization.Seats;
MaxCollections = organization.MaxCollections; MaxCollections = organization.MaxCollections;
MaxStorageGb = organization.MaxStorageGb; MaxStorageGb = organization.MaxStorageGb;
Key = organization.Key; Key = organization.Key;
HasPublicAndPrivateKeys = organization.PublicKey != null && organization.PrivateKey != null;
Status = organization.Status; Status = organization.Status;
Type = organization.Type; Type = organization.Type;
Enabled = organization.Enabled; Enabled = organization.Enabled;
@ -44,6 +46,7 @@ namespace Bit.Core.Models.Api
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi { get; set; } public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed public bool UseBusinessPortal => UsePolicies || UseSso; // TODO add events if needed
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
@ -59,5 +62,6 @@ namespace Bit.Core.Models.Api
public Permissions Permissions { get; set; } public Permissions Permissions { get; set; }
public bool ResetPasswordEnrolled { get; set; } public bool ResetPasswordEnrolled { get; set; }
public string UserId { get; set; } public string UserId { get; set; }
public bool HasPublicAndPrivateKeys { get; set; }
} }
} }

View File

@ -20,7 +20,7 @@ namespace Bit.Core.Models.Business
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId, public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
ILicensingService licenseService, int? version = null) ILicensingService licenseService, int? version = null)
{ {
Version = version.GetValueOrDefault(6); // TODO: bump to version 7 Version = version.GetValueOrDefault(7); // TODO: bump to version 8
LicenseKey = org.LicenseKey; LicenseKey = org.LicenseKey;
InstallationId = installationId; InstallationId = installationId;
Id = org.Id; Id = org.Id;
@ -40,6 +40,7 @@ namespace Bit.Core.Models.Business
UseTotp = org.UseTotp; UseTotp = org.UseTotp;
Use2fa = org.Use2fa; Use2fa = org.Use2fa;
UseApi = org.UseApi; UseApi = org.UseApi;
UseResetPassword = org.UseResetPassword;
MaxStorageGb = org.MaxStorageGb; MaxStorageGb = org.MaxStorageGb;
SelfHost = org.SelfHost; SelfHost = org.SelfHost;
UsersGetPremium = org.UsersGetPremium; UsersGetPremium = org.UsersGetPremium;
@ -109,6 +110,7 @@ namespace Bit.Core.Models.Business
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi { get; set; } public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public short? MaxStorageGb { get; set; } public short? MaxStorageGb { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
@ -125,7 +127,7 @@ namespace Bit.Core.Models.Business
public byte[] GetDataBytes(bool forHash = false) public byte[] GetDataBytes(bool forHash = false)
{ {
string data = null; string data = null;
if (Version >= 1 && Version <= 7) if (Version >= 1 && Version <= 8)
{ {
var props = typeof(OrganizationLicense) var props = typeof(OrganizationLicense)
.GetProperties(BindingFlags.Public | BindingFlags.Instance) .GetProperties(BindingFlags.Public | BindingFlags.Instance)
@ -144,6 +146,8 @@ namespace Bit.Core.Models.Business
(Version >= 6 || !p.Name.Equals(nameof(UsePolicies))) && (Version >= 6 || !p.Name.Equals(nameof(UsePolicies))) &&
// UseSso was added in Version 7 // UseSso was added in Version 7
(Version >= 7 || !p.Name.Equals(nameof(UseSso))) && (Version >= 7 || !p.Name.Equals(nameof(UseSso))) &&
// UseResetPassword was added in Version 8
(Version >= 8 || !p.Name.Equals(nameof(UseResetPassword))) &&
( (
!forHash || !forHash ||
( (
@ -180,7 +184,7 @@ namespace Bit.Core.Models.Business
return false; return false;
} }
if (Version >= 1 && Version <= 7) if (Version >= 1 && Version <= 8)
{ {
return InstallationId == globalSettings.Installation.Id && SelfHost; return InstallationId == globalSettings.Installation.Id && SelfHost;
} }
@ -197,7 +201,7 @@ namespace Bit.Core.Models.Business
return false; return false;
} }
if (Version >= 1 && Version <= 7) if (Version >= 1 && Version <= 8)
{ {
var valid = var valid =
globalSettings.Installation.Id == InstallationId && globalSettings.Installation.Id == InstallationId &&
@ -242,6 +246,11 @@ namespace Bit.Core.Models.Business
valid = organization.UseSso == UseSso; valid = organization.UseSso == UseSso;
} }
if (valid && Version >= 8)
{
valid = organization.UseResetPassword == UseResetPassword;
}
return valid; return valid;
} }
else else

View File

@ -10,5 +10,7 @@ namespace Bit.Core.Models.Business
public short AdditionalStorageGb { get; set; } public short AdditionalStorageGb { get; set; }
public bool PremiumAccessAddon { get; set; } public bool PremiumAccessAddon { get; set; }
public TaxInfo TaxInfo { get; set; } public TaxInfo TaxInfo { get; set; }
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
} }
} }

View File

@ -17,6 +17,7 @@ namespace Bit.Core.Models.Data
UsersGetPremium = organization.UsersGetPremium; UsersGetPremium = organization.UsersGetPremium;
Enabled = organization.Enabled; Enabled = organization.Enabled;
UseSso = organization.UseSso; UseSso = organization.UseSso;
UseResetPassword = organization.UseResetPassword;
} }
public Guid Id { get; set; } public Guid Id { get; set; }
@ -26,5 +27,6 @@ namespace Bit.Core.Models.Data
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public bool Enabled { get; set; } public bool Enabled { get; set; }
public bool UseSso { get; set; } public bool UseSso { get; set; }
public bool UseResetPassword { get; set; }
} }
} }

View File

@ -15,6 +15,7 @@ namespace Bit.Core.Models.Data
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi{ get; set; } public bool UseApi{ get; set; }
public bool UseResetPassword { get; set; }
public bool UseBusinessPortal => UsePolicies || UseSso; public bool UseBusinessPortal => UsePolicies || UseSso;
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
@ -29,5 +30,7 @@ namespace Bit.Core.Models.Data
public string Identifier { get; set; } public string Identifier { get; set; }
public string Permissions { get; set; } public string Permissions { get; set; }
public string ResetPasswordKey { get; set; } public string ResetPasswordKey { get; set; }
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
} }
} }

View File

@ -32,6 +32,7 @@ namespace Bit.Core.Models.StaticStore
public bool Has2fa { get; set; } public bool Has2fa { get; set; }
public bool HasApi { get; set; } public bool HasApi { get; set; }
public bool HasSso { get; set; } public bool HasSso { get; set; }
public bool HasResetPassword { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public int UpgradeSortOrder { get; set; } public int UpgradeSortOrder { get; set; }

View File

@ -33,6 +33,7 @@ namespace Bit.Core.Models.Table
public bool UseTotp { get; set; } public bool UseTotp { get; set; }
public bool Use2fa { get; set; } public bool Use2fa { get; set; }
public bool UseApi { get; set; } public bool UseApi { get; set; }
public bool UseResetPassword { get; set; }
public bool SelfHost { get; set; } public bool SelfHost { get; set; }
public bool UsersGetPremium { get; set; } public bool UsersGetPremium { get; set; }
public long? Storage { get; set; } public long? Storage { get; set; }
@ -44,6 +45,8 @@ namespace Bit.Core.Models.Table
public bool Enabled { get; set; } = true; public bool Enabled { get; set; } = true;
public string LicenseKey { get; set; } public string LicenseKey { get; set; }
public string ApiKey { get; set; } public string ApiKey { get; set; }
public string PublicKey { get; set; }
public string PrivateKey { get; set; }
public string TwoFactorProviders { get; set; } public string TwoFactorProviders { get; set; }
public DateTime? ExpirationDate { get; set; } public DateTime? ExpirationDate { get; set; }
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;

View File

@ -20,7 +20,7 @@ namespace Bit.Core.Services
Task VerifyBankAsync(Guid organizationId, int amount1, int amount2); Task VerifyBankAsync(Guid organizationId, int amount1, int amount2);
Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup organizationSignup); Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationSignup organizationSignup);
Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationLicense license, User owner, Task<Tuple<Organization, OrganizationUser>> SignUpAsync(OrganizationLicense license, User owner,
string ownerKey, string collectionName); string ownerKey, string collectionName, string publicKey, string privateKey);
Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license); Task UpdateLicenseAsync(Guid organizationId, OrganizationLicense license);
Task DeleteAsync(Organization organization); Task DeleteAsync(Organization organization);
Task EnableAsync(Guid organizationId, DateTime? expirationDate); Task EnableAsync(Guid organizationId, DateTime? expirationDate);

View File

@ -238,6 +238,8 @@ namespace Bit.Core.Services
} }
} }
// TODO Reset Password - Throw error if policy enabled and new pland doesn't allow
// TODO: Check storage? // TODO: Check storage?
string paymentIntentClientSecret = null; string paymentIntentClientSecret = null;
@ -275,10 +277,13 @@ namespace Bit.Core.Services
organization.Use2fa = newPlan.Has2fa; organization.Use2fa = newPlan.Has2fa;
organization.UseApi = newPlan.HasApi; organization.UseApi = newPlan.HasApi;
organization.UseSso = newPlan.HasSso; organization.UseSso = newPlan.HasSso;
organization.UseResetPassword = newPlan.HasResetPassword;
organization.SelfHost = newPlan.HasSelfHost; organization.SelfHost = newPlan.HasSelfHost;
organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon; organization.UsersGetPremium = newPlan.UsersGetPremium || upgrade.PremiumAccessAddon;
organization.Plan = newPlan.Name; organization.Plan = newPlan.Name;
organization.Enabled = success; organization.Enabled = success;
organization.PublicKey = upgrade.PublicKey;
organization.PrivateKey = upgrade.PrivateKey;
await ReplaceAndUpdateCache(organization); await ReplaceAndUpdateCache(organization);
if (success) if (success)
{ {
@ -564,6 +569,7 @@ namespace Bit.Core.Services
UseTotp = plan.HasTotp, UseTotp = plan.HasTotp,
Use2fa = plan.Has2fa, Use2fa = plan.Has2fa,
UseApi = plan.HasApi, UseApi = plan.HasApi,
UseResetPassword = plan.HasResetPassword,
SelfHost = plan.HasSelfHost, SelfHost = plan.HasSelfHost,
UsersGetPremium = plan.UsersGetPremium || signup.PremiumAccessAddon, UsersGetPremium = plan.UsersGetPremium || signup.PremiumAccessAddon,
Plan = plan.Name, Plan = plan.Name,
@ -572,6 +578,8 @@ namespace Bit.Core.Services
Enabled = true, Enabled = true,
LicenseKey = CoreHelpers.SecureRandomString(20), LicenseKey = CoreHelpers.SecureRandomString(20),
ApiKey = CoreHelpers.SecureRandomString(30), ApiKey = CoreHelpers.SecureRandomString(30),
PublicKey = signup.PublicKey,
PrivateKey = signup.PrivateKey,
CreationDate = DateTime.UtcNow, CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow, RevisionDate = DateTime.UtcNow,
}; };
@ -605,7 +613,8 @@ namespace Bit.Core.Services
} }
public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync( public async Task<Tuple<Organization, OrganizationUser>> SignUpAsync(
OrganizationLicense license, User owner, string ownerKey, string collectionName) OrganizationLicense license, User owner, string ownerKey, string collectionName, string publicKey,
string privateKey)
{ {
if (license == null || !_licensingService.VerifyLicense(license)) if (license == null || !_licensingService.VerifyLicense(license))
{ {
@ -647,6 +656,7 @@ namespace Bit.Core.Services
UseTotp = license.UseTotp, UseTotp = license.UseTotp,
Use2fa = license.Use2fa, Use2fa = license.Use2fa,
UseApi = license.UseApi, UseApi = license.UseApi,
UseResetPassword = license.UseResetPassword,
Plan = license.Plan, Plan = license.Plan,
SelfHost = license.SelfHost, SelfHost = license.SelfHost,
UsersGetPremium = license.UsersGetPremium, UsersGetPremium = license.UsersGetPremium,
@ -658,6 +668,8 @@ namespace Bit.Core.Services
ExpirationDate = license.Expires, ExpirationDate = license.Expires,
LicenseKey = license.LicenseKey, LicenseKey = license.LicenseKey,
ApiKey = CoreHelpers.SecureRandomString(30), ApiKey = CoreHelpers.SecureRandomString(30),
PublicKey = publicKey,
PrivateKey = privateKey,
CreationDate = DateTime.UtcNow, CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow RevisionDate = DateTime.UtcNow
}; };
@ -813,6 +825,9 @@ namespace Bit.Core.Services
} }
} }
// TODO Reset Password - If the license does not allow reset password, but the organization currently does
// TODO Reset Password - Pull Reset Password policy and make sure its disabled.
var dir = $"{_globalSettings.LicenseDirectory}/organization"; var dir = $"{_globalSettings.LicenseDirectory}/organization";
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);
System.IO.File.WriteAllText($"{dir}/{organization.Id}.json", System.IO.File.WriteAllText($"{dir}/{organization.Id}.json",
@ -832,6 +847,7 @@ namespace Bit.Core.Services
organization.UseApi = license.UseApi; organization.UseApi = license.UseApi;
organization.UsePolicies = license.UsePolicies; organization.UsePolicies = license.UsePolicies;
organization.UseSso = license.UseSso; organization.UseSso = license.UseSso;
organization.UseResetPassword = license.UseResetPassword;
organization.SelfHost = license.SelfHost; organization.SelfHost = license.SelfHost;
organization.UsersGetPremium = license.UsersGetPremium; organization.UsersGetPremium = license.UsersGetPremium;
organization.Plan = license.Plan; organization.Plan = license.Plan;

View File

@ -392,6 +392,7 @@ namespace Bit.Core.Utilities
Has2fa = true, Has2fa = true,
HasApi = true, HasApi = true,
HasSso = true, HasSso = true,
HasResetPassword = true,
UsersGetPremium = true, UsersGetPremium = true,
UpgradeSortOrder = 3, UpgradeSortOrder = 3,
@ -427,6 +428,7 @@ namespace Bit.Core.Utilities
HasApi = true, HasApi = true,
HasSelfHost = true, HasSelfHost = true,
HasSso = true, HasSso = true,
HasResetPassword = true,
UsersGetPremium = true, UsersGetPremium = true,
UpgradeSortOrder = 3, UpgradeSortOrder = 3,

View File

@ -21,6 +21,7 @@
@UseTotp BIT, @UseTotp BIT,
@Use2fa BIT, @Use2fa BIT,
@UseApi BIT, @UseApi BIT,
@UseResetPassword BIT,
@SelfHost BIT, @SelfHost BIT,
@UsersGetPremium BIT, @UsersGetPremium BIT,
@Storage BIGINT, @Storage BIGINT,
@ -32,6 +33,8 @@
@Enabled BIT, @Enabled BIT,
@LicenseKey VARCHAR(100), @LicenseKey VARCHAR(100),
@ApiKey VARCHAR(30), @ApiKey VARCHAR(30),
@PublicKey VARCHAR(MAX),
@PrivateKey VARCHAR(MAX),
@TwoFactorProviders NVARCHAR(MAX), @TwoFactorProviders NVARCHAR(MAX),
@ExpirationDate DATETIME2(7), @ExpirationDate DATETIME2(7),
@CreationDate DATETIME2(7), @CreationDate DATETIME2(7),
@ -64,6 +67,7 @@ BEGIN
[UseTotp], [UseTotp],
[Use2fa], [Use2fa],
[UseApi], [UseApi],
[UseResetPassword],
[SelfHost], [SelfHost],
[UsersGetPremium], [UsersGetPremium],
[Storage], [Storage],
@ -75,6 +79,8 @@ BEGIN
[Enabled], [Enabled],
[LicenseKey], [LicenseKey],
[ApiKey], [ApiKey],
[PublicKey],
[PrivateKey],
[TwoFactorProviders], [TwoFactorProviders],
[ExpirationDate], [ExpirationDate],
[CreationDate], [CreationDate],
@ -104,6 +110,7 @@ BEGIN
@UseTotp, @UseTotp,
@Use2fa, @Use2fa,
@UseApi, @UseApi,
@UseResetPassword,
@SelfHost, @SelfHost,
@UsersGetPremium, @UsersGetPremium,
@Storage, @Storage,
@ -115,6 +122,8 @@ BEGIN
@Enabled, @Enabled,
@LicenseKey, @LicenseKey,
@ApiKey, @ApiKey,
@PublicKey,
@PrivateKey,
@TwoFactorProviders, @TwoFactorProviders,
@ExpirationDate, @ExpirationDate,
@CreationDate, @CreationDate,

View File

@ -15,6 +15,7 @@ BEGIN
END AS [Using2fa], END AS [Using2fa],
[UsersGetPremium], [UsersGetPremium],
[UseSso], [UseSso],
[UseResetPassword],
[Enabled] [Enabled]
FROM FROM
[dbo].[Organization] [dbo].[Organization]

View File

@ -21,6 +21,7 @@
@UseTotp BIT, @UseTotp BIT,
@Use2fa BIT, @Use2fa BIT,
@UseApi BIT, @UseApi BIT,
@UseResetPassword BIT,
@SelfHost BIT, @SelfHost BIT,
@UsersGetPremium BIT, @UsersGetPremium BIT,
@Storage BIGINT, @Storage BIGINT,
@ -32,6 +33,8 @@
@Enabled BIT, @Enabled BIT,
@LicenseKey VARCHAR(100), @LicenseKey VARCHAR(100),
@ApiKey VARCHAR(30), @ApiKey VARCHAR(30),
@PublicKey VARCHAR(MAX),
@PrivateKey VARCHAR(MAX),
@TwoFactorProviders NVARCHAR(MAX), @TwoFactorProviders NVARCHAR(MAX),
@ExpirationDate DATETIME2(7), @ExpirationDate DATETIME2(7),
@CreationDate DATETIME2(7), @CreationDate DATETIME2(7),
@ -64,6 +67,7 @@ BEGIN
[UseTotp] = @UseTotp, [UseTotp] = @UseTotp,
[Use2fa] = @Use2fa, [Use2fa] = @Use2fa,
[UseApi] = @UseApi, [UseApi] = @UseApi,
[UseResetPassword] = @UseResetPassword,
[SelfHost] = @SelfHost, [SelfHost] = @SelfHost,
[UsersGetPremium] = @UsersGetPremium, [UsersGetPremium] = @UsersGetPremium,
[Storage] = @Storage, [Storage] = @Storage,
@ -75,6 +79,8 @@ BEGIN
[Enabled] = @Enabled, [Enabled] = @Enabled,
[LicenseKey] = @LicenseKey, [LicenseKey] = @LicenseKey,
[ApiKey] = @ApiKey, [ApiKey] = @ApiKey,
[PublicKey] = @PublicKey,
[PrivateKey] = @PrivateKey,
[TwoFactorProviders] = @TwoFactorProviders, [TwoFactorProviders] = @TwoFactorProviders,
[ExpirationDate] = @ExpirationDate, [ExpirationDate] = @ExpirationDate,
[CreationDate] = @CreationDate, [CreationDate] = @CreationDate,

View File

@ -21,6 +21,7 @@
[UseTotp] BIT NOT NULL, [UseTotp] BIT NOT NULL,
[Use2fa] BIT NOT NULL, [Use2fa] BIT NOT NULL,
[UseApi] BIT NOT NULL, [UseApi] BIT NOT NULL,
[UseResetPassword] BIT NOT NULL,
[SelfHost] BIT NOT NULL, [SelfHost] BIT NOT NULL,
[UsersGetPremium] BIT NOT NULL, [UsersGetPremium] BIT NOT NULL,
[Storage] BIGINT NULL, [Storage] BIGINT NULL,
@ -32,6 +33,8 @@
[Enabled] BIT NOT NULL, [Enabled] BIT NOT NULL,
[LicenseKey] VARCHAR (100) NULL, [LicenseKey] VARCHAR (100) NULL,
[ApiKey] VARCHAR (30) NOT NULL, [ApiKey] VARCHAR (30) NOT NULL,
[PublicKey] VARCHAR (MAX) NULL,
[PrivateKey] VARCHAR (MAX) NULL,
[TwoFactorProviders] NVARCHAR (MAX) NULL, [TwoFactorProviders] NVARCHAR (MAX) NULL,
[ExpirationDate] DATETIME2 (7) NULL, [ExpirationDate] DATETIME2 (7) NULL,
[CreationDate] DATETIME2 (7) NOT NULL, [CreationDate] DATETIME2 (7) NOT NULL,

View File

@ -13,6 +13,7 @@ SELECT
O.[UseTotp], O.[UseTotp],
O.[Use2fa], O.[Use2fa],
O.[UseApi], O.[UseApi],
O.[UseResetPassword],
O.[SelfHost], O.[SelfHost],
O.[UsersGetPremium], O.[UsersGetPremium],
O.[Seats], O.[Seats],
@ -21,6 +22,8 @@ SELECT
O.[Identifier], O.[Identifier],
OU.[Key], OU.[Key],
OU.[ResetPasswordKey], OU.[ResetPasswordKey],
O.[PublicKey],
O.[PrivateKey],
OU.[Status], OU.[Status],
OU.[Type], OU.[Type],
SU.[ExternalId] SsoExternalId, SU.[ExternalId] SsoExternalId,

View File

@ -36,9 +36,11 @@ namespace Bit.Core.Test.AutoFixture.OrganizationFixtures
.With(o => o.PlanType, PlanType.Free)); .With(o => o.PlanType, PlanType.Free));
var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom }; var plansToIgnore = new List<PlanType> { PlanType.Free, PlanType.Custom };
var validPlans = StaticStore.Plans.Where(p => !plansToIgnore.Contains(p.Type) && !p.Disabled).Select(p => p.Type).ToList(); var selectedPlan = StaticStore.Plans.Last(p => !plansToIgnore.Contains(p.Type) && !p.Disabled);
fixture.Customize<OrganizationUpgrade>(composer => composer fixture.Customize<OrganizationUpgrade>(composer => composer
.With(ou => ou.Plan, validPlans.Last())); .With(ou => ou.Plan, selectedPlan.Type)
.With(ou => ou.PremiumAccessAddon, selectedPlan.HasPremiumAccessOption));
fixture.Customize<Organization>(composer => composer fixture.Customize<Organization>(composer => composer
.Without(o => o.GatewaySubscriptionId)); .Without(o => o.GatewaySubscriptionId));
} }

View File

@ -0,0 +1,372 @@
-- Table: Organization (UseResetPassword)
IF COL_LENGTH('[dbo].[Organization]', 'UseResetPassword') IS NULL
BEGIN
ALTER TABLE
[dbo].[Organization]
ADD
[UseResetPassword] BIT NULL
END
GO
UPDATE
[dbo].[Organization]
SET
[UseResetPassword] = (CASE WHEN [PlanType] = 10 OR [PlanType] = 11 THEN 1 ELSE 0 END)
GO
ALTER TABLE
[dbo].[Organization]
ALTER COLUMN
[UseResetPassword] BIT NOT NULL
GO
-- Table: Organization (PublicKey)
IF COL_LENGTH('[dbo].[Organization]', 'PublicKey') IS NULL
BEGIN
ALTER TABLE
[dbo].[Organization]
ADD
[PublicKey] VARCHAR (MAX) NULL
END
GO
-- Table: Organization (PrivateKey)
IF COL_LENGTH('[dbo].[Organization]', 'PrivateKey') IS NULL
BEGIN
ALTER TABLE
[dbo].[Organization]
ADD
[PrivateKey] VARCHAR (MAX) NULL
END
GO
-- View: Organization
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationView')
BEGIN
DROP VIEW [dbo].[OrganizationView]
END
GO
CREATE VIEW [dbo].[OrganizationView]
AS
SELECT
*
FROM
[dbo].[Organization]
GO
-- View: OrganizationUserOrganizationDetails
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationUserOrganizationDetailsView')
BEGIN
DROP VIEW [dbo].[OrganizationUserOrganizationDetailsView]
END
GO
CREATE VIEW [dbo].[OrganizationUserOrganizationDetailsView]
AS
SELECT
OU.[UserId],
OU.[OrganizationId],
O.[Name],
O.[Enabled],
O.[UsePolicies],
O.[UseSso],
O.[UseGroups],
O.[UseDirectory],
O.[UseEvents],
O.[UseTotp],
O.[Use2fa],
O.[UseApi],
O.[UseResetPassword],
O.[SelfHost],
O.[UsersGetPremium],
O.[Seats],
O.[MaxCollections],
O.[MaxStorageGb],
O.[Identifier],
OU.[Key],
OU.[ResetPasswordKey],
O.[PublicKey],
O.[PrivateKey],
OU.[Status],
OU.[Type],
SU.[ExternalId] SsoExternalId,
OU.[Permissions]
FROM
[dbo].[OrganizationUser] OU
INNER JOIN
[dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
LEFT JOIN
[dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
GO
-- Stored Procedure: Organization_Create
IF OBJECT_ID('[dbo].[Organization_Create]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Organization_Create]
END
GO
CREATE PROCEDURE [dbo].[Organization_Create]
@Id UNIQUEIDENTIFIER,
@Identifier NVARCHAR(50),
@Name NVARCHAR(50),
@BusinessName NVARCHAR(50),
@BusinessAddress1 NVARCHAR(50),
@BusinessAddress2 NVARCHAR(50),
@BusinessAddress3 NVARCHAR(50),
@BusinessCountry VARCHAR(2),
@BusinessTaxNumber NVARCHAR(30),
@BillingEmail NVARCHAR(256),
@Plan NVARCHAR(50),
@PlanType TINYINT,
@Seats SMALLINT,
@MaxCollections SMALLINT,
@UsePolicies BIT,
@UseSso BIT,
@UseGroups BIT,
@UseDirectory BIT,
@UseEvents BIT,
@UseTotp BIT,
@Use2fa BIT,
@UseApi BIT,
@UseResetPassword BIT,
@SelfHost BIT,
@UsersGetPremium BIT,
@Storage BIGINT,
@MaxStorageGb SMALLINT,
@Gateway TINYINT,
@GatewayCustomerId VARCHAR(50),
@GatewaySubscriptionId VARCHAR(50),
@ReferenceData VARCHAR(MAX),
@Enabled BIT,
@LicenseKey VARCHAR(100),
@ApiKey VARCHAR(30),
@PublicKey VARCHAR(MAX),
@PrivateKey VARCHAR(MAX),
@TwoFactorProviders NVARCHAR(MAX),
@ExpirationDate DATETIME2(7),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[Organization]
(
[Id],
[Identifier],
[Name],
[BusinessName],
[BusinessAddress1],
[BusinessAddress2],
[BusinessAddress3],
[BusinessCountry],
[BusinessTaxNumber],
[BillingEmail],
[Plan],
[PlanType],
[Seats],
[MaxCollections],
[UsePolicies],
[UseSso],
[UseGroups],
[UseDirectory],
[UseEvents],
[UseTotp],
[Use2fa],
[UseApi],
[UseResetPassword],
[SelfHost],
[UsersGetPremium],
[Storage],
[MaxStorageGb],
[Gateway],
[GatewayCustomerId],
[GatewaySubscriptionId],
[ReferenceData],
[Enabled],
[LicenseKey],
[ApiKey],
[PublicKey],
[PrivateKey],
[TwoFactorProviders],
[ExpirationDate],
[CreationDate],
[RevisionDate]
)
VALUES
(
@Id,
@Identifier,
@Name,
@BusinessName,
@BusinessAddress1,
@BusinessAddress2,
@BusinessAddress3,
@BusinessCountry,
@BusinessTaxNumber,
@BillingEmail,
@Plan,
@PlanType,
@Seats,
@MaxCollections,
@UsePolicies,
@UseSso,
@UseGroups,
@UseDirectory,
@UseEvents,
@UseTotp,
@Use2fa,
@UseApi,
@UseResetPassword,
@SelfHost,
@UsersGetPremium,
@Storage,
@MaxStorageGb,
@Gateway,
@GatewayCustomerId,
@GatewaySubscriptionId,
@ReferenceData,
@Enabled,
@LicenseKey,
@ApiKey,
@PublicKey,
@PrivateKey,
@TwoFactorProviders,
@ExpirationDate,
@CreationDate,
@RevisionDate
)
END
GO
-- Stored Procedure: Organization_Update
IF OBJECT_ID('[dbo].[Organization_Update]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Organization_Update]
END
GO
CREATE PROCEDURE [dbo].[Organization_Update]
@Id UNIQUEIDENTIFIER,
@Identifier NVARCHAR(50),
@Name NVARCHAR(50),
@BusinessName NVARCHAR(50),
@BusinessAddress1 NVARCHAR(50),
@BusinessAddress2 NVARCHAR(50),
@BusinessAddress3 NVARCHAR(50),
@BusinessCountry VARCHAR(2),
@BusinessTaxNumber NVARCHAR(30),
@BillingEmail NVARCHAR(256),
@Plan NVARCHAR(50),
@PlanType TINYINT,
@Seats SMALLINT,
@MaxCollections SMALLINT,
@UsePolicies BIT,
@UseSso BIT,
@UseGroups BIT,
@UseDirectory BIT,
@UseEvents BIT,
@UseTotp BIT,
@Use2fa BIT,
@UseApi BIT,
@UseResetPassword BIT,
@SelfHost BIT,
@UsersGetPremium BIT,
@Storage BIGINT,
@MaxStorageGb SMALLINT,
@Gateway TINYINT,
@GatewayCustomerId VARCHAR(50),
@GatewaySubscriptionId VARCHAR(50),
@ReferenceData VARCHAR(MAX),
@Enabled BIT,
@LicenseKey VARCHAR(100),
@ApiKey VARCHAR(30),
@PublicKey VARCHAR(MAX),
@PrivateKey VARCHAR(MAX),
@TwoFactorProviders NVARCHAR(MAX),
@ExpirationDate DATETIME2(7),
@CreationDate DATETIME2(7),
@RevisionDate DATETIME2(7)
AS
BEGIN
SET NOCOUNT ON
UPDATE
[dbo].[Organization]
SET
[Identifier] = @Identifier,
[Name] = @Name,
[BusinessName] = @BusinessName,
[BusinessAddress1] = @BusinessAddress1,
[BusinessAddress2] = @BusinessAddress2,
[BusinessAddress3] = @BusinessAddress3,
[BusinessCountry] = @BusinessCountry,
[BusinessTaxNumber] = @BusinessTaxNumber,
[BillingEmail] = @BillingEmail,
[Plan] = @Plan,
[PlanType] = @PlanType,
[Seats] = @Seats,
[MaxCollections] = @MaxCollections,
[UsePolicies] = @UsePolicies,
[UseSso] = @UseSso,
[UseGroups] = @UseGroups,
[UseDirectory] = @UseDirectory,
[UseEvents] = @UseEvents,
[UseTotp] = @UseTotp,
[Use2fa] = @Use2fa,
[UseApi] = @UseApi,
[UseResetPassword] = @UseResetPassword,
[SelfHost] = @SelfHost,
[UsersGetPremium] = @UsersGetPremium,
[Storage] = @Storage,
[MaxStorageGb] = @MaxStorageGb,
[Gateway] = @Gateway,
[GatewayCustomerId] = @GatewayCustomerId,
[GatewaySubscriptionId] = @GatewaySubscriptionId,
[ReferenceData] = @ReferenceData,
[Enabled] = @Enabled,
[LicenseKey] = @LicenseKey,
[ApiKey] = @ApiKey,
[PublicKey] = @PublicKey,
[PrivateKey] = @PrivateKey,
[TwoFactorProviders] = @TwoFactorProviders,
[ExpirationDate] = @ExpirationDate,
[CreationDate] = @CreationDate,
[RevisionDate] = @RevisionDate
WHERE
[Id] = @Id
END
GO
-- Stored Procedure: Organization_ReadAbilities
IF OBJECT_ID('[dbo].[Organization_ReadAbilities]') IS NOT NULL
BEGIN
DROP PROCEDURE [dbo].[Organization_ReadAbilities]
END
GO
CREATE PROCEDURE [dbo].[Organization_ReadAbilities]
AS
BEGIN
SET NOCOUNT ON
SELECT
[Id],
[UseEvents],
[Use2fa],
CASE
WHEN [Use2fa] = 1 AND [TwoFactorProviders] IS NOT NULL AND [TwoFactorProviders] != '{}' THEN
1
ELSE
0
END AS [Using2fa],
[UsersGetPremium],
[UseSso],
[UseResetPassword],
[Enabled]
FROM
[dbo].[Organization]
END
GO