diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/IPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/IPolicyDefinition.cs
index e1c759ae5..5b1229ff7 100644
--- a/src/Core/AdminConsole/OrganizationFeatures/Policies/IPolicyDefinition.cs
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/IPolicyDefinition.cs
@@ -15,7 +15,7 @@ public interface IPolicyDefinition
///
/// PolicyTypes that must be enabled before this policy can be enabled, if any.
///
- public IEnumerable RequiredPolicies { get; }
+ public IEnumerable RequiredPolicies => [];
///
/// Validates a policy before saving it.
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/ActivateAutofillPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/ActivateAutofillPolicyDefinition.cs
new file mode 100644
index 000000000..5044b1c3d
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/ActivateAutofillPolicyDefinition.cs
@@ -0,0 +1,8 @@
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class ActivateAutofillPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.ActivateAutofill;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/AutomaticAppLogInPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/AutomaticAppLogInPolicyDefinition.cs
new file mode 100644
index 000000000..ed8ddd34b
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/AutomaticAppLogInPolicyDefinition.cs
@@ -0,0 +1,8 @@
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class AutomaticAppLogInPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.AutomaticAppLogIn;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/DisablePersonalVaultExportPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/DisablePersonalVaultExportPolicyDefinition.cs
new file mode 100644
index 000000000..bd758aa32
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/DisablePersonalVaultExportPolicyDefinition.cs
@@ -0,0 +1,8 @@
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class DisablePersonalVaultExportPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.DisablePersonalVaultExport;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/DisableSendPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/DisableSendPolicyDefinition.cs
new file mode 100644
index 000000000..bd58aca06
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/DisableSendPolicyDefinition.cs
@@ -0,0 +1,10 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class DisableSendPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.DisableSend;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/MasterPasswordPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/MasterPasswordPolicyDefinition.cs
new file mode 100644
index 000000000..380b82777
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/MasterPasswordPolicyDefinition.cs
@@ -0,0 +1,10 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class MasterPasswordPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.MasterPassword;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/MaximumVaultTimeoutPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/MaximumVaultTimeoutPolicyDefinition.cs
new file mode 100644
index 000000000..5b851c3e1
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/MaximumVaultTimeoutPolicyDefinition.cs
@@ -0,0 +1,9 @@
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class MaximumVaultTimeoutPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.MaximumVaultTimeout;
+ public IEnumerable RequiredPolicies => [PolicyType.SingleOrg];
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PasswordGeneratorPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PasswordGeneratorPolicyDefinition.cs
new file mode 100644
index 000000000..47200293f
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PasswordGeneratorPolicyDefinition.cs
@@ -0,0 +1,10 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class PasswordGeneratorPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.PasswordGenerator;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PersonalOwnershipPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PersonalOwnershipPolicyDefinition.cs
new file mode 100644
index 000000000..b874f16cf
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/PersonalOwnershipPolicyDefinition.cs
@@ -0,0 +1,10 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class PersonalOwnershipPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.PersonalOwnership;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/RequireSsoPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/RequireSsoPolicyDefinition.cs
new file mode 100644
index 000000000..02bfd7d4c
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/RequireSsoPolicyDefinition.cs
@@ -0,0 +1,11 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class RequireSsoPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.RequireSso;
+ public IEnumerable RequiredPolicies => [PolicyType.SingleOrg];
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/ResetPasswordPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/ResetPasswordPolicyDefinition.cs
new file mode 100644
index 000000000..7f900e638
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/ResetPasswordPolicyDefinition.cs
@@ -0,0 +1,36 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Entities;
+using Bit.Core.AdminConsole.Enums;
+using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
+using Bit.Core.Auth.Enums;
+using Bit.Core.Auth.Repositories;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class ResetPasswordPolicyDefinition : IPolicyDefinition
+{
+ private readonly ISsoConfigRepository _ssoConfigRepository;
+ public PolicyType Type => PolicyType.ResetPassword;
+ public IEnumerable RequiredPolicies => [PolicyType.SingleOrg];
+
+ ResetPasswordPolicyDefinition(ISsoConfigRepository ssoConfigRepository)
+ {
+ _ssoConfigRepository = ssoConfigRepository;
+ }
+
+ public async Task ValidateAsync(Policy? currentPolicy, Policy modifiedPolicy)
+ {
+ if (modifiedPolicy is not { Enabled:true } ||
+ modifiedPolicy.GetDataModel()?.AutoEnrollEnabled == false)
+ {
+ var ssoConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(modifiedPolicy.OrganizationId);
+ if (ssoConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
+ {
+ return "Trusted device encryption is on and requires this policy.";
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/SendOptionsPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/SendOptionsPolicyDefinition.cs
new file mode 100644
index 000000000..73cd247bb
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/SendOptionsPolicyDefinition.cs
@@ -0,0 +1,10 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Enums;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class SendOptionsPolicyDefinition : IPolicyDefinition
+{
+ public PolicyType Type => PolicyType.SendOptions;
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/SingleOrgPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/SingleOrgPolicyDefinition.cs
index a77926ede..289638875 100644
--- a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/SingleOrgPolicyDefinition.cs
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/SingleOrgPolicyDefinition.cs
@@ -15,7 +15,6 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
public class SingleOrgPolicyDefinition : IPolicyDefinition
{
public PolicyType Type => PolicyType.SingleOrg;
- public IEnumerable RequiredPolicies => Array.Empty();
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IMailService _mailService;
@@ -39,7 +38,7 @@ public class SingleOrgPolicyDefinition : IPolicyDefinition
public async Task OnSaveSideEffectsAsync(Policy? currentPolicy, Policy modifiedPolicy)
{
- if (currentPolicy is null or { Enabled: false } && modifiedPolicy is { Enabled: true })
+ if (currentPolicy is not { Enabled: true } && modifiedPolicy is { Enabled: true })
{
await RemoveNonCompliantUsersAsync(modifiedPolicy.OrganizationId);
}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/TwoFactorAuthenticationPolicyDefinition.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/TwoFactorAuthenticationPolicyDefinition.cs
new file mode 100644
index 000000000..59399aaf0
--- /dev/null
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/Implementations/TwoFactorAuthenticationPolicyDefinition.cs
@@ -0,0 +1,82 @@
+#nullable enable
+
+using Bit.Core.AdminConsole.Entities;
+using Bit.Core.AdminConsole.Enums;
+using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
+using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
+using Bit.Core.Context;
+using Bit.Core.Enums;
+using Bit.Core.Exceptions;
+using Bit.Core.Repositories;
+using Bit.Core.Services;
+
+namespace Bit.Core.AdminConsole.OrganizationFeatures.Policies.Implementations;
+
+public class TwoFactorAuthenticationPolicyDefinition : IPolicyDefinition
+{
+ private readonly IOrganizationUserRepository _organizationUserRepository;
+ private readonly IMailService _mailService;
+ private readonly IOrganizationRepository _organizationRepository;
+ private readonly ICurrentContext _currentContext;
+ private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
+ private readonly IRemoveOrganizationUserCommand _removeOrganizationUserCommand;
+
+ public PolicyType Type => PolicyType.TwoFactorAuthentication;
+
+ public TwoFactorAuthenticationPolicyDefinition(
+ IOrganizationUserRepository organizationUserRepository,
+ IMailService mailService,
+ IOrganizationRepository organizationRepository,
+ ICurrentContext currentContext,
+ ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
+ IRemoveOrganizationUserCommand removeOrganizationUserCommand)
+ {
+ _organizationUserRepository = organizationUserRepository;
+ _mailService = mailService;
+ _organizationRepository = organizationRepository;
+ _currentContext = currentContext;
+ _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
+ _removeOrganizationUserCommand = removeOrganizationUserCommand;
+ }
+
+ public async Task OnSaveSideEffectsAsync(Policy? currentPolicy, Policy modifiedPolicy)
+ {
+ if (currentPolicy is not { Enabled: true } && modifiedPolicy is { Enabled: true })
+ {
+ await RemoveNonCompliantUsersAsync(modifiedPolicy.OrganizationId);
+ }
+ }
+
+ private async Task RemoveNonCompliantUsersAsync(Guid organizationId)
+ {
+ var org = await _organizationRepository.GetByIdAsync(organizationId);
+ var savingUserId = _currentContext.UserId;
+
+ var orgUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
+ var organizationUsersTwoFactorEnabled = await _twoFactorIsEnabledQuery.TwoFactorIsEnabledAsync(orgUsers);
+ var removableOrgUsers = orgUsers.Where(ou =>
+ ou.Status != OrganizationUserStatusType.Invited && ou.Status != OrganizationUserStatusType.Revoked &&
+ ou.Type != OrganizationUserType.Owner && ou.Type != OrganizationUserType.Admin &&
+ ou.UserId != savingUserId);
+
+ // Reorder by HasMasterPassword to prioritize checking users without a master if they have 2FA enabled
+ foreach (var orgUser in removableOrgUsers.OrderBy(ou => ou.HasMasterPassword))
+ {
+ var userTwoFactorEnabled = organizationUsersTwoFactorEnabled.FirstOrDefault(u => u.user.Id == orgUser.Id)
+ .twoFactorIsEnabled;
+ if (!userTwoFactorEnabled)
+ {
+ if (!orgUser.HasMasterPassword)
+ {
+ throw new BadRequestException(
+ "Policy could not be enabled. Non-compliant members will lose access to their accounts. Identify members without two-step login from the policies column in the members page.");
+ }
+
+ await _removeOrganizationUserCommand.RemoveUserAsync(organizationId, orgUser.Id,
+ savingUserId);
+ await _mailService.SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(
+ org!.DisplayName(), orgUser.Email);
+ }
+ }
+ }
+}
diff --git a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs
index 5c3ec7f03..fe02bf25e 100644
--- a/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs
+++ b/src/Core/AdminConsole/OrganizationFeatures/Policies/PolicyServiceCollectionExtensions.cs
@@ -11,6 +11,19 @@ public static class PolicyServiceCollectionExtensions
{
services.AddScoped();
services.AddScoped();
+
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
}
}