diff --git a/src/Api/Api.csproj b/src/Api/Api.csproj index d5aaef9d89..0f6e643b9c 100644 --- a/src/Api/Api.csproj +++ b/src/Api/Api.csproj @@ -9,6 +9,14 @@ ..\..\docker\Docker.dcproj + + + + + + + + diff --git a/src/Api/Properties/launchSettings.json b/src/Api/Properties/launchSettings.json index d56f373353..b846e65755 100644 --- a/src/Api/Properties/launchSettings.json +++ b/src/Api/Properties/launchSettings.json @@ -4,7 +4,7 @@ "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:4000", - "sslPort": 44377 + "sslPort": 0 } }, "profiles": { diff --git a/src/Api/licensing.cer b/src/Api/licensing.cer new file mode 100644 index 0000000000..0dbb09c3c6 Binary files /dev/null and b/src/Api/licensing.cer differ diff --git a/src/Billing/Billing.csproj b/src/Billing/Billing.csproj index e2d119fba0..b68cd4f788 100644 --- a/src/Billing/Billing.csproj +++ b/src/Billing/Billing.csproj @@ -8,6 +8,10 @@ bitwarden-Billing + + + + diff --git a/src/Billing/licensing.cer b/src/Billing/licensing.cer new file mode 100644 index 0000000000..0dbb09c3c6 Binary files /dev/null and b/src/Billing/licensing.cer differ diff --git a/src/Core/GlobalSettings.cs b/src/Core/GlobalSettings.cs index ebcef09f54..b0ef548ab0 100644 --- a/src/Core/GlobalSettings.cs +++ b/src/Core/GlobalSettings.cs @@ -7,6 +7,7 @@ public virtual string StripeApiKey { get; set; } public virtual string ProjectName { get; set; } public virtual string LogDirectory { get; set; } + public virtual string LicenseDirectory { get; set; } public virtual BaseServiceUriSettings BaseServiceUri { get; set; } = new BaseServiceUriSettings(); public virtual SqlServerSettings SqlServer { get; set; } = new SqlServerSettings(); public virtual MailSettings Mail { get; set; } = new MailSettings(); diff --git a/src/Core/Models/Business/ILicense.cs b/src/Core/Models/Business/ILicense.cs new file mode 100644 index 0000000000..5dc8470f1c --- /dev/null +++ b/src/Core/Models/Business/ILicense.cs @@ -0,0 +1,17 @@ +using System; +using System.Security.Cryptography.X509Certificates; + +namespace Bit.Core.Models.Business +{ + public interface ILicense + { + string LicenseKey { get; set; } + int Version { get; set; } + DateTime Issued { get; set; } + DateTime Expires { get; set; } + bool Trial { get; set; } + string Signature { get; set; } + byte[] GetSignatureData(); + bool VerifySignature(X509Certificate2 certificate); + } +} diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs new file mode 100644 index 0000000000..d42f6b3c17 --- /dev/null +++ b/src/Core/Models/Business/OrganizationLicense.cs @@ -0,0 +1,119 @@ +using Bit.Core.Enums; +using Bit.Core.Models.Table; +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace Bit.Core.Models.Business +{ + public class OrganizationLicense : ILicense + { + public OrganizationLicense() + { } + + public OrganizationLicense(Organization org) + { + LicenseKey = ""; + Id = org.Id; + Name = org.Name; + Enabled = org.Enabled; + Seats = org.Seats; + MaxCollections = org.MaxCollections; + UseGroups = org.UseGroups; + UseDirectory = org.UseDirectory; + UseTotp = org.UseTotp; + MaxStorageGb = org.MaxStorageGb; + SelfHost = org.SelfHost; + Version = 1; + } + + public string LicenseKey { get; set; } + public Guid Id { get; set; } + public string Name { get; set; } + public bool Enabled { get; set; } + public string Plan { get; set; } + public PlanType PlanType { get; set; } + public short? Seats { get; set; } + public short? MaxCollections { get; set; } + public bool UseGroups { get; set; } + public bool UseDirectory { get; set; } + public bool UseTotp { get; set; } + public short? MaxStorageGb { get; set; } + public bool SelfHost { get; set; } + public int Version { get; set; } + public DateTime Issued { get; set; } + public DateTime Expires { get; set; } + public bool Trial { get; set; } + public string Signature { get; set; } + public byte[] SignatureBytes => Convert.FromBase64String(Signature); + + public byte[] GetSignatureData() + { + string data = null; + if(Version == 1) + { + data = string.Format("organization:{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}_{8}_{9}_{10}_{11}_{12}_{13}", + Version, + Utilities.CoreHelpers.ToEpocMilliseconds(Issued), + Utilities.CoreHelpers.ToEpocMilliseconds(Expires), + LicenseKey, + Id, + Enabled, + PlanType, + Seats, + MaxCollections, + UseGroups, + UseDirectory, + UseTotp, + MaxStorageGb, + SelfHost); + } + else + { + throw new NotSupportedException($"Version {Version} is not supported."); + } + + return Encoding.UTF8.GetBytes(data); + } + + public bool VerifyData(Organization organization) + { + if(Issued > DateTime.UtcNow) + { + return false; + } + + if(Expires < DateTime.UtcNow) + { + return false; + } + + if(Version == 1) + { + return + organization.LicenseKey.Equals(LicenseKey, StringComparison.InvariantCultureIgnoreCase) && + organization.Enabled == Enabled && + organization.PlanType == PlanType && + organization.Seats == Seats && + organization.MaxCollections == MaxCollections && + organization.UseGroups == UseGroups && + organization.UseDirectory == UseDirectory && + organization.UseTotp == UseTotp && + organization.SelfHost == SelfHost; + } + else + { + throw new NotSupportedException($"Version {Version} is not supported."); + } + } + + public bool VerifySignature(X509Certificate2 certificate) + { + using(var rsa = certificate.GetRSAPublicKey()) + { + return rsa.VerifyData(GetSignatureData(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + } + } +} diff --git a/src/Core/Models/Business/UserLicense.cs b/src/Core/Models/Business/UserLicense.cs new file mode 100644 index 0000000000..3320a19b6a --- /dev/null +++ b/src/Core/Models/Business/UserLicense.cs @@ -0,0 +1,90 @@ +using Bit.Core.Models.Table; +using System; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace Bit.Core.Models.Business +{ + public class UserLicense : ILicense + { + public UserLicense() + { } + + public UserLicense(User user) + { + LicenseKey = ""; + Id = user.Id; + Email = user.Email; + Version = 1; + } + + public string LicenseKey { get; set; } + public Guid Id { 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 Expires { get; set; } + public bool Trial { get; set; } + public string Signature { get; set; } + public byte[] SignatureBytes => Convert.FromBase64String(Signature); + + public byte[] GetSignatureData() + { + string data = null; + if(Version == 1) + { + data = string.Format("user:{0}_{1}_{2}_{3}_{4}_{5}_{6}_{7}", + Version, + Utilities.CoreHelpers.ToEpocMilliseconds(Issued), + Utilities.CoreHelpers.ToEpocMilliseconds(Expires), + LicenseKey, + Id, + Email, + Premium, + MaxStorageGb); + } + else + { + throw new NotSupportedException($"Version {Version} is not supported."); + } + + return Encoding.UTF8.GetBytes(data); + } + + public bool VerifyData(User user) + { + if(Issued > DateTime.UtcNow) + { + return false; + } + + if(Expires < DateTime.UtcNow) + { + return false; + } + + if(Version == 1) + { + return + user.LicenseKey.Equals(LicenseKey, StringComparison.InvariantCultureIgnoreCase) && + 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(GetSignatureData(), SignatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + } + } + } +} diff --git a/src/Core/Models/Table/Organization.cs b/src/Core/Models/Table/Organization.cs index 146ca21c59..e0ba24829e 100644 --- a/src/Core/Models/Table/Organization.cs +++ b/src/Core/Models/Table/Organization.cs @@ -19,12 +19,14 @@ namespace Bit.Core.Models.Table public bool UseGroups { get; set; } public bool UseDirectory { get; set; } public bool UseTotp { get; set; } + public bool SelfHost { get; set; } public long? Storage { get; set; } public short? MaxStorageGb { get; set; } public GatewayType? Gateway { get; set; } public string GatewayCustomerId { get; set; } public string GatewaySubscriptionId { get; set; } public bool Enabled { get; set; } = true; + public string LicenseKey { get; set; } public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow; diff --git a/src/Core/Models/Table/User.cs b/src/Core/Models/Table/User.cs index 40faeb3b98..f242bccf8c 100644 --- a/src/Core/Models/Table/User.cs +++ b/src/Core/Models/Table/User.cs @@ -35,6 +35,7 @@ namespace Bit.Core.Models.Table public GatewayType? Gateway { get; set; } public string GatewayCustomerId { get; set; } public string GatewaySubscriptionId { get; set; } + public string LicenseKey { get; set; } public DateTime CreationDate { get; internal set; } = DateTime.UtcNow; public DateTime RevisionDate { get; internal set; } = DateTime.UtcNow; diff --git a/src/Core/Services/ILicenseVerificationService.cs b/src/Core/Services/ILicenseVerificationService.cs new file mode 100644 index 0000000000..3a077fa1e0 --- /dev/null +++ b/src/Core/Services/ILicenseVerificationService.cs @@ -0,0 +1,10 @@ +using Bit.Core.Models.Table; + +namespace Bit.Core.Services +{ + public interface ILicenseVerificationService + { + bool VerifyOrganizationPlan(Organization organization); + bool VerifyUserPremium(User user); + } +} diff --git a/src/Core/Services/Implementations/RsaLicenseVerificationService.cs b/src/Core/Services/Implementations/RsaLicenseVerificationService.cs new file mode 100644 index 0000000000..d684707d97 --- /dev/null +++ b/src/Core/Services/Implementations/RsaLicenseVerificationService.cs @@ -0,0 +1,111 @@ +using Bit.Core.Models.Business; +using Bit.Core.Models.Table; +using Bit.Core.Utilities; +using Microsoft.AspNetCore.Hosting; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace Bit.Core.Services +{ + public class RsaLicenseVerificationService : ILicenseVerificationService + { + private readonly X509Certificate2 _certificate; + private readonly GlobalSettings _globalSettings; + private IDictionary _userLicenseCache; + private IDictionary _organizationLicenseCache; + + public RsaLicenseVerificationService( + IHostingEnvironment environment, + GlobalSettings globalSettings) + { + if(!environment.IsDevelopment() && !globalSettings.SelfHosted) + { + throw new Exception($"{nameof(RsaLicenseVerificationService)} can only be used for self hosted instances."); + } + + _globalSettings = globalSettings; + _certificate = CoreHelpers.GetCertificate("licensing.crt", null); + if(false && !_certificate.Thumbprint.Equals("")) + { + throw new Exception("Invalid licensing certificate."); + } + + if(!CoreHelpers.SettingHasValue(_globalSettings.LicenseDirectory)) + { + throw new InvalidOperationException("No license directory."); + } + } + + public bool VerifyOrganizationPlan(Organization organization) + { + if(_globalSettings.SelfHosted && !organization.SelfHost) + { + return false; + } + + var license = ReadOrganiztionLicense(organization); + return license != null && license.VerifyData(organization) && license.VerifySignature(_certificate); + } + + public bool VerifyUserPremium(User user) + { + if(!user.Premium) + { + return false; + } + + var license = ReadUserLicense(user); + return license != null && license.VerifyData(user) && license.VerifySignature(_certificate); + } + + private UserLicense ReadUserLicense(User user) + { + if(_userLicenseCache != null && _userLicenseCache.ContainsKey(user.LicenseKey)) + { + return _userLicenseCache[user.LicenseKey]; + } + + var filePath = $"{_globalSettings.LicenseDirectory}/user/{user.LicenseKey}.json"; + if(!File.Exists(filePath)) + { + return null; + } + + var data = File.ReadAllText(filePath, Encoding.UTF8); + var obj = JsonConvert.DeserializeObject(data); + if(_userLicenseCache == null) + { + _userLicenseCache = new Dictionary(); + } + _userLicenseCache.Add(obj.LicenseKey, obj); + return obj; + } + + private OrganizationLicense ReadOrganiztionLicense(Organization organization) + { + if(_organizationLicenseCache != null && _organizationLicenseCache.ContainsKey(organization.LicenseKey)) + { + return _organizationLicenseCache[organization.LicenseKey]; + } + + var filePath = $"{_globalSettings.LicenseDirectory}/organization/{organization.LicenseKey}.json"; + if(!File.Exists(filePath)) + { + return null; + } + + var data = File.ReadAllText(filePath, Encoding.UTF8); + var obj = JsonConvert.DeserializeObject(data); + if(_organizationLicenseCache == null) + { + _organizationLicenseCache = new Dictionary(); + } + _organizationLicenseCache.Add(obj.LicenseKey, obj); + return obj; + } + } +} diff --git a/src/Core/Services/NoopImplementations/NoopLicenseVerificationService.cs b/src/Core/Services/NoopImplementations/NoopLicenseVerificationService.cs new file mode 100644 index 0000000000..b88cea21df --- /dev/null +++ b/src/Core/Services/NoopImplementations/NoopLicenseVerificationService.cs @@ -0,0 +1,29 @@ +using Bit.Core.Models.Table; +using Microsoft.AspNetCore.Hosting; +using System; + +namespace Bit.Core.Services +{ + public class NoopLicenseVerificationService : ILicenseVerificationService + { + public NoopLicenseVerificationService( + IHostingEnvironment environment, + GlobalSettings globalSettings) + { + if(!environment.IsDevelopment() && globalSettings.SelfHosted) + { + throw new Exception($"{nameof(NoopLicenseVerificationService)} cannot be used for self hosted instances."); + } + } + + public bool VerifyOrganizationPlan(Organization organization) + { + return true; + } + + public bool VerifyUserPremium(User user) + { + return user.Premium; + } + } +} diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index 4e7c50a61e..571ee4d6bc 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -107,6 +107,15 @@ namespace Bit.Core.Utilities { services.AddSingleton(); } + + if(globalSettings.SelfHosted) + { + services.AddSingleton(); + } + else + { + services.AddSingleton(); + } } public static void AddNoopServices(this IServiceCollection services) @@ -117,6 +126,7 @@ namespace Bit.Core.Utilities services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); } public static IdentityBuilder AddCustomIdentityServices( diff --git a/src/Identity/Identity.csproj b/src/Identity/Identity.csproj index d8e8b58bad..e962ebd5a8 100644 --- a/src/Identity/Identity.csproj +++ b/src/Identity/Identity.csproj @@ -9,6 +9,14 @@ ..\..\docker\Docker.dcproj + + + + + + + + diff --git a/src/Identity/Properties/launchSettings.json b/src/Identity/Properties/launchSettings.json index d822506f3f..2edb5e39ff 100644 --- a/src/Identity/Properties/launchSettings.json +++ b/src/Identity/Properties/launchSettings.json @@ -4,7 +4,7 @@ "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:33656/", - "sslPort": 44392 + "sslPort": 0 } }, "profiles": { diff --git a/src/Identity/licensing.cer b/src/Identity/licensing.cer new file mode 100644 index 0000000000..0dbb09c3c6 Binary files /dev/null and b/src/Identity/licensing.cer differ diff --git a/src/Sql/dbo/Stored Procedures/Organization_Create.sql b/src/Sql/dbo/Stored Procedures/Organization_Create.sql index ce1b843e11..635ffc34f1 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_Create.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_Create.sql @@ -10,12 +10,14 @@ @UseGroups BIT, @UseDirectory BIT, @UseTotp BIT, + @SelfHost BIT, @Storage BIGINT, @MaxStorageGb SMALLINT, @Gateway TINYINT, @GatewayCustomerId VARCHAR(50), @GatewaySubscriptionId VARCHAR(50), @Enabled BIT, + @LicenseKey VARCHAR(100), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7) AS @@ -35,12 +37,14 @@ BEGIN [UseGroups], [UseDirectory], [UseTotp], + [SelfHost], [Storage], [MaxStorageGb], [Gateway], [GatewayCustomerId], [GatewaySubscriptionId], [Enabled], + [LicenseKey], [CreationDate], [RevisionDate] ) @@ -57,12 +61,14 @@ BEGIN @UseGroups, @UseDirectory, @UseTotp, + @SelfHost, @Storage, @MaxStorageGb, @Gateway, @GatewayCustomerId, @GatewaySubscriptionId, @Enabled, + @LicenseKey, @CreationDate, @RevisionDate ) diff --git a/src/Sql/dbo/Stored Procedures/Organization_Update.sql b/src/Sql/dbo/Stored Procedures/Organization_Update.sql index d9df669aea..14ad2c62ce 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_Update.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_Update.sql @@ -10,12 +10,14 @@ @UseGroups BIT, @UseDirectory BIT, @UseTotp BIT, + @SelfHost BIT, @Storage BIGINT, @MaxStorageGb SMALLINT, @Gateway TINYINT, @GatewayCustomerId VARCHAR(50), @GatewaySubscriptionId VARCHAR(50), @Enabled BIT, + @LicenseKey VARCHAR(100), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7) @@ -36,12 +38,14 @@ BEGIN [UseGroups] = @UseGroups, [UseDirectory] = @UseDirectory, [UseTotp] = @UseTotp, + [SelfHost] = @SelfHost, [Storage] = @Storage, [MaxStorageGb] = @MaxStorageGb, [Gateway] = @Gateway, [GatewayCustomerId] = @GatewayCustomerId, [GatewaySubscriptionId] = @GatewaySubscriptionId, [Enabled] = @Enabled, + [LicenseKey] = @LicenseKey, [CreationDate] = @CreationDate, [RevisionDate] = @RevisionDate WHERE diff --git a/src/Sql/dbo/Stored Procedures/User_Create.sql b/src/Sql/dbo/Stored Procedures/User_Create.sql index bb691d1075..ed334f9dd1 100644 --- a/src/Sql/dbo/Stored Procedures/User_Create.sql +++ b/src/Sql/dbo/Stored Procedures/User_Create.sql @@ -21,6 +21,7 @@ @Gateway TINYINT, @GatewayCustomerId VARCHAR(50), @GatewaySubscriptionId VARCHAR(50), + @LicenseKey VARCHAR(100), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7) AS @@ -51,6 +52,7 @@ BEGIN [Gateway], [GatewayCustomerId], [GatewaySubscriptionId], + [LicenseKey], [CreationDate], [RevisionDate] ) @@ -78,6 +80,7 @@ BEGIN @Gateway, @GatewayCustomerId, @GatewaySubscriptionId, + @LicenseKey, @CreationDate, @RevisionDate ) diff --git a/src/Sql/dbo/Stored Procedures/User_Update.sql b/src/Sql/dbo/Stored Procedures/User_Update.sql index 2c3dc21238..258b8c5af7 100644 --- a/src/Sql/dbo/Stored Procedures/User_Update.sql +++ b/src/Sql/dbo/Stored Procedures/User_Update.sql @@ -21,6 +21,7 @@ @Gateway TINYINT, @GatewayCustomerId VARCHAR(50), @GatewaySubscriptionId VARCHAR(50), + @LicenseKey VARCHAR(100), @CreationDate DATETIME2(7), @RevisionDate DATETIME2(7) AS @@ -51,6 +52,7 @@ BEGIN [Gateway] = @Gateway, [GatewayCustomerId] = @GatewayCustomerId, [GatewaySubscriptionId] = @GatewaySubscriptionId, + [LicenseKey] = @LicenseKey, [CreationDate] = @CreationDate, [RevisionDate] = @RevisionDate WHERE diff --git a/src/Sql/dbo/Tables/Organization.sql b/src/Sql/dbo/Tables/Organization.sql index 31d0ef077c..44258e20dc 100644 --- a/src/Sql/dbo/Tables/Organization.sql +++ b/src/Sql/dbo/Tables/Organization.sql @@ -10,12 +10,14 @@ [UseGroups] BIT NOT NULL, [UseDirectory] BIT NOT NULL, [UseTotp] BIT NOT NULL, + [SelfHost] BIT NOT NULL, [Storage] BIGINT NULL, [MaxStorageGb] SMALLINT NULL, [Gateway] TINYINT NULL, [GatewayCustomerId] VARCHAR (50) NULL, [GatewaySubscriptionId] VARCHAR (50) NULL, [Enabled] BIT NOT NULL, + [LicenseKey] VARCHAR (100) NULL, [CreationDate] DATETIME2 (7) NOT NULL, [RevisionDate] DATETIME2 (7) NOT NULL, CONSTRAINT [PK_Organization] PRIMARY KEY CLUSTERED ([Id] ASC) diff --git a/src/Sql/dbo/Tables/User.sql b/src/Sql/dbo/Tables/User.sql index 47a4495445..abc7e53be4 100644 --- a/src/Sql/dbo/Tables/User.sql +++ b/src/Sql/dbo/Tables/User.sql @@ -21,6 +21,7 @@ [Gateway] TINYINT NULL, [GatewayCustomerId] VARCHAR (50) NULL, [GatewaySubscriptionId] VARCHAR (50) NULL, + [LicenseKey] VARCHAR (100) NULL, [CreationDate] DATETIME2 (7) NOT NULL, [RevisionDate] DATETIME2 (7) NOT NULL, CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Id] ASC) diff --git a/util/Setup/Program.cs b/util/Setup/Program.cs index c27b183e84..2c4678e7ac 100644 --- a/util/Setup/Program.cs +++ b/util/Setup/Program.cs @@ -260,6 +260,7 @@ globalSettings:attachment:baseDirectory={_outputDir}/core/attachments globalSettings:attachment:baseUrl={_url}/attachments globalSettings:dataProtection:directory={_outputDir}/core/aspnet-dataprotection globalSettings:logDirectory={_outputDir}/core/logs +globalSettings:licenseDirectory={_outputDir}/core/licenses globalSettings:duo:aKey={Helpers.SecureRandomString(32, alpha: true, numeric: true)} globalSettings:yubico:clientId=REPLACE globalSettings:yubico:REPLACE"); diff --git a/util/SqlUpdate/2017-08-09_00_OrgSelfHost.sql b/util/SqlUpdate/2017-08-09_00_OrgSelfHost.sql new file mode 100644 index 0000000000..92bfae5c36 --- /dev/null +++ b/util/SqlUpdate/2017-08-09_00_OrgSelfHost.sql @@ -0,0 +1,26 @@ +alter table [Organization] add [SelfHost] BIT NULL +go + + +update [Organization] set [SelfHost] = 0 +go + +update [Organization] set [SelfHost] = 1 where PlanType = 4 or PlanType = 5 +go + + +alter table [Organization] alter column [SelfHost] BIT NOT NULL +go + + +drop view [dbo].[OrganizationView] +go + +CREATE VIEW [dbo].[OrganizationView] +AS +SELECT + * +FROM + [dbo].[Organization] +GO +