diff --git a/src/Admin/Models/OrganizationEditModel.cs b/src/Admin/Models/OrganizationEditModel.cs
index baef034634..af3671f3fe 100644
--- a/src/Admin/Models/OrganizationEditModel.cs
+++ b/src/Admin/Models/OrganizationEditModel.cs
@@ -28,6 +28,7 @@ namespace Bit.Admin.Models
Plan = org.Plan;
Seats = org.Seats;
MaxCollections = org.MaxCollections;
+ UsePolicies = org.UsePolicies;
UseGroups = org.UseGroups;
UseDirectory = org.UseDirectory;
UseEvents = org.UseEvents;
@@ -67,6 +68,8 @@ namespace Bit.Admin.Models
public short? Seats { get; set; }
[Display(Name = "Max. Collections")]
public short? MaxCollections { get; set; }
+ [Display(Name = "Policies")]
+ public bool UsePolicies { get; set; }
[Display(Name = "Groups")]
public bool UseGroups { get; set; }
[Display(Name = "Directory")]
@@ -107,6 +110,7 @@ namespace Bit.Admin.Models
existingOrganization.Plan = Plan;
existingOrganization.Seats = Seats;
existingOrganization.MaxCollections = MaxCollections;
+ existingOrganization.UsePolicies = UsePolicies;
existingOrganization.UseGroups = UseGroups;
existingOrganization.UseDirectory = UseDirectory;
existingOrganization.UseEvents = UseEvents;
diff --git a/src/Admin/Views/Organizations/Edit.cshtml b/src/Admin/Views/Organizations/Edit.cshtml
index 988d13aa75..626008bf8e 100644
--- a/src/Admin/Views/Organizations/Edit.cshtml
+++ b/src/Admin/Views/Organizations/Edit.cshtml
@@ -21,6 +21,7 @@
document.getElementById('@(nameof(Model.MaxCollections))').value = '';
document.getElementById('@(nameof(Model.MaxStorageGb))').value = '1';
// Features
+ document.getElementById('@(nameof(Model.UsePolicies))').checked = true;
document.getElementById('@(nameof(Model.UseGroups))').checked = true;
document.getElementById('@(nameof(Model.UseDirectory))').checked = true;
document.getElementById('@(nameof(Model.UseEvents))').checked = true;
@@ -160,6 +161,10 @@
+
+
+
+
diff --git a/src/Core/Models/Api/Response/OrganizationResponseModel.cs b/src/Core/Models/Api/Response/OrganizationResponseModel.cs
index a77c9b972c..6dfd3e86d8 100644
--- a/src/Core/Models/Api/Response/OrganizationResponseModel.cs
+++ b/src/Core/Models/Api/Response/OrganizationResponseModel.cs
@@ -30,6 +30,7 @@ namespace Bit.Core.Models.Api
Seats = organization.Seats;
MaxCollections = organization.MaxCollections;
MaxStorageGb = organization.MaxStorageGb;
+ UsePolicies = organization.UsePolicies;
UseGroups = organization.UseGroups;
UseDirectory = organization.UseDirectory;
UseEvents = organization.UseEvents;
@@ -54,6 +55,7 @@ namespace Bit.Core.Models.Api
public short? Seats { get; set; }
public short? MaxCollections { get; set; }
public short? MaxStorageGb { get; set; }
+ public bool UsePolicies { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs
index 5050b0354c..23d10160bb 100644
--- a/src/Core/Models/Business/OrganizationLicense.cs
+++ b/src/Core/Models/Business/OrganizationLicense.cs
@@ -19,7 +19,7 @@ namespace Bit.Core.Models.Business
public OrganizationLicense(Organization org, SubscriptionInfo subscriptionInfo, Guid installationId,
ILicensingService licenseService)
{
- Version = 5;
+ Version = 5; // TODO: bump to version 6
LicenseKey = org.LicenseKey;
InstallationId = installationId;
Id = org.Id;
@@ -31,6 +31,7 @@ namespace Bit.Core.Models.Business
PlanType = org.PlanType;
Seats = org.Seats;
MaxCollections = org.MaxCollections;
+ UsePolicies = org.UsePolicies;
UseGroups = org.UseGroups;
UseEvents = org.UseEvents;
UseDirectory = org.UseDirectory;
@@ -98,6 +99,7 @@ namespace Bit.Core.Models.Business
public PlanType PlanType { get; set; }
public short? Seats { get; set; }
public short? MaxCollections { get; set; }
+ public bool UsePolicies { get; set; }
public bool UseGroups { get; set; }
public bool UseEvents { get; set; }
public bool UseDirectory { get; set; }
@@ -120,7 +122,7 @@ namespace Bit.Core.Models.Business
public byte[] GetDataBytes(bool forHash = false)
{
string data = null;
- if(Version >= 1 && Version <= 5)
+ if(Version >= 1 && Version <= 6)
{
var props = typeof(OrganizationLicense)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
@@ -135,6 +137,8 @@ namespace Bit.Core.Models.Business
(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))) &&
(
!forHash ||
(
@@ -171,7 +175,7 @@ namespace Bit.Core.Models.Business
return false;
}
- if(Version >= 1 && Version <= 5)
+ if(Version >= 1 && Version <= 6)
{
return InstallationId == globalSettings.Installation.Id && SelfHost;
}
@@ -188,7 +192,7 @@ namespace Bit.Core.Models.Business
return false;
}
- if(Version >= 1 && Version <= 5)
+ if(Version >= 1 && Version <= 6)
{
var valid =
globalSettings.Installation.Id == InstallationId &&
@@ -223,6 +227,11 @@ namespace Bit.Core.Models.Business
valid = organization.UseApi == UseApi;
}
+ if(valid && Version >= 6)
+ {
+ valid = organization.UsePolicies == UsePolicies;
+ }
+
return valid;
}
else
diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs
index 9f5b5e513c..cf0bcfb391 100644
--- a/src/Core/Models/StaticStore/Plan.cs
+++ b/src/Core/Models/StaticStore/Plan.cs
@@ -15,6 +15,7 @@ namespace Bit.Core.Models.StaticStore
public short? MaxAdditionalSeats { get; set; }
public bool CanBuyPremiumAccessAddon { get; set; }
public bool UseGroups { get; set; }
+ public bool UsePolicies { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
public bool UseTotp { get; set; }
diff --git a/src/Core/Models/Table/Organization.cs b/src/Core/Models/Table/Organization.cs
index 01404fa958..be61289213 100644
--- a/src/Core/Models/Table/Organization.cs
+++ b/src/Core/Models/Table/Organization.cs
@@ -24,6 +24,7 @@ namespace Bit.Core.Models.Table
public PlanType PlanType { get; set; }
public short? Seats { get; set; }
public short? MaxCollections { get; set; }
+ public bool UsePolicies { get; set; }
public bool UseGroups { get; set; }
public bool UseDirectory { get; set; }
public bool UseEvents { get; set; }
diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs
index 683b073f6f..c7a759dddf 100644
--- a/src/Core/Services/Implementations/OrganizationService.cs
+++ b/src/Core/Services/Implementations/OrganizationService.cs
@@ -33,6 +33,7 @@ namespace Bit.Core.Services
private readonly IInstallationRepository _installationRepository;
private readonly IApplicationCacheService _applicationCacheService;
private readonly IPaymentService _paymentService;
+ private readonly IPolicyRepository _policyRepository;
private readonly GlobalSettings _globalSettings;
public OrganizationService(
@@ -51,6 +52,7 @@ namespace Bit.Core.Services
IInstallationRepository installationRepository,
IApplicationCacheService applicationCacheService,
IPaymentService paymentService,
+ IPolicyRepository policyRepository,
GlobalSettings globalSettings)
{
_organizationRepository = organizationRepository;
@@ -68,6 +70,7 @@ namespace Bit.Core.Services
_installationRepository = installationRepository;
_applicationCacheService = applicationCacheService;
_paymentService = paymentService;
+ _policyRepository = policyRepository;
_globalSettings = globalSettings;
}
@@ -193,6 +196,16 @@ namespace Bit.Core.Services
}
}
+ if(!newPlan.UsePolicies && organization.UsePolicies)
+ {
+ var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id);
+ if(policies.Any(p => p.Enabled))
+ {
+ throw new BadRequestException($"Your new plan does not allow the policies feature. " +
+ $"Disable your policies.");
+ }
+ }
+
// TODO: Check storage?
string paymentIntentClientSecret = null;
@@ -453,6 +466,7 @@ namespace Bit.Core.Services
MaxCollections = plan.MaxCollections,
MaxStorageGb = !plan.MaxStorageGb.HasValue ?
(short?)null : (short)(plan.MaxStorageGb.Value + signup.AdditionalStorageGb),
+ UsePolicies = plan.UsePolicies,
UseGroups = plan.UseGroups,
UseEvents = plan.UseEvents,
UseDirectory = plan.UseDirectory,
@@ -524,6 +538,7 @@ namespace Bit.Core.Services
Seats = license.Seats,
MaxCollections = license.MaxCollections,
MaxStorageGb = _globalSettings.SelfHosted ? 10240 : license.MaxStorageGb, // 10 TB
+ UsePolicies = license.UsePolicies,
UseGroups = license.UseGroups,
UseDirectory = license.UseDirectory,
UseEvents = license.UseEvents,
@@ -675,6 +690,16 @@ namespace Bit.Core.Services
}
}
+ if(!license.UsePolicies && organization.UsePolicies)
+ {
+ var policies = await _policyRepository.GetManyByOrganizationIdAsync(organization.Id);
+ if(policies.Any(p => p.Enabled))
+ {
+ throw new BadRequestException($"Your organization currently has {policies.Count} enabled " +
+ $"policies. Your new license does not allow for the use of policies. Disable all policies.");
+ }
+ }
+
var dir = $"{_globalSettings.LicenseDirectory}/organization";
Directory.CreateDirectory(dir);
System.IO.File.WriteAllText($"{dir}/{organization.Id}.json",
diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs
index 9fd24f8da6..23b0f0e71f 100644
--- a/src/Core/Utilities/ServiceCollectionExtensions.cs
+++ b/src/Core/Utilities/ServiceCollectionExtensions.cs
@@ -75,6 +75,7 @@ namespace Bit.Core.Utilities
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
+ services.AddSingleton();
}
if(globalSettings.SelfHosted)
@@ -105,6 +106,7 @@ namespace Bit.Core.Utilities
services.AddScoped();
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
services.AddScoped();
services.AddSingleton();
services.AddSingleton();
diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs
index 25fa63fa99..d366cb0948 100644
--- a/src/Core/Utilities/StaticStore.cs
+++ b/src/Core/Utilities/StaticStore.cs
@@ -164,6 +164,7 @@ namespace Bit.Core.Utilities
StripeStoragePlanId = "storage-gb-monthly",
UpgradeSortOrder = 3,
TrialPeriodDays = 7,
+ UsePolicies = true,
UseGroups = true,
UseDirectory = true,
UseEvents = true,
@@ -187,6 +188,7 @@ namespace Bit.Core.Utilities
StripeStoragePlanId = "storage-gb-annually",
UpgradeSortOrder = 3,
TrialPeriodDays = 7,
+ UsePolicies = true,
UseGroups = true,
UseDirectory = true,
UseEvents = true,
diff --git a/src/Sql/dbo/Stored Procedures/Organization_Create.sql b/src/Sql/dbo/Stored Procedures/Organization_Create.sql
index 4ddb006234..965d23e2d3 100644
--- a/src/Sql/dbo/Stored Procedures/Organization_Create.sql
+++ b/src/Sql/dbo/Stored Procedures/Organization_Create.sql
@@ -12,6 +12,7 @@
@PlanType TINYINT,
@Seats SMALLINT,
@MaxCollections SMALLINT,
+ @UsePolicies BIT,
@UseGroups BIT,
@UseDirectory BIT,
@UseEvents BIT,
@@ -51,6 +52,7 @@ BEGIN
[PlanType],
[Seats],
[MaxCollections],
+ [UsePolicies],
[UseGroups],
[UseDirectory],
[UseEvents],
@@ -87,6 +89,7 @@ BEGIN
@PlanType,
@Seats,
@MaxCollections,
+ @UsePolicies,
@UseGroups,
@UseDirectory,
@UseEvents,
diff --git a/src/Sql/dbo/Stored Procedures/Organization_Update.sql b/src/Sql/dbo/Stored Procedures/Organization_Update.sql
index 0d9f64a655..1c7c79a2fe 100644
--- a/src/Sql/dbo/Stored Procedures/Organization_Update.sql
+++ b/src/Sql/dbo/Stored Procedures/Organization_Update.sql
@@ -12,6 +12,7 @@
@PlanType TINYINT,
@Seats SMALLINT,
@MaxCollections SMALLINT,
+ @UsePolicies BIT,
@UseGroups BIT,
@UseDirectory BIT,
@UseEvents BIT,
@@ -51,6 +52,7 @@ BEGIN
[PlanType] = @PlanType,
[Seats] = @Seats,
[MaxCollections] = @MaxCollections,
+ [UsePolicies] = @UsePolicies,
[UseGroups] = @UseGroups,
[UseDirectory] = @UseDirectory,
[UseEvents] = @UseEvents,
diff --git a/src/Sql/dbo/Tables/Organization.sql b/src/Sql/dbo/Tables/Organization.sql
index 72ff576ff1..226335de70 100644
--- a/src/Sql/dbo/Tables/Organization.sql
+++ b/src/Sql/dbo/Tables/Organization.sql
@@ -12,6 +12,7 @@
[PlanType] TINYINT NOT NULL,
[Seats] SMALLINT NULL,
[MaxCollections] SMALLINT NULL,
+ [UsePolicies] BIT NOT NULL,
[UseGroups] BIT NOT NULL,
[UseDirectory] BIT NOT NULL,
[UseEvents] BIT NOT NULL,
diff --git a/test/Core.Test/Services/OrganizationServiceTests.cs b/test/Core.Test/Services/OrganizationServiceTests.cs
index 554ad9ce5e..a185c7b6fa 100644
--- a/test/Core.Test/Services/OrganizationServiceTests.cs
+++ b/test/Core.Test/Services/OrganizationServiceTests.cs
@@ -31,11 +31,13 @@ namespace Bit.Core.Test.Services
var installationRepo = Substitute.For();
var appCacheService = Substitute.For();
var paymentService = Substitute.For();
+ var policyRepo = Substitute.For();
var globalSettings = Substitute.For();
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
- licenseService, eventService, installationRepo, appCacheService, paymentService, globalSettings);
+ licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
+ globalSettings);
var id = Guid.NewGuid();
var userId = Guid.NewGuid();
@@ -87,11 +89,13 @@ namespace Bit.Core.Test.Services
var installationRepo = Substitute.For();
var appCacheService = Substitute.For();
var paymentService = Substitute.For();
+ var policyRepo = Substitute.For();
var globalSettings = Substitute.For();
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
- licenseService, eventService, installationRepo, appCacheService, paymentService, globalSettings);
+ licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
+ globalSettings);
var id = Guid.NewGuid();
var userId = Guid.NewGuid();
diff --git a/util/Migrator/DbScripts/2020-01-15_00_PolicySetup.sql b/util/Migrator/DbScripts/2020-01-15_00_PolicySetup.sql
index 0a08bd509a..f6c2090320 100644
--- a/util/Migrator/DbScripts/2020-01-15_00_PolicySetup.sql
+++ b/util/Migrator/DbScripts/2020-01-15_00_PolicySetup.sql
@@ -257,3 +257,246 @@ BEGIN
)
END
GO
+
+IF COL_LENGTH('[dbo].[Organization]', 'UsePolicies') IS NULL
+BEGIN
+ ALTER TABLE
+ [dbo].[Organization]
+ ADD
+ [UsePolicies] BIT NULL
+END
+GO
+
+UPDATE
+ [dbo].[Organization]
+SET
+ [UsePolicies] = (CASE WHEN [PlanType] = 5 OR [PlanType] = 4 THEN 1 ELSE 0 END)
+GO
+
+ALTER TABLE
+ [dbo].[Organization]
+ALTER COLUMN
+ [UsePolicies] BIT NOT NULL
+GO
+
+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
+
+IF OBJECT_ID('[dbo].[Organization_Create]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Organization_Create]
+END
+GO
+
+CREATE PROCEDURE [dbo].[Organization_Create]
+ @Id UNIQUEIDENTIFIER,
+ @Name NVARCHAR(50),
+ @BusinessName NVARCHAR(50),
+ @BusinessAddress1 NVARCHAR(50),
+ @BusinessAddress2 NVARCHAR(50),
+ @BusinessAddress3 NVARCHAR(50),
+ @BusinessCountry VARCHAR(2),
+ @BusinessTaxNumber NVARCHAR(30),
+ @BillingEmail NVARCHAR(50),
+ @Plan NVARCHAR(50),
+ @PlanType TINYINT,
+ @Seats SMALLINT,
+ @MaxCollections SMALLINT,
+ @UsePolicies BIT,
+ @UseGroups BIT,
+ @UseDirectory BIT,
+ @UseEvents BIT,
+ @UseTotp BIT,
+ @Use2fa BIT,
+ @UseApi BIT,
+ @SelfHost BIT,
+ @UsersGetPremium BIT,
+ @Storage BIGINT,
+ @MaxStorageGb SMALLINT,
+ @Gateway TINYINT,
+ @GatewayCustomerId VARCHAR(50),
+ @GatewaySubscriptionId VARCHAR(50),
+ @Enabled BIT,
+ @LicenseKey VARCHAR(100),
+ @ApiKey VARCHAR(30),
+ @TwoFactorProviders NVARCHAR(MAX),
+ @ExpirationDate DATETIME2(7),
+ @CreationDate DATETIME2(7),
+ @RevisionDate DATETIME2(7)
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ INSERT INTO [dbo].[Organization]
+ (
+ [Id],
+ [Name],
+ [BusinessName],
+ [BusinessAddress1],
+ [BusinessAddress2],
+ [BusinessAddress3],
+ [BusinessCountry],
+ [BusinessTaxNumber],
+ [BillingEmail],
+ [Plan],
+ [PlanType],
+ [Seats],
+ [MaxCollections],
+ [UsePolicies],
+ [UseGroups],
+ [UseDirectory],
+ [UseEvents],
+ [UseTotp],
+ [Use2fa],
+ [UseApi],
+ [SelfHost],
+ [UsersGetPremium],
+ [Storage],
+ [MaxStorageGb],
+ [Gateway],
+ [GatewayCustomerId],
+ [GatewaySubscriptionId],
+ [Enabled],
+ [LicenseKey],
+ [ApiKey],
+ [TwoFactorProviders],
+ [ExpirationDate],
+ [CreationDate],
+ [RevisionDate]
+ )
+ VALUES
+ (
+ @Id,
+ @Name,
+ @BusinessName,
+ @BusinessAddress1,
+ @BusinessAddress2,
+ @BusinessAddress3,
+ @BusinessCountry,
+ @BusinessTaxNumber,
+ @BillingEmail,
+ @Plan,
+ @PlanType,
+ @Seats,
+ @MaxCollections,
+ @UsePolicies,
+ @UseGroups,
+ @UseDirectory,
+ @UseEvents,
+ @UseTotp,
+ @Use2fa,
+ @UseApi,
+ @SelfHost,
+ @UsersGetPremium,
+ @Storage,
+ @MaxStorageGb,
+ @Gateway,
+ @GatewayCustomerId,
+ @GatewaySubscriptionId,
+ @Enabled,
+ @LicenseKey,
+ @ApiKey,
+ @TwoFactorProviders,
+ @ExpirationDate,
+ @CreationDate,
+ @RevisionDate
+ )
+END
+GO
+
+IF OBJECT_ID('[dbo].[Organization_Update]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Organization_Update]
+END
+GO
+
+CREATE PROCEDURE [dbo].[Organization_Update]
+ @Id UNIQUEIDENTIFIER,
+ @Name NVARCHAR(50),
+ @BusinessName NVARCHAR(50),
+ @BusinessAddress1 NVARCHAR(50),
+ @BusinessAddress2 NVARCHAR(50),
+ @BusinessAddress3 NVARCHAR(50),
+ @BusinessCountry VARCHAR(2),
+ @BusinessTaxNumber NVARCHAR(30),
+ @BillingEmail NVARCHAR(50),
+ @Plan NVARCHAR(50),
+ @PlanType TINYINT,
+ @Seats SMALLINT,
+ @MaxCollections SMALLINT,
+ @UsePolicies BIT,
+ @UseGroups BIT,
+ @UseDirectory BIT,
+ @UseEvents BIT,
+ @UseTotp BIT,
+ @Use2fa BIT,
+ @UseApi BIT,
+ @SelfHost BIT,
+ @UsersGetPremium BIT,
+ @Storage BIGINT,
+ @MaxStorageGb SMALLINT,
+ @Gateway TINYINT,
+ @GatewayCustomerId VARCHAR(50),
+ @GatewaySubscriptionId VARCHAR(50),
+ @Enabled BIT,
+ @LicenseKey VARCHAR(100),
+ @ApiKey VARCHAR(30),
+ @TwoFactorProviders NVARCHAR(MAX),
+ @ExpirationDate DATETIME2(7),
+ @CreationDate DATETIME2(7),
+ @RevisionDate DATETIME2(7)
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ UPDATE
+ [dbo].[Organization]
+ SET
+ [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,
+ [UseGroups] = @UseGroups,
+ [UseDirectory] = @UseDirectory,
+ [UseEvents] = @UseEvents,
+ [UseTotp] = @UseTotp,
+ [Use2fa] = @Use2fa,
+ [UseApi] = @UseApi,
+ [SelfHost] = @SelfHost,
+ [UsersGetPremium] = @UsersGetPremium,
+ [Storage] = @Storage,
+ [MaxStorageGb] = @MaxStorageGb,
+ [Gateway] = @Gateway,
+ [GatewayCustomerId] = @GatewayCustomerId,
+ [GatewaySubscriptionId] = @GatewaySubscriptionId,
+ [Enabled] = @Enabled,
+ [LicenseKey] = @LicenseKey,
+ [ApiKey] = @ApiKey,
+ [TwoFactorProviders] = @TwoFactorProviders,
+ [ExpirationDate] = @ExpirationDate,
+ [CreationDate] = @CreationDate,
+ [RevisionDate] = @RevisionDate
+ WHERE
+ [Id] = @Id
+END
+GO