diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 9f3048a340..5399bed391 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -90,6 +90,9 @@ src/Admin/Views/Tools @bitwarden/team-billing-dev
.github/workflows/test-database.yml @bitwarden/team-platform-dev
.github/workflows/test.yml @bitwarden/team-platform-dev
**/*Platform* @bitwarden/team-platform-dev
+**/.dockerignore @bitwarden/team-platform-dev
+**/Dockerfile @bitwarden/team-platform-dev
+**/entrypoint.sh @bitwarden/team-platform-dev
# Multiple owners - DO NOT REMOVE (BRE)
**/packages.lock.json
diff --git a/.github/workflows/build_target.yml b/.github/workflows/build_target.yml
index 313446c949..d825721a7d 100644
--- a/.github/workflows/build_target.yml
+++ b/.github/workflows/build_target.yml
@@ -2,7 +2,9 @@ name: Build on PR Target
on:
pull_request_target:
- types: [opened, synchronize]
+ types: [opened, synchronize, reopened]
+ branches:
+ - "main"
defaults:
run:
diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml
index fe88782e35..f24a0973fd 100644
--- a/.github/workflows/scan.yml
+++ b/.github/workflows/scan.yml
@@ -7,8 +7,14 @@ on:
- "main"
- "rc"
- "hotfix-rc"
+ pull_request:
+ types: [opened, synchronize, reopened]
+ branches-ignore:
+ - main
pull_request_target:
- types: [opened, synchronize]
+ types: [opened, synchronize, reopened]
+ branches:
+ - "main"
jobs:
check-run:
diff --git a/bitwarden_license/src/Sso/package-lock.json b/bitwarden_license/src/Sso/package-lock.json
index 0b861365bc..98ea72c69e 100644
--- a/bitwarden_license/src/Sso/package-lock.json
+++ b/bitwarden_license/src/Sso/package-lock.json
@@ -15,7 +15,7 @@
},
"devDependencies": {
"css-loader": "7.1.2",
- "expose-loader": "5.0.0",
+ "expose-loader": "5.0.1",
"mini-css-extract-plugin": "2.9.2",
"sass": "1.85.0",
"sass-loader": "16.0.4",
@@ -1083,9 +1083,9 @@
}
},
"node_modules/expose-loader": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.0.tgz",
- "integrity": "sha512-BtUqYRmvx1bEY5HN6eK2I9URUZgNmN0x5UANuocaNjXSgfoDlkXt+wyEMe7i5DzDNh2BKJHPc5F4rBwEdSQX6w==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.1.tgz",
+ "integrity": "sha512-5YPZuszN/eWND/B+xuq5nIpb/l5TV1HYmdO6SubYtHv+HenVw9/6bn33Mm5reY8DNid7AVtbARvyUD34edfCtg==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/bitwarden_license/src/Sso/package.json b/bitwarden_license/src/Sso/package.json
index d9aefafef3..289612e79a 100644
--- a/bitwarden_license/src/Sso/package.json
+++ b/bitwarden_license/src/Sso/package.json
@@ -14,7 +14,7 @@
},
"devDependencies": {
"css-loader": "7.1.2",
- "expose-loader": "5.0.0",
+ "expose-loader": "5.0.1",
"mini-css-extract-plugin": "2.9.2",
"sass": "1.85.0",
"sass-loader": "16.0.4",
diff --git a/src/Admin/AdminConsole/Controllers/OrganizationsController.cs b/src/Admin/AdminConsole/Controllers/OrganizationsController.cs
index cb163f400a..6eb81b5956 100644
--- a/src/Admin/AdminConsole/Controllers/OrganizationsController.cs
+++ b/src/Admin/AdminConsole/Controllers/OrganizationsController.cs
@@ -462,6 +462,7 @@ public class OrganizationsController : Controller
organization.UsersGetPremium = model.UsersGetPremium;
organization.UseSecretsManager = model.UseSecretsManager;
organization.UseRiskInsights = model.UseRiskInsights;
+ organization.UseOrganizationDomains = model.UseOrganizationDomains;
organization.UseAdminSponsoredFamilies = model.UseAdminSponsoredFamilies;
//secrets
diff --git a/src/Admin/AdminConsole/Models/OrganizationEditModel.cs b/src/Admin/AdminConsole/Models/OrganizationEditModel.cs
index 6af6c1b50a..c79124688e 100644
--- a/src/Admin/AdminConsole/Models/OrganizationEditModel.cs
+++ b/src/Admin/AdminConsole/Models/OrganizationEditModel.cs
@@ -102,7 +102,7 @@ public class OrganizationEditModel : OrganizationViewModel
MaxAutoscaleSmSeats = org.MaxAutoscaleSmSeats;
SmServiceAccounts = org.SmServiceAccounts;
MaxAutoscaleSmServiceAccounts = org.MaxAutoscaleSmServiceAccounts;
-
+ UseOrganizationDomains = org.UseOrganizationDomains;
_plans = plans;
}
@@ -186,6 +186,8 @@ public class OrganizationEditModel : OrganizationViewModel
public int? SmServiceAccounts { get; set; }
[Display(Name = "Max Autoscale Machine Accounts")]
public int? MaxAutoscaleSmServiceAccounts { get; set; }
+ [Display(Name = "Use Organization Domains")]
+ public bool UseOrganizationDomains { get; set; }
/**
* Creates a Plan[] object for use in Javascript
@@ -215,6 +217,7 @@ public class OrganizationEditModel : OrganizationViewModel
Has2fa = p.Has2fa,
HasApi = p.HasApi,
HasSso = p.HasSso,
+ HasOrganizationDomains = p.HasOrganizationDomains,
HasKeyConnector = p.HasKeyConnector,
HasScim = p.HasScim,
HasResetPassword = p.HasResetPassword,
@@ -315,6 +318,7 @@ public class OrganizationEditModel : OrganizationViewModel
existingOrganization.MaxAutoscaleSmSeats = MaxAutoscaleSmSeats;
existingOrganization.SmServiceAccounts = SmServiceAccounts;
existingOrganization.MaxAutoscaleSmServiceAccounts = MaxAutoscaleSmServiceAccounts;
+ existingOrganization.UseOrganizationDomains = UseOrganizationDomains;
return existingOrganization;
}
}
diff --git a/src/Admin/AdminConsole/Views/Shared/_OrganizationForm.cshtml b/src/Admin/AdminConsole/Views/Shared/_OrganizationForm.cshtml
index 7b19b19939..267264a38f 100644
--- a/src/Admin/AdminConsole/Views/Shared/_OrganizationForm.cshtml
+++ b/src/Admin/AdminConsole/Views/Shared/_OrganizationForm.cshtml
@@ -124,6 +124,10 @@
+
+
+
+
diff --git a/src/Admin/AdminConsole/Views/Shared/_OrganizationFormScripts.cshtml b/src/Admin/AdminConsole/Views/Shared/_OrganizationFormScripts.cshtml
index 98d4c0d900..0ce25c700f 100644
--- a/src/Admin/AdminConsole/Views/Shared/_OrganizationFormScripts.cshtml
+++ b/src/Admin/AdminConsole/Views/Shared/_OrganizationFormScripts.cshtml
@@ -69,6 +69,7 @@
document.getElementById('@(nameof(Model.UseGroups))').checked = plan.hasGroups;
document.getElementById('@(nameof(Model.UsePolicies))').checked = plan.hasPolicies;
document.getElementById('@(nameof(Model.UseSso))').checked = plan.hasSso;
+ document.getElementById('@(nameof(Model.UseOrganizationDomains))').checked = hasOrganizationDomains;
document.getElementById('@(nameof(Model.UseScim))').checked = plan.hasScim;
document.getElementById('@(nameof(Model.UseDirectory))').checked = plan.hasDirectory;
document.getElementById('@(nameof(Model.UseEvents))').checked = plan.hasEvents;
diff --git a/src/Admin/Models/ChargeBraintreeModel.cs b/src/Admin/Models/ChargeBraintreeModel.cs
index 2ba06cb980..8c2f39e58d 100644
--- a/src/Admin/Models/ChargeBraintreeModel.cs
+++ b/src/Admin/Models/ChargeBraintreeModel.cs
@@ -17,7 +17,7 @@ public class ChargeBraintreeModel : IValidatableObject
{
if (Id != null)
{
- if (Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u') ||
+ if (Id.Length != 36 || (Id[0] != 'o' && Id[0] != 'u' && Id[0] != 'p') ||
!Guid.TryParse(Id.Substring(1, 32), out var guid))
{
yield return new ValidationResult("Customer Id is not a valid format.");
diff --git a/src/Admin/package-lock.json b/src/Admin/package-lock.json
index 24c2466746..3d339bd80c 100644
--- a/src/Admin/package-lock.json
+++ b/src/Admin/package-lock.json
@@ -16,7 +16,7 @@
},
"devDependencies": {
"css-loader": "7.1.2",
- "expose-loader": "5.0.0",
+ "expose-loader": "5.0.1",
"mini-css-extract-plugin": "2.9.2",
"sass": "1.85.0",
"sass-loader": "16.0.4",
@@ -1084,9 +1084,9 @@
}
},
"node_modules/expose-loader": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.0.tgz",
- "integrity": "sha512-BtUqYRmvx1bEY5HN6eK2I9URUZgNmN0x5UANuocaNjXSgfoDlkXt+wyEMe7i5DzDNh2BKJHPc5F4rBwEdSQX6w==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/expose-loader/-/expose-loader-5.0.1.tgz",
+ "integrity": "sha512-5YPZuszN/eWND/B+xuq5nIpb/l5TV1HYmdO6SubYtHv+HenVw9/6bn33Mm5reY8DNid7AVtbARvyUD34edfCtg==",
"dev": true,
"license": "MIT",
"engines": {
diff --git a/src/Admin/package.json b/src/Admin/package.json
index 7f3c8046a2..eed8eaf7aa 100644
--- a/src/Admin/package.json
+++ b/src/Admin/package.json
@@ -15,7 +15,7 @@
},
"devDependencies": {
"css-loader": "7.1.2",
- "expose-loader": "5.0.0",
+ "expose-loader": "5.0.1",
"mini-css-extract-plugin": "2.9.2",
"sass": "1.85.0",
"sass-loader": "16.0.4",
diff --git a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs
index a14e3efb51..95754598b9 100644
--- a/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs
+++ b/src/Api/AdminConsole/Models/Response/Organizations/OrganizationResponseModel.cs
@@ -64,6 +64,7 @@ public class OrganizationResponseModel : ResponseModel
LimitItemDeletion = organization.LimitItemDeletion;
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
UseRiskInsights = organization.UseRiskInsights;
+ UseOrganizationDomains = organization.UseOrganizationDomains;
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
}
@@ -111,6 +112,7 @@ public class OrganizationResponseModel : ResponseModel
public bool LimitItemDeletion { get; set; }
public bool AllowAdminAccessToAllCollectionItems { get; set; }
public bool UseRiskInsights { get; set; }
+ public bool UseOrganizationDomains { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
}
diff --git a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs
index 259ce3e795..cb0ab62fd1 100644
--- a/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs
+++ b/src/Api/AdminConsole/Models/Response/ProfileOrganizationResponseModel.cs
@@ -73,6 +73,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
UserIsClaimedByOrganization = organizationIdsClaimingUser.Contains(organization.OrganizationId);
UseRiskInsights = organization.UseRiskInsights;
+ UseOrganizationDomains = organization.UseOrganizationDomains;
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
if (organization.SsoConfig != null)
@@ -153,6 +154,7 @@ public class ProfileOrganizationResponseModel : ResponseModel
///
public bool UserIsClaimedByOrganization { get; set; }
public bool UseRiskInsights { get; set; }
+ public bool UseOrganizationDomains { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
public bool IsAdminInitiated { get; set; }
}
diff --git a/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs
index 5d5e1f9b85..24b6fed704 100644
--- a/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs
+++ b/src/Api/AdminConsole/Models/Response/ProfileProviderOrganizationResponseModel.cs
@@ -50,6 +50,7 @@ public class ProfileProviderOrganizationResponseModel : ProfileOrganizationRespo
LimitItemDeletion = organization.LimitItemDeletion;
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
UseRiskInsights = organization.UseRiskInsights;
+ UseOrganizationDomains = organization.UseOrganizationDomains;
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
}
}
diff --git a/src/Api/Models/Response/PlanResponseModel.cs b/src/Api/Models/Response/PlanResponseModel.cs
index 74bcb59661..f48a06b4ec 100644
--- a/src/Api/Models/Response/PlanResponseModel.cs
+++ b/src/Api/Models/Response/PlanResponseModel.cs
@@ -32,6 +32,7 @@ public class PlanResponseModel : ResponseModel
HasTotp = plan.HasTotp;
Has2fa = plan.Has2fa;
HasSso = plan.HasSso;
+ HasOrganizationDomains = plan.HasOrganizationDomains;
HasResetPassword = plan.HasResetPassword;
UsersGetPremium = plan.UsersGetPremium;
UpgradeSortOrder = plan.UpgradeSortOrder;
@@ -71,6 +72,7 @@ public class PlanResponseModel : ResponseModel
public bool Has2fa { get; set; }
public bool HasApi { get; set; }
public bool HasSso { get; set; }
+ public bool HasOrganizationDomains { get; set; }
public bool HasResetPassword { get; set; }
public bool UsersGetPremium { get; set; }
diff --git a/src/Api/Vault/Controllers/CiphersController.cs b/src/Api/Vault/Controllers/CiphersController.cs
index 02dace894d..4f105128ea 100644
--- a/src/Api/Vault/Controllers/CiphersController.cs
+++ b/src/Api/Vault/Controllers/CiphersController.cs
@@ -315,26 +315,10 @@ public class CiphersController : Controller
{
var org = _currentContext.GetOrganization(organizationId);
- // If we're not an "admin", we don't need to check the ciphers
- if (org is not ({ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or { Permissions.EditAnyCollection: true }))
+ // If we're not an "admin" or if we're not a provider user we don't need to check the ciphers
+ if (org is not ({ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or { Permissions.EditAnyCollection: true }) || await _currentContext.ProviderUserForOrgAsync(organizationId))
{
- // Are we a provider user? If so, we need to be sure we're not restricted
- // Once the feature flag is removed, this check can be combined with the above
- if (await _currentContext.ProviderUserForOrgAsync(organizationId))
- {
- // Provider is restricted from editing ciphers, so we're not an "admin"
- if (_featureService.IsEnabled(FeatureFlagKeys.RestrictProviderAccess))
- {
- return false;
- }
-
- // Provider is unrestricted, so we're an "admin", don't return early
- }
- else
- {
- // Not a provider or admin
- return false;
- }
+ return false;
}
// We know we're an "admin", now check the ciphers explicitly (in case admins are restricted)
@@ -350,26 +334,10 @@ public class CiphersController : Controller
var org = _currentContext.GetOrganization(organizationId);
- // If we're not an "admin", we don't need to check the ciphers
- if (org is not ({ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or { Permissions.EditAnyCollection: true }))
+ // If we're not an "admin" or if we're a provider user we don't need to check the ciphers
+ if (org is not ({ Type: OrganizationUserType.Owner or OrganizationUserType.Admin } or { Permissions.EditAnyCollection: true }) || await _currentContext.ProviderUserForOrgAsync(organizationId))
{
- // Are we a provider user? If so, we need to be sure we're not restricted
- // Once the feature flag is removed, this check can be combined with the above
- if (await _currentContext.ProviderUserForOrgAsync(organizationId))
- {
- // Provider is restricted from editing ciphers, so we're not an "admin"
- if (_featureService.IsEnabled(FeatureFlagKeys.RestrictProviderAccess))
- {
- return false;
- }
-
- // Provider is unrestricted, so we're an "admin", don't return early
- }
- else
- {
- // Not a provider or admin
- return false;
- }
+ return false;
}
// If the user can edit all ciphers for the organization, just check they all belong to the org
@@ -462,10 +430,10 @@ public class CiphersController : Controller
return true;
}
- // Provider users can edit all ciphers if RestrictProviderAccess is disabled
+ // Provider users cannot edit ciphers
if (await _currentContext.ProviderUserForOrgAsync(organizationId))
{
- return !_featureService.IsEnabled(FeatureFlagKeys.RestrictProviderAccess);
+ return false;
}
return false;
@@ -485,10 +453,10 @@ public class CiphersController : Controller
return true;
}
- // Provider users can only access organization ciphers if RestrictProviderAccess is disabled
+ // Provider users cannot access organization ciphers
if (await _currentContext.ProviderUserForOrgAsync(organizationId))
{
- return !_featureService.IsEnabled(FeatureFlagKeys.RestrictProviderAccess);
+ return false;
}
return false;
@@ -508,10 +476,10 @@ public class CiphersController : Controller
return true;
}
- // Provider users can only access all ciphers if RestrictProviderAccess is disabled
+ // Provider users cannot access ciphers
if (await _currentContext.ProviderUserForOrgAsync(organizationId))
{
- return !_featureService.IsEnabled(FeatureFlagKeys.RestrictProviderAccess);
+ return false;
}
return false;
diff --git a/src/Core/AdminConsole/Entities/Organization.cs b/src/Core/AdminConsole/Entities/Organization.cs
index 17d9847574..4b7d497277 100644
--- a/src/Core/AdminConsole/Entities/Organization.cs
+++ b/src/Core/AdminConsole/Entities/Organization.cs
@@ -114,6 +114,11 @@ public class Organization : ITableObject
, IStorableSubscriber, IRevisable,
///
public bool UseRiskInsights { get; set; }
+ ///
+ /// If true, the organization can claim domains, which unlocks additional enterprise features
+ ///
+ public bool UseOrganizationDomains { get; set; }
+
///
/// If set to true, admins can initiate organization-issued sponsorships.
///
@@ -319,5 +324,6 @@ public class Organization : ITableObject, IStorableSubscriber, IRevisable,
SmSeats = license.SmSeats;
SmServiceAccounts = license.SmServiceAccounts;
UseRiskInsights = license.UseRiskInsights;
+ UseOrganizationDomains = license.UseOrganizationDomains;
}
}
diff --git a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs
index d27bf40994..ae91f204e3 100644
--- a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs
+++ b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationAbility.cs
@@ -26,6 +26,7 @@ public class OrganizationAbility
LimitItemDeletion = organization.LimitItemDeletion;
AllowAdminAccessToAllCollectionItems = organization.AllowAdminAccessToAllCollectionItems;
UseRiskInsights = organization.UseRiskInsights;
+ UseOrganizationDomains = organization.UseOrganizationDomains;
UseAdminSponsoredFamilies = organization.UseAdminSponsoredFamilies;
}
@@ -46,5 +47,6 @@ public class OrganizationAbility
public bool LimitItemDeletion { get; set; }
public bool AllowAdminAccessToAllCollectionItems { get; set; }
public bool UseRiskInsights { get; set; }
+ public bool UseOrganizationDomains { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
}
diff --git a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs
index a804dc0f6a..8de422ee31 100644
--- a/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs
+++ b/src/Core/AdminConsole/Models/Data/Organizations/OrganizationUsers/OrganizationUserOrganizationDetails.cs
@@ -59,6 +59,7 @@ public class OrganizationUserOrganizationDetails
public bool LimitItemDeletion { get; set; }
public bool AllowAdminAccessToAllCollectionItems { get; set; }
public bool UseRiskInsights { get; set; }
+ public bool UseOrganizationDomains { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
public bool? IsAdminInitiated { get; set; }
}
diff --git a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs
index 8717a8f008..4621de8268 100644
--- a/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs
+++ b/src/Core/AdminConsole/Models/Data/Provider/ProviderUserOrganizationDetails.cs
@@ -45,6 +45,7 @@ public class ProviderUserOrganizationDetails
public bool LimitItemDeletion { get; set; }
public bool AllowAdminAccessToAllCollectionItems { get; set; }
public bool UseRiskInsights { get; set; }
+ public bool UseOrganizationDomains { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
public ProviderType ProviderType { get; set; }
}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Organizations/CloudOrganizationSignUpCommand.cs b/src/Core/AdminConsole/OrganizationFeatures/Organizations/CloudOrganizationSignUpCommand.cs
index 60e090de2a..7449628ed0 100644
--- a/src/Core/AdminConsole/OrganizationFeatures/Organizations/CloudOrganizationSignUpCommand.cs
+++ b/src/Core/AdminConsole/OrganizationFeatures/Organizations/CloudOrganizationSignUpCommand.cs
@@ -104,7 +104,8 @@ public class CloudOrganizationSignUpCommand(
RevisionDate = DateTime.UtcNow,
Status = OrganizationStatusType.Created,
UsePasswordManager = true,
- UseSecretsManager = signup.UseSecretsManager
+ UseSecretsManager = signup.UseSecretsManager,
+ UseOrganizationDomains = plan.HasOrganizationDomains,
};
if (signup.UseSecretsManager)
diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs
index 5c7e5e29ed..3a9fcff847 100644
--- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs
+++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs
@@ -449,6 +449,7 @@ public class OrganizationService : IOrganizationService
MaxStorageGb = 1,
UsePolicies = plan.HasPolicies,
UseSso = plan.HasSso,
+ UseOrganizationDomains = plan.HasOrganizationDomains,
UseGroups = plan.HasGroups,
UseEvents = plan.HasEvents,
UseDirectory = plan.HasDirectory,
@@ -570,6 +571,7 @@ public class OrganizationService : IOrganizationService
SmSeats = license.SmSeats,
SmServiceAccounts = license.SmServiceAccounts,
UseRiskInsights = license.UseRiskInsights,
+ UseOrganizationDomains = license.UseOrganizationDomains,
};
var result = await SignUpAsync(organization, owner.Id, ownerKey, collectionName, false);
diff --git a/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs b/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs
index 834d2722cc..e721649dc9 100644
--- a/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs
+++ b/src/Core/Auth/UserFeatures/Registration/Implementations/RegisterUserCommand.cs
@@ -108,6 +108,7 @@ public class RegisterUserCommand : IRegisterUserCommand
var result = await _userService.CreateUserAsync(user, masterPasswordHash);
if (result == IdentityResult.Success)
{
+ var sentWelcomeEmail = false;
if (!string.IsNullOrEmpty(user.ReferenceData))
{
var referenceData = JsonConvert.DeserializeObject>(user.ReferenceData);
@@ -115,6 +116,7 @@ public class RegisterUserCommand : IRegisterUserCommand
{
var initiationPath = value.ToString();
await SendAppropriateWelcomeEmailAsync(user, initiationPath);
+ sentWelcomeEmail = true;
if (!string.IsNullOrEmpty(initiationPath))
{
await _referenceEventService.RaiseEventAsync(
@@ -128,6 +130,11 @@ public class RegisterUserCommand : IRegisterUserCommand
}
}
+ if (!sentWelcomeEmail)
+ {
+ await _mailService.SendWelcomeEmailAsync(user);
+ }
+
await _referenceEventService.RaiseEventAsync(new ReferenceEvent(ReferenceEventType.Signup, user, _currentContext));
}
diff --git a/src/Core/Billing/Licenses/LicenseConstants.cs b/src/Core/Billing/Licenses/LicenseConstants.cs
index 513578f43e..cdfac76614 100644
--- a/src/Core/Billing/Licenses/LicenseConstants.cs
+++ b/src/Core/Billing/Licenses/LicenseConstants.cs
@@ -42,6 +42,7 @@ public static class OrganizationLicenseConstants
public const string ExpirationWithoutGracePeriod = nameof(ExpirationWithoutGracePeriod);
public const string Trial = nameof(Trial);
public const string UseAdminSponsoredFamilies = nameof(UseAdminSponsoredFamilies);
+ public const string UseOrganizationDomains = nameof(UseOrganizationDomains);
}
public static class UserLicenseConstants
diff --git a/src/Core/Billing/Licenses/Services/Implementations/OrganizationLicenseClaimsFactory.cs b/src/Core/Billing/Licenses/Services/Implementations/OrganizationLicenseClaimsFactory.cs
index 6819d3cc0b..b3f2ab4ec9 100644
--- a/src/Core/Billing/Licenses/Services/Implementations/OrganizationLicenseClaimsFactory.cs
+++ b/src/Core/Billing/Licenses/Services/Implementations/OrganizationLicenseClaimsFactory.cs
@@ -54,6 +54,7 @@ public class OrganizationLicenseClaimsFactory : ILicenseClaimsFactory
-
+
@@ -60,10 +60,10 @@
-
-
-
-
+
+
+
+
diff --git a/src/Core/Models/Business/OrganizationLicense.cs b/src/Core/Models/Business/OrganizationLicense.cs
index 02e1c109a7..a201da3847 100644
--- a/src/Core/Models/Business/OrganizationLicense.cs
+++ b/src/Core/Models/Business/OrganizationLicense.cs
@@ -182,6 +182,7 @@ public class OrganizationLicense : ILicense
public bool Trial { get; set; }
public LicenseType? LicenseType { get; set; }
+ public bool UseOrganizationDomains { get; set; }
public bool UseAdminSponsoredFamilies { get; set; }
public string Hash { get; set; }
public string Signature { get; set; }
@@ -445,6 +446,7 @@ public class OrganizationLicense : ILicense
var smSeats = claimsPrincipal.GetValue(nameof(SmSeats));
var smServiceAccounts = claimsPrincipal.GetValue(nameof(SmServiceAccounts));
var useAdminSponsoredFamilies = claimsPrincipal.GetValue(nameof(UseAdminSponsoredFamilies));
+ var useOrganizationDomains = claimsPrincipal.GetValue(nameof(UseOrganizationDomains));
return issued <= DateTime.UtcNow &&
expires >= DateTime.UtcNow &&
@@ -473,7 +475,8 @@ public class OrganizationLicense : ILicense
usePasswordManager == organization.UsePasswordManager &&
smSeats == organization.SmSeats &&
smServiceAccounts == organization.SmServiceAccounts &&
- useAdminSponsoredFamilies == organization.UseAdminSponsoredFamilies;
+ useAdminSponsoredFamilies == organization.UseAdminSponsoredFamilies &&
+ useOrganizationDomains == organization.UseOrganizationDomains;
}
diff --git a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs
index 09b766e885..cb37e478f7 100644
--- a/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs
+++ b/src/Core/OrganizationFeatures/OrganizationSubscriptions/UpgradeOrganizationPlanCommand.cs
@@ -263,6 +263,7 @@ public class UpgradeOrganizationPlanCommand : IUpgradeOrganizationPlanCommand
organization.Use2fa = newPlan.Has2fa;
organization.UseApi = newPlan.HasApi;
organization.UseSso = newPlan.HasSso;
+ organization.UseOrganizationDomains = newPlan.HasOrganizationDomains;
organization.UseKeyConnector = newPlan.HasKeyConnector;
organization.UseScim = newPlan.HasScim;
organization.UseResetPassword = newPlan.HasResetPassword;
diff --git a/src/Core/Resources/SharedResources.en.resx b/src/Core/Resources/SharedResources.en.resx
index 3ef0b54efe..90a791222f 100644
--- a/src/Core/Resources/SharedResources.en.resx
+++ b/src/Core/Resources/SharedResources.en.resx
@@ -1,17 +1,17 @@
-
+
-
diff --git a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs
index 91e29c1b52..f83f7b70b6 100644
--- a/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs
+++ b/src/Infrastructure.EntityFramework/AdminConsole/Repositories/OrganizationRepository.cs
@@ -107,6 +107,7 @@ public class OrganizationRepository : Repository sutProvider
- )
- {
- cipherDetails.OrganizationId = organization.Id;
-
- // Simulate that the user is a provider for the organization
- sutProvider.GetDependency().EditAnyCollection(organization.Id).Returns(true);
- sutProvider.GetDependency().ProviderUserForOrgAsync(organization.Id).Returns(true);
-
- sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
-
- sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
- sutProvider.GetDependency().GetManyByOrganizationIdAsync(organization.Id).Returns(new List { cipherDetails });
-
- sutProvider.GetDependency().GetOrganizationAbilityAsync(organization.Id).Returns(new OrganizationAbility
- {
- Id = organization.Id,
- AllowAdminAccessToAllCollectionItems = false
- });
- sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(restrictProviders);
-
- // Non restricted providers should succeed
- if (!restrictProviders)
- {
- await sutProvider.Sut.DeleteAdmin(cipherDetails.Id);
- await sutProvider.GetDependency().ReceivedWithAnyArgs()
- .DeleteAsync(default, default);
- }
- else // Otherwise, they should fail
- {
- await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteAdmin(cipherDetails.Id));
- await sutProvider.GetDependency().DidNotReceiveWithAnyArgs()
- .DeleteAsync(default, default);
- }
-
- await sutProvider.GetDependency().Received().ProviderUserForOrgAsync(organization.Id);
- }
-
[Theory]
[BitAutoData(OrganizationUserType.Owner)]
[BitAutoData(OrganizationUserType.Admin)]
@@ -456,24 +413,7 @@ public class CiphersControllerTests
[Theory]
[BitAutoData]
- public async Task DeleteAdmin_WithProviderUser_DeletesCipher(
- CipherDetails cipherDetails, Guid userId, SutProvider sutProvider)
- {
- cipherDetails.OrganizationId = Guid.NewGuid();
-
- sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
- sutProvider.GetDependency().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true);
- sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
- sutProvider.GetDependency().GetManyByOrganizationIdAsync(cipherDetails.OrganizationId.Value).Returns(new List { cipherDetails });
-
- await sutProvider.Sut.DeleteAdmin(cipherDetails.Id);
-
- await sutProvider.GetDependency().Received(1).DeleteAsync(cipherDetails, userId, true);
- }
-
- [Theory]
- [BitAutoData]
- public async Task DeleteAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
+ public async Task DeleteAdmin_WithProviderUser_ThrowsNotFoundException(
Cipher cipher, Guid userId, SutProvider sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
@@ -481,7 +421,6 @@ public class CiphersControllerTests
sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency().GetByIdAsync(cipher.Id).Returns(cipher);
- sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteAdmin(cipher.Id));
}
@@ -737,43 +676,13 @@ public class CiphersControllerTests
[Theory]
[BitAutoData]
- public async Task DeleteManyAdmin_WithProviderUser_DeletesCiphers(
- CipherBulkDeleteRequestModel model, Guid userId,
- List ciphers, SutProvider sutProvider)
- {
- var organizationId = Guid.NewGuid();
- model.OrganizationId = organizationId.ToString();
- model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
-
- foreach (var cipher in ciphers)
- {
- cipher.OrganizationId = organizationId;
- }
-
- sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
- sutProvider.GetDependency().ProviderUserForOrgAsync(organizationId).Returns(true);
- sutProvider.GetDependency().GetManyByOrganizationIdAsync(organizationId).Returns(ciphers);
-
- await sutProvider.Sut.DeleteManyAdmin(model);
-
- await sutProvider.GetDependency()
- .Received(1)
- .DeleteManyAsync(
- Arg.Is>(ids =>
- ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
- userId, organizationId, true);
- }
-
- [Theory]
- [BitAutoData]
- public async Task DeleteManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
+ public async Task DeleteManyAdmin_WithProviderUser_ThrowsNotFoundException(
CipherBulkDeleteRequestModel model, SutProvider sutProvider)
{
var organizationId = Guid.NewGuid();
model.OrganizationId = organizationId.ToString();
sutProvider.GetDependency().ProviderUserForOrgAsync(organizationId).Returns(true);
- sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync(() => sutProvider.Sut.DeleteManyAdmin(model));
}
@@ -1000,24 +909,7 @@ public class CiphersControllerTests
[Theory]
[BitAutoData]
- public async Task PutDeleteAdmin_WithProviderUser_SoftDeletesCipher(
- CipherDetails cipherDetails, Guid userId, SutProvider sutProvider)
- {
- cipherDetails.OrganizationId = Guid.NewGuid();
-
- sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
- sutProvider.GetDependency().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true);
- sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
- sutProvider.GetDependency().GetManyByOrganizationIdAsync(cipherDetails.OrganizationId.Value).Returns(new List { cipherDetails });
-
- await sutProvider.Sut.PutDeleteAdmin(cipherDetails.Id);
-
- await sutProvider.GetDependency().Received(1).SoftDeleteAsync(cipherDetails, userId, true);
- }
-
- [Theory]
- [BitAutoData]
- public async Task PutDeleteAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
+ public async Task PutDeleteAdmin_WithProviderUser_ThrowsNotFoundException(
Cipher cipher, Guid userId, SutProvider sutProvider)
{
cipher.OrganizationId = Guid.NewGuid();
@@ -1025,7 +917,6 @@ public class CiphersControllerTests
sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency().ProviderUserForOrgAsync(cipher.OrganizationId.Value).Returns(true);
sutProvider.GetDependency().GetByIdAsync(cipher.Id).Returns(cipher);
- sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync(() => sutProvider.Sut.PutDeleteAdmin(cipher.Id));
}
@@ -1272,43 +1163,13 @@ public class CiphersControllerTests
[Theory]
[BitAutoData]
- public async Task PutDeleteManyAdmin_WithProviderUser_SoftDeletesCiphers(
- CipherBulkDeleteRequestModel model, Guid userId,
- List ciphers, SutProvider sutProvider)
- {
- var organizationId = Guid.NewGuid();
- model.OrganizationId = organizationId.ToString();
- model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
-
- foreach (var cipher in ciphers)
- {
- cipher.OrganizationId = organizationId;
- }
-
- sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
- sutProvider.GetDependency().ProviderUserForOrgAsync(organizationId).Returns(true);
- sutProvider.GetDependency().GetManyByOrganizationIdAsync(organizationId).Returns(ciphers);
-
- await sutProvider.Sut.PutDeleteManyAdmin(model);
-
- await sutProvider.GetDependency()
- .Received(1)
- .SoftDeleteManyAsync(
- Arg.Is>(ids =>
- ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count() == model.Ids.Count()),
- userId, organizationId, true);
- }
-
- [Theory]
- [BitAutoData]
- public async Task PutDeleteManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
+ public async Task PutDeleteManyAdmin_WithProviderUser_ThrowsNotFoundException(
CipherBulkDeleteRequestModel model, SutProvider sutProvider)
{
var organizationId = Guid.NewGuid();
model.OrganizationId = organizationId.ToString();
sutProvider.GetDependency().ProviderUserForOrgAsync(organizationId).Returns(true);
- sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync(() => sutProvider.Sut.PutDeleteManyAdmin(model));
}
@@ -1546,27 +1407,7 @@ public class CiphersControllerTests
[Theory]
[BitAutoData]
- public async Task PutRestoreAdmin_WithProviderUser_RestoresCipher(
- CipherDetails cipherDetails, Guid userId, SutProvider sutProvider)
- {
- cipherDetails.OrganizationId = Guid.NewGuid();
- cipherDetails.Type = CipherType.Login;
- cipherDetails.Data = JsonSerializer.Serialize(new CipherLoginData());
-
- sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
- sutProvider.GetDependency().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true);
- sutProvider.GetDependency().GetByIdAsync(cipherDetails.Id, userId).Returns(cipherDetails);
- sutProvider.GetDependency().GetManyByOrganizationIdAsync(cipherDetails.OrganizationId.Value).Returns(new List { cipherDetails });
-
- var result = await sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id);
-
- Assert.IsType(result);
- await sutProvider.GetDependency().Received(1).RestoreAsync(cipherDetails, userId, true);
- }
-
- [Theory]
- [BitAutoData]
- public async Task PutRestoreAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
+ public async Task PutRestoreAdmin_WithProviderUser_ThrowsNotFoundException(
CipherDetails cipherDetails, Guid userId, SutProvider sutProvider)
{
cipherDetails.OrganizationId = Guid.NewGuid();
@@ -1574,7 +1415,6 @@ public class CiphersControllerTests
sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
sutProvider.GetDependency().ProviderUserForOrgAsync(cipherDetails.OrganizationId.Value).Returns(true);
sutProvider.GetDependency().GetOrganizationDetailsByIdAsync(cipherDetails.Id).Returns(cipherDetails);
- sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync(() => sutProvider.Sut.PutRestoreAdmin(cipherDetails.Id));
}
@@ -1896,49 +1736,12 @@ public class CiphersControllerTests
[Theory]
[BitAutoData]
- public async Task PutRestoreManyAdmin_WithProviderUser_RestoresCiphers(
- CipherBulkRestoreRequestModel model, Guid userId,
- List ciphers, SutProvider sutProvider)
- {
- model.OrganizationId = Guid.NewGuid();
- model.Ids = ciphers.Select(c => c.Id.ToString()).ToList();
-
- sutProvider.GetDependency().GetProperUserId(default).ReturnsForAnyArgs(userId);
- sutProvider.GetDependency().ProviderUserForOrgAsync(model.OrganizationId).Returns(true);
- sutProvider.GetDependency().GetManyByOrganizationIdAsync(model.OrganizationId).Returns(ciphers);
-
- var cipherOrgDetails = ciphers.Select(c => new CipherOrganizationDetails
- {
- Id = c.Id,
- OrganizationId = model.OrganizationId
- }).ToList();
-
- sutProvider.GetDependency()
- .RestoreManyAsync(
- Arg.Any>(),
- userId, model.OrganizationId, true)
- .Returns(cipherOrgDetails);
-
- var result = await sutProvider.Sut.PutRestoreManyAdmin(model);
-
- Assert.NotNull(result);
- await sutProvider.GetDependency()
- .Received(1)
- .RestoreManyAsync(
- Arg.Is>(ids =>
- ids.All(id => model.Ids.Contains(id.ToString())) && ids.Count == model.Ids.Count()),
- userId, model.OrganizationId, true);
- }
-
- [Theory]
- [BitAutoData]
- public async Task PutRestoreManyAdmin_WithProviderUser_WithRestrictProviderAccessTrue_ThrowsNotFoundException(
+ public async Task PutRestoreManyAdmin_WithProviderUser_ThrowsNotFoundException(
CipherBulkRestoreRequestModel model, SutProvider sutProvider)
{
model.OrganizationId = Guid.NewGuid();
sutProvider.GetDependency().ProviderUserForOrgAsync(model.OrganizationId).Returns(true);
- sutProvider.GetDependency().IsEnabled(FeatureFlagKeys.RestrictProviderAccess).Returns(true);
await Assert.ThrowsAsync(() => sutProvider.Sut.PutRestoreManyAdmin(model));
}
diff --git a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs
index 02ecb4ecd7..ffc56e89b2 100644
--- a/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs
+++ b/test/Core.Test/Auth/UserFeatures/Registration/RegisterUserCommandTests.cs
@@ -226,6 +226,11 @@ public class RegisterUserCommandTests
await sutProvider.GetDependency()
.Received(1)
.RaiseEventAsync(Arg.Is(refEvent => refEvent.Type == ReferenceEventType.Signup && refEvent.SignupInitiationPath == default));
+
+ // Even if user doesn't have reference data, we should send them welcome email
+ await sutProvider.GetDependency()
+ .Received(1)
+ .SendWelcomeEmailAsync(user);
}
Assert.True(result.Succeeded);
diff --git a/test/Core.Test/Models/Business/OrganizationLicenseFileFixtures.cs b/test/Core.Test/Models/Business/OrganizationLicenseFileFixtures.cs
index de5fb25fca..1004cefeca 100644
--- a/test/Core.Test/Models/Business/OrganizationLicenseFileFixtures.cs
+++ b/test/Core.Test/Models/Business/OrganizationLicenseFileFixtures.cs
@@ -35,7 +35,7 @@ public static class OrganizationLicenseFileFixtures
if (!LicenseVersions.ContainsKey(licenseVersion))
{
throw new Exception(
- $"Cannot find serialized license version {licenseVersion}. You must add this to OrganizationLicenseFileFixtures when adding a new license version.");
+ $"Cannot find serialized license version {licenseVersion}. You must add this to OrganizationLicenseFileFixtures when adding a new license version.");
}
var json = LicenseVersions.GetValueOrDefault(licenseVersion).Replace("'", "\"");
@@ -76,6 +76,7 @@ public static class OrganizationLicenseFileFixtures
MaxCollections = 2,
UsePolicies = true,
UseSso = true,
+ UseOrganizationDomains = true,
UseKeyConnector = true,
UseScim = true,
UseGroups = true,
diff --git a/test/Core.Test/OrganizationFeatures/OrganizationLicenses/UpdateOrganizationLicenseCommandTests.cs b/test/Core.Test/OrganizationFeatures/OrganizationLicenses/UpdateOrganizationLicenseCommandTests.cs
index 420d330aaa..5ad6abd26a 100644
--- a/test/Core.Test/OrganizationFeatures/OrganizationLicenses/UpdateOrganizationLicenseCommandTests.cs
+++ b/test/Core.Test/OrganizationFeatures/OrganizationLicenses/UpdateOrganizationLicenseCommandTests.cs
@@ -86,7 +86,8 @@ public class UpdateOrganizationLicenseCommandTests
"Id", "MaxStorageGb", "Issued", "Refresh", "Version", "Trial", "LicenseType",
"Hash", "Signature", "SignatureBytes", "InstallationId", "Expires",
"ExpirationWithoutGracePeriod", "Token", "LimitCollectionCreationDeletion",
- "LimitCollectionCreation", "LimitCollectionDeletion", "AllowAdminAccessToAllCollectionItems") &&
+ "LimitCollectionCreation", "LimitCollectionDeletion", "AllowAdminAccessToAllCollectionItems",
+ "UseOrganizationDomains", "UseAdminSponsoredFamilies") &&
// Same property but different name, use explicit mapping
org.ExpirationDate == license.Expires));
}
diff --git a/util/Migrator/DbScripts/2025-05-13-00_AddUseOrganizationDomainsToOrganization.sql b/util/Migrator/DbScripts/2025-05-13-00_AddUseOrganizationDomainsToOrganization.sql
new file mode 100644
index 0000000000..9bc205bfed
--- /dev/null
+++ b/util/Migrator/DbScripts/2025-05-13-00_AddUseOrganizationDomainsToOrganization.sql
@@ -0,0 +1,364 @@
+/* adds new column "UseOrganizationDomains" not nullable with default of 0 */
+
+ALTER TABLE [dbo].[Organization] ADD [UseOrganizationDomains] bit NOT NULL CONSTRAINT [DF_Organization_UseOrganizationDomains] default (0)
+GO
+
+/* add column to Organization_Create*/
+
+CREATE OR ALTER PROCEDURE [dbo].[Organization_Create]
+ @Id UNIQUEIDENTIFIER OUTPUT,
+ @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 INT,
+ @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),
+ @PublicKey VARCHAR(MAX),
+ @PrivateKey VARCHAR(MAX),
+ @TwoFactorProviders NVARCHAR(MAX),
+ @ExpirationDate DATETIME2(7),
+ @CreationDate DATETIME2(7),
+ @RevisionDate DATETIME2(7),
+ @OwnersNotifiedOfAutoscaling DATETIME2(7),
+ @MaxAutoscaleSeats INT,
+ @UseKeyConnector BIT = 0,
+ @UseScim BIT = 0,
+ @UseCustomPermissions BIT = 0,
+ @UseSecretsManager BIT = 0,
+ @Status TINYINT = 0,
+ @UsePasswordManager BIT = 1,
+ @SmSeats INT = null,
+ @SmServiceAccounts INT = null,
+ @MaxAutoscaleSmSeats INT= null,
+ @MaxAutoscaleSmServiceAccounts INT = null,
+ @SecretsManagerBeta BIT = 0,
+ @LimitCollectionCreation BIT = NULL,
+ @LimitCollectionDeletion BIT = NULL,
+ @AllowAdminAccessToAllCollectionItems BIT = 0,
+ @UseRiskInsights BIT = 0,
+ @LimitItemDeletion BIT = 0,
+ @UseOrganizationDomains BIT = 0,
+ @UseAdminSponsoredFamilies BIT = 0
+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],
+ [PublicKey],
+ [PrivateKey],
+ [TwoFactorProviders],
+ [ExpirationDate],
+ [CreationDate],
+ [RevisionDate],
+ [OwnersNotifiedOfAutoscaling],
+ [MaxAutoscaleSeats],
+ [UseKeyConnector],
+ [UseScim],
+ [UseCustomPermissions],
+ [UseSecretsManager],
+ [Status],
+ [UsePasswordManager],
+ [SmSeats],
+ [SmServiceAccounts],
+ [MaxAutoscaleSmSeats],
+ [MaxAutoscaleSmServiceAccounts],
+ [SecretsManagerBeta],
+ [LimitCollectionCreation],
+ [LimitCollectionDeletion],
+ [AllowAdminAccessToAllCollectionItems],
+ [UseRiskInsights],
+ [LimitItemDeletion],
+ [UseOrganizationDomains],
+ [UseAdminSponsoredFamilies]
+ )
+ 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,
+ @PublicKey,
+ @PrivateKey,
+ @TwoFactorProviders,
+ @ExpirationDate,
+ @CreationDate,
+ @RevisionDate,
+ @OwnersNotifiedOfAutoscaling,
+ @MaxAutoscaleSeats,
+ @UseKeyConnector,
+ @UseScim,
+ @UseCustomPermissions,
+ @UseSecretsManager,
+ @Status,
+ @UsePasswordManager,
+ @SmSeats,
+ @SmServiceAccounts,
+ @MaxAutoscaleSmSeats,
+ @MaxAutoscaleSmServiceAccounts,
+ @SecretsManagerBeta,
+ @LimitCollectionCreation,
+ @LimitCollectionDeletion,
+ @AllowAdminAccessToAllCollectionItems,
+ @UseRiskInsights,
+ @LimitItemDeletion,
+ @UseOrganizationDomains,
+ @UseAdminSponsoredFamilies
+ )
+END
+GO
+
+/* add column to Organization_ReadAbilities*/
+CREATE OR ALTER 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],
+ [UseCustomPermissions],
+ [UseSso],
+ [UseKeyConnector],
+ [UseScim],
+ [UseResetPassword],
+ [UsePolicies],
+ [Enabled],
+ [LimitCollectionCreation],
+ [LimitCollectionDeletion],
+ [AllowAdminAccessToAllCollectionItems],
+ [UseRiskInsights],
+ [LimitItemDeletion],
+ [UseOrganizationDomains],
+ [UseAdminSponsoredFamilies]
+ FROM
+ [dbo].[Organization]
+END
+GO
+
+/* add column to Organization_Update*/
+CREATE OR ALTER 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 INT,
+ @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),
+ @PublicKey VARCHAR(MAX),
+ @PrivateKey VARCHAR(MAX),
+ @TwoFactorProviders NVARCHAR(MAX),
+ @ExpirationDate DATETIME2(7),
+ @CreationDate DATETIME2(7),
+ @RevisionDate DATETIME2(7),
+ @OwnersNotifiedOfAutoscaling DATETIME2(7),
+ @MaxAutoscaleSeats INT,
+ @UseKeyConnector BIT = 0,
+ @UseScim BIT = 0,
+ @UseCustomPermissions BIT = 0,
+ @UseSecretsManager BIT = 0,
+ @Status TINYINT = 0,
+ @UsePasswordManager BIT = 1,
+ @SmSeats INT = null,
+ @SmServiceAccounts INT = null,
+ @MaxAutoscaleSmSeats INT = null,
+ @MaxAutoscaleSmServiceAccounts INT = null,
+ @SecretsManagerBeta BIT = 0,
+ @LimitCollectionCreation BIT = null,
+ @LimitCollectionDeletion BIT = null,
+ @AllowAdminAccessToAllCollectionItems BIT = 0,
+ @UseRiskInsights BIT = 0,
+ @LimitItemDeletion BIT = 0,
+ @UseOrganizationDomains BIT = 0,
+ @UseAdminSponsoredFamilies BIT = 0
+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,
+ [PublicKey] = @PublicKey,
+ [PrivateKey] = @PrivateKey,
+ [TwoFactorProviders] = @TwoFactorProviders,
+ [ExpirationDate] = @ExpirationDate,
+ [CreationDate] = @CreationDate,
+ [RevisionDate] = @RevisionDate,
+ [OwnersNotifiedOfAutoscaling] = @OwnersNotifiedOfAutoscaling,
+ [MaxAutoscaleSeats] = @MaxAutoscaleSeats,
+ [UseKeyConnector] = @UseKeyConnector,
+ [UseScim] = @UseScim,
+ [UseCustomPermissions] = @UseCustomPermissions,
+ [UseSecretsManager] = @UseSecretsManager,
+ [Status] = @Status,
+ [UsePasswordManager] = @UsePasswordManager,
+ [SmSeats] = @SmSeats,
+ [SmServiceAccounts] = @SmServiceAccounts,
+ [MaxAutoscaleSmSeats] = @MaxAutoscaleSmSeats,
+ [MaxAutoscaleSmServiceAccounts] = @MaxAutoscaleSmServiceAccounts,
+ [SecretsManagerBeta] = @SecretsManagerBeta,
+ [LimitCollectionCreation] = @LimitCollectionCreation,
+ [LimitCollectionDeletion] = @LimitCollectionDeletion,
+ [AllowAdminAccessToAllCollectionItems] = @AllowAdminAccessToAllCollectionItems,
+ [UseRiskInsights] = @UseRiskInsights,
+ [LimitItemDeletion] = @LimitItemDeletion,
+ [UseOrganizationDomains] = @UseOrganizationDomains,
+ [UseAdminSponsoredFamilies] = @UseAdminSponsoredFamilies
+ WHERE
+ [Id] = @Id
+END
+GO
diff --git a/util/Migrator/DbScripts/2025-05-13-01_AddUseOrganizationDomainsToViews.sql b/util/Migrator/DbScripts/2025-05-13-01_AddUseOrganizationDomainsToViews.sql
new file mode 100644
index 0000000000..2e53bb2473
--- /dev/null
+++ b/util/Migrator/DbScripts/2025-05-13-01_AddUseOrganizationDomainsToViews.sql
@@ -0,0 +1,131 @@
+CREATE OR ALTER VIEW [dbo].[OrganizationUserOrganizationDetailsView]
+AS
+SELECT
+ OU.[UserId],
+ OU.[OrganizationId],
+ OU.[Id] OrganizationUserId,
+ O.[Name],
+ O.[Enabled],
+ O.[PlanType],
+ O.[UsePolicies],
+ O.[UseSso],
+ O.[UseKeyConnector],
+ O.[UseScim],
+ O.[UseGroups],
+ O.[UseDirectory],
+ O.[UseEvents],
+ O.[UseTotp],
+ O.[Use2fa],
+ O.[UseApi],
+ O.[UseResetPassword],
+ O.[SelfHost],
+ O.[UsersGetPremium],
+ O.[UseCustomPermissions],
+ O.[UseSecretsManager],
+ 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],
+ PO.[ProviderId],
+ P.[Name] ProviderName,
+ P.[Type] ProviderType,
+ SS.[Data] SsoConfig,
+ OS.[FriendlyName] FamilySponsorshipFriendlyName,
+ OS.[LastSyncDate] FamilySponsorshipLastSyncDate,
+ OS.[ToDelete] FamilySponsorshipToDelete,
+ OS.[ValidUntil] FamilySponsorshipValidUntil,
+ OU.[AccessSecretsManager],
+ O.[UsePasswordManager],
+ O.[SmSeats],
+ O.[SmServiceAccounts],
+ O.[LimitCollectionCreation],
+ O.[LimitCollectionDeletion],
+ O.[AllowAdminAccessToAllCollectionItems],
+ O.[UseRiskInsights],
+ O.[LimitItemDeletion],
+ O.[UseAdminSponsoredFamilies],
+ O.[UseOrganizationDomains],
+ OS.[IsAdminInitiated]
+FROM
+ [dbo].[OrganizationUser] OU
+LEFT JOIN
+ [dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
+LEFT JOIN
+ [dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
+LEFT JOIN
+ [dbo].[ProviderOrganization] PO ON PO.[OrganizationId] = O.[Id]
+LEFT JOIN
+ [dbo].[Provider] P ON P.[Id] = PO.[ProviderId]
+LEFT JOIN
+ [dbo].[SsoConfig] SS ON SS.[OrganizationId] = OU.[OrganizationId]
+LEFT JOIN
+ [dbo].[OrganizationSponsorship] OS ON OS.[SponsoringOrganizationUserID] = OU.[Id]
+GO
+
+CREATE OR ALTER VIEW [dbo].[ProviderUserProviderOrganizationDetailsView]
+AS
+SELECT
+ PU.[UserId],
+ PO.[OrganizationId],
+ O.[Name],
+ O.[Enabled],
+ O.[UsePolicies],
+ O.[UseSso],
+ O.[UseKeyConnector],
+ O.[UseScim],
+ O.[UseGroups],
+ O.[UseDirectory],
+ O.[UseEvents],
+ O.[UseTotp],
+ O.[Use2fa],
+ O.[UseApi],
+ O.[UseResetPassword],
+ O.[SelfHost],
+ O.[UsersGetPremium],
+ O.[UseCustomPermissions],
+ O.[Seats],
+ O.[MaxCollections],
+ O.[MaxStorageGb],
+ O.[Identifier],
+ PO.[Key],
+ O.[PublicKey],
+ O.[PrivateKey],
+ PU.[Status],
+ PU.[Type],
+ PO.[ProviderId],
+ PU.[Id] ProviderUserId,
+ P.[Name] ProviderName,
+ O.[PlanType],
+ O.[LimitCollectionCreation],
+ O.[LimitCollectionDeletion],
+ O.[AllowAdminAccessToAllCollectionItems],
+ O.[UseRiskInsights],
+ O.[UseAdminSponsoredFamilies],
+ P.[Type] ProviderType,
+ O.[LimitItemDeletion],
+ O.[UseOrganizationDomains]
+FROM
+ [dbo].[ProviderUser] PU
+INNER JOIN
+ [dbo].[ProviderOrganization] PO ON PO.[ProviderId] = PU.[ProviderId]
+INNER JOIN
+ [dbo].[Organization] O ON O.[Id] = PO.[OrganizationId]
+INNER JOIN
+ [dbo].[Provider] P ON P.[Id] = PU.[ProviderId]
+GO
+
+CREATE OR ALTER VIEW [dbo].[OrganizationView]
+AS
+SELECT
+ *
+FROM
+ [dbo].[Organization]
+GO
diff --git a/util/Migrator/DbScripts/2025-05-13-02_AddUseOrganizationDomainsDataMigration.sql b/util/Migrator/DbScripts/2025-05-13-02_AddUseOrganizationDomainsDataMigration.sql
new file mode 100644
index 0000000000..505a667e8f
--- /dev/null
+++ b/util/Migrator/DbScripts/2025-05-13-02_AddUseOrganizationDomainsDataMigration.sql
@@ -0,0 +1,9 @@
+/* update the new column to have the value used in UseSso to preserve existing orgs ability */
+
+UPDATE
+ [dbo].[Organization]
+SET
+ [UseOrganizationDomains] = [UseSso]
+WHERE
+ [UseSso] = 1
+GO
diff --git a/util/MySqlMigrations/HelperScripts/2025-05-13_00_AddUseOrganizationDomains.sql b/util/MySqlMigrations/HelperScripts/2025-05-13_00_AddUseOrganizationDomains.sql
new file mode 100644
index 0000000000..7959d838d3
--- /dev/null
+++ b/util/MySqlMigrations/HelperScripts/2025-05-13_00_AddUseOrganizationDomains.sql
@@ -0,0 +1,3 @@
+UPDATE Organization
+SET UseOrganizationDomains = UseSso
+WHERE UseSso = 1
diff --git a/util/MySqlMigrations/Migrations/20250513151140_AddUseOrganizationDomains.Designer.cs b/util/MySqlMigrations/Migrations/20250513151140_AddUseOrganizationDomains.Designer.cs
new file mode 100644
index 0000000000..29fcb2e342
--- /dev/null
+++ b/util/MySqlMigrations/Migrations/20250513151140_AddUseOrganizationDomains.Designer.cs
@@ -0,0 +1,3115 @@
+//
+using System;
+using Bit.Infrastructure.EntityFramework.Repositories;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Bit.MySqlMigrations.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ [Migration("20250513151140_AddUseOrganizationDomains")]
+ partial class AddUseOrganizationDomains
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.8")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("AllowAdminAccessToAllCollectionItems")
+ .HasColumnType("tinyint(1)")
+ .HasDefaultValue(true);
+
+ b.Property("BillingEmail")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("BusinessAddress1")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("BusinessAddress2")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("BusinessAddress3")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("BusinessCountry")
+ .HasMaxLength(2)
+ .HasColumnType("varchar(2)");
+
+ b.Property("BusinessName")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("BusinessTaxNumber")
+ .HasMaxLength(30)
+ .HasColumnType("varchar(30)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Enabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("ExpirationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Gateway")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("GatewayCustomerId")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("GatewaySubscriptionId")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("Identifier")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("LicenseKey")
+ .HasMaxLength(100)
+ .HasColumnType("varchar(100)");
+
+ b.Property("LimitCollectionCreation")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LimitCollectionDeletion")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("LimitItemDeletion")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("MaxAutoscaleSeats")
+ .HasColumnType("int");
+
+ b.Property("MaxAutoscaleSmSeats")
+ .HasColumnType("int");
+
+ b.Property("MaxAutoscaleSmServiceAccounts")
+ .HasColumnType("int");
+
+ b.Property("MaxCollections")
+ .HasColumnType("smallint");
+
+ b.Property("MaxStorageGb")
+ .HasColumnType("smallint");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("OwnersNotifiedOfAutoscaling")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Plan")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("PlanType")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("PrivateKey")
+ .HasColumnType("longtext");
+
+ b.Property("PublicKey")
+ .HasColumnType("longtext");
+
+ b.Property("ReferenceData")
+ .HasColumnType("longtext");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Seats")
+ .HasColumnType("int");
+
+ b.Property("SelfHost")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("SmSeats")
+ .HasColumnType("int");
+
+ b.Property("SmServiceAccounts")
+ .HasColumnType("int");
+
+ b.Property("Status")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("Storage")
+ .HasColumnType("bigint");
+
+ b.Property("TwoFactorProviders")
+ .HasColumnType("longtext");
+
+ b.Property("Use2fa")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseAdminSponsoredFamilies")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseApi")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseCustomPermissions")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseDirectory")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseEvents")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseGroups")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseKeyConnector")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseOrganizationDomains")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UsePasswordManager")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UsePolicies")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseResetPassword")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseRiskInsights")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseScim")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseSecretsManager")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseSso")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UseTotp")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("UsersGetPremium")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Id", "Enabled")
+ .HasAnnotation("Npgsql:IndexInclude", new[] { "UseTotp" });
+
+ b.ToTable("Organization", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationIntegration", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("Configuration")
+ .HasColumnType("longtext");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("OrganizationId")
+ .HasColumnType("char(36)");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Type")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId")
+ .HasAnnotation("SqlServer:Clustered", false);
+
+ b.HasIndex("OrganizationId", "Type")
+ .IsUnique()
+ .HasAnnotation("SqlServer:Clustered", false);
+
+ b.ToTable("OrganizationIntegration", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.OrganizationIntegrationConfiguration", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("Configuration")
+ .HasColumnType("longtext");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("EventType")
+ .HasColumnType("int");
+
+ b.Property("OrganizationIntegrationId")
+ .HasColumnType("char(36)");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Template")
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationIntegrationId");
+
+ b.ToTable("OrganizationIntegrationConfiguration", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Policy", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Data")
+ .HasColumnType("longtext");
+
+ b.Property("Enabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("OrganizationId")
+ .HasColumnType("char(36)");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Type")
+ .HasColumnType("tinyint unsigned");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId")
+ .HasAnnotation("SqlServer:Clustered", false);
+
+ b.HasIndex("OrganizationId", "Type")
+ .IsUnique()
+ .HasAnnotation("SqlServer:Clustered", false);
+
+ b.ToTable("Policy", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.Provider", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("BillingEmail")
+ .HasColumnType("longtext");
+
+ b.Property("BillingPhone")
+ .HasColumnType("longtext");
+
+ b.Property("BusinessAddress1")
+ .HasColumnType("longtext");
+
+ b.Property("BusinessAddress2")
+ .HasColumnType("longtext");
+
+ b.Property("BusinessAddress3")
+ .HasColumnType("longtext");
+
+ b.Property("BusinessCountry")
+ .HasColumnType("longtext");
+
+ b.Property("BusinessName")
+ .HasColumnType("longtext");
+
+ b.Property("BusinessTaxNumber")
+ .HasColumnType("longtext");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DiscountId")
+ .HasColumnType("longtext");
+
+ b.Property("Enabled")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("Gateway")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("GatewayCustomerId")
+ .HasColumnType("longtext");
+
+ b.Property("GatewaySubscriptionId")
+ .HasColumnType("longtext");
+
+ b.Property("Name")
+ .HasColumnType("longtext");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Status")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("Type")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("UseEvents")
+ .HasColumnType("tinyint(1)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Provider", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderOrganization", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Key")
+ .HasColumnType("longtext");
+
+ b.Property("OrganizationId")
+ .HasColumnType("char(36)");
+
+ b.Property("ProviderId")
+ .HasColumnType("char(36)");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Settings")
+ .HasColumnType("longtext");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("ProviderId");
+
+ b.ToTable("ProviderOrganization", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.AdminConsole.Models.Provider.ProviderUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Email")
+ .HasColumnType("longtext");
+
+ b.Property("Key")
+ .HasColumnType("longtext");
+
+ b.Property("Permissions")
+ .HasColumnType("longtext");
+
+ b.Property("ProviderId")
+ .HasColumnType("char(36)");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Status")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("Type")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProviderId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ProviderUser", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.AuthRequest", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("AccessCode")
+ .HasMaxLength(25)
+ .HasColumnType("varchar(25)");
+
+ b.Property("Approved")
+ .HasColumnType("tinyint(1)");
+
+ b.Property("AuthenticationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Key")
+ .HasColumnType("longtext");
+
+ b.Property("MasterPasswordHash")
+ .HasColumnType("longtext");
+
+ b.Property("OrganizationId")
+ .HasColumnType("char(36)");
+
+ b.Property("PublicKey")
+ .HasColumnType("longtext");
+
+ b.Property("RequestCountryName")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("RequestDeviceIdentifier")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("RequestDeviceType")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("RequestIpAddress")
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("ResponseDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("ResponseDeviceId")
+ .HasColumnType("char(36)");
+
+ b.Property("Type")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("UserId")
+ .HasColumnType("char(36)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("ResponseDeviceId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AuthRequest", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.EmergencyAccess", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("char(36)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("varchar(256)");
+
+ b.Property("GranteeId")
+ .HasColumnType("char(36)");
+
+ b.Property("GrantorId")
+ .HasColumnType("char(36)");
+
+ b.Property("KeyEncrypted")
+ .HasColumnType("longtext");
+
+ b.Property("LastNotificationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("RecoveryInitiatedDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("RevisionDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Status")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("Type")
+ .HasColumnType("tinyint unsigned");
+
+ b.Property("WaitTimeDays")
+ .HasColumnType("int");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GranteeId");
+
+ b.HasIndex("GrantorId");
+
+ b.ToTable("EmergencyAccess", (string)null);
+ });
+
+ modelBuilder.Entity("Bit.Infrastructure.EntityFramework.Auth.Models.Grant", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id"));
+
+ b.Property("ClientId")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("ConsumedDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("CreationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Data")
+ .IsRequired()
+ .HasColumnType("longtext");
+
+ b.Property("Description")
+ .HasMaxLength(200)
+ .HasColumnType("varchar(200)");
+
+ b.Property("ExpirationDate")
+ .HasColumnType("datetime(6)");
+
+ b.Property