mirror of
https://github.com/bitwarden/server
synced 2025-12-18 01:03:17 +00:00
* Add PolicyValidatorsRefactor constant to FeatureFlagKeys in Constants.cs * Add Metadata property and ToSavePolicyModel method to PolicyUpdateRequestModel * Refactor PoliciesController to utilize IVNextSavePolicyCommand based on feature flag - Added IFeatureService and IVNextSavePolicyCommand dependencies to PoliciesController. - Updated PutVNext method to conditionally use VNextSavePolicyCommand or SavePolicyCommand based on the PolicyValidatorsRefactor feature flag. - Enhanced unit tests to verify behavior for both enabled and disabled states of the feature flag. * Update public PoliciesController to to utilize IVNextSavePolicyCommand based on feature flag - Introduced IFeatureService and IVNextSavePolicyCommand to manage policy saving based on the PolicyValidatorsRefactor feature flag. - Updated the Put method to conditionally use the new VNextSavePolicyCommand or the legacy SavePolicyCommand. - Added unit tests to validate the behavior of the Put method for both enabled and disabled states of the feature flag. * Refactor VerifyOrganizationDomainCommand to utilize IVNextSavePolicyCommand based on feature flag - Added IFeatureService and IVNextSavePolicyCommand dependencies to VerifyOrganizationDomainCommand. - Updated EnableSingleOrganizationPolicyAsync method to conditionally use VNextSavePolicyCommand or SavePolicyCommand based on the PolicyValidatorsRefactor feature flag. - Enhanced unit tests to validate the behavior when the feature flag is enabled. * Enhance SsoConfigService to utilize IVNextSavePolicyCommand based on feature flag - Added IFeatureService and IVNextSavePolicyCommand dependencies to SsoConfigService. - Updated SaveAsync method to conditionally use VNextSavePolicyCommand or SavePolicyCommand based on the PolicyValidatorsRefactor feature flag. - Added unit tests to validate the behavior when the feature flag is enabled. * Refactor SavePolicyModel to simplify constructor usage by removing EmptyMetadataModel parameter. Update related usages across the codebase to reflect the new constructor overloads. * Update PolicyUpdateRequestModel to make Metadata property nullable for improved null safety
171 lines
7.1 KiB
C#
171 lines
7.1 KiB
C#
// FIXME: Update this file to be null safe and then delete the line below
|
|
#nullable disable
|
|
|
|
using Bit.Core.AdminConsole.Entities;
|
|
using Bit.Core.AdminConsole.Enums;
|
|
using Bit.Core.AdminConsole.Models.Data;
|
|
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
|
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
|
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.Models;
|
|
using Bit.Core.AdminConsole.OrganizationFeatures.Policies.PolicyUpdateEvents.Interfaces;
|
|
using Bit.Core.AdminConsole.Repositories;
|
|
using Bit.Core.Auth.Entities;
|
|
using Bit.Core.Auth.Enums;
|
|
using Bit.Core.Auth.Repositories;
|
|
using Bit.Core.Enums;
|
|
using Bit.Core.Exceptions;
|
|
using Bit.Core.Repositories;
|
|
using Bit.Core.Services;
|
|
|
|
namespace Bit.Core.Auth.Services;
|
|
|
|
public class SsoConfigService : ISsoConfigService
|
|
{
|
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
|
private readonly IPolicyRepository _policyRepository;
|
|
private readonly IOrganizationRepository _organizationRepository;
|
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
private readonly IEventService _eventService;
|
|
private readonly IFeatureService _featureService;
|
|
private readonly ISavePolicyCommand _savePolicyCommand;
|
|
private readonly IVNextSavePolicyCommand _vNextSavePolicyCommand;
|
|
|
|
public SsoConfigService(
|
|
ISsoConfigRepository ssoConfigRepository,
|
|
IPolicyRepository policyRepository,
|
|
IOrganizationRepository organizationRepository,
|
|
IOrganizationUserRepository organizationUserRepository,
|
|
IEventService eventService,
|
|
IFeatureService featureService,
|
|
ISavePolicyCommand savePolicyCommand,
|
|
IVNextSavePolicyCommand vNextSavePolicyCommand)
|
|
{
|
|
_ssoConfigRepository = ssoConfigRepository;
|
|
_policyRepository = policyRepository;
|
|
_organizationRepository = organizationRepository;
|
|
_organizationUserRepository = organizationUserRepository;
|
|
_eventService = eventService;
|
|
_featureService = featureService;
|
|
_savePolicyCommand = savePolicyCommand;
|
|
_vNextSavePolicyCommand = vNextSavePolicyCommand;
|
|
}
|
|
|
|
public async Task SaveAsync(SsoConfig config, Organization organization)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
config.RevisionDate = now;
|
|
if (config.Id == default)
|
|
{
|
|
config.CreationDate = now;
|
|
}
|
|
|
|
var useKeyConnector = config.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector;
|
|
if (useKeyConnector)
|
|
{
|
|
await VerifyDependenciesAsync(config, organization);
|
|
}
|
|
|
|
var oldConfig = await _ssoConfigRepository.GetByOrganizationIdAsync(config.OrganizationId);
|
|
var disabledKeyConnector = oldConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.KeyConnector && !useKeyConnector;
|
|
if (disabledKeyConnector && await AnyOrgUserHasKeyConnectorEnabledAsync(config.OrganizationId))
|
|
{
|
|
throw new BadRequestException("Key Connector cannot be disabled at this moment.");
|
|
}
|
|
|
|
// Automatically enable account recovery, SSO required, and single org policies if trusted device encryption is selected
|
|
if (config.GetData().MemberDecryptionType == MemberDecryptionType.TrustedDeviceEncryption)
|
|
{
|
|
var singleOrgPolicy = new PolicyUpdate
|
|
{
|
|
OrganizationId = config.OrganizationId,
|
|
Type = PolicyType.SingleOrg,
|
|
Enabled = true
|
|
};
|
|
|
|
var resetPasswordPolicy = new PolicyUpdate
|
|
{
|
|
OrganizationId = config.OrganizationId,
|
|
Type = PolicyType.ResetPassword,
|
|
Enabled = true,
|
|
};
|
|
resetPasswordPolicy.SetDataModel(new ResetPasswordDataModel { AutoEnrollEnabled = true });
|
|
|
|
var requireSsoPolicy = new PolicyUpdate
|
|
{
|
|
OrganizationId = config.OrganizationId,
|
|
Type = PolicyType.RequireSso,
|
|
Enabled = true
|
|
};
|
|
|
|
if (_featureService.IsEnabled(FeatureFlagKeys.PolicyValidatorsRefactor))
|
|
{
|
|
var performedBy = new SystemUser(EventSystemUser.Unknown);
|
|
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(singleOrgPolicy, performedBy));
|
|
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(resetPasswordPolicy, performedBy));
|
|
await _vNextSavePolicyCommand.SaveAsync(new SavePolicyModel(requireSsoPolicy, performedBy));
|
|
}
|
|
else
|
|
{
|
|
await _savePolicyCommand.SaveAsync(singleOrgPolicy);
|
|
await _savePolicyCommand.SaveAsync(resetPasswordPolicy);
|
|
await _savePolicyCommand.SaveAsync(requireSsoPolicy);
|
|
}
|
|
}
|
|
|
|
await LogEventsAsync(config, oldConfig);
|
|
await _ssoConfigRepository.UpsertAsync(config);
|
|
}
|
|
|
|
private async Task<bool> AnyOrgUserHasKeyConnectorEnabledAsync(Guid organizationId)
|
|
{
|
|
var userDetails =
|
|
await _organizationUserRepository.GetManyDetailsByOrganizationAsync(organizationId);
|
|
return userDetails.Any(u => u.UsesKeyConnector);
|
|
}
|
|
|
|
private async Task VerifyDependenciesAsync(SsoConfig config, Organization organization)
|
|
{
|
|
if (!organization.UseKeyConnector)
|
|
{
|
|
throw new BadRequestException("Organization cannot use Key Connector.");
|
|
}
|
|
|
|
var singleOrgPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.SingleOrg);
|
|
if (singleOrgPolicy is not { Enabled: true })
|
|
{
|
|
throw new BadRequestException("Key Connector requires the Single Organization policy to be enabled.");
|
|
}
|
|
|
|
var ssoPolicy = await _policyRepository.GetByOrganizationIdTypeAsync(config.OrganizationId, PolicyType.RequireSso);
|
|
if (ssoPolicy is not { Enabled: true })
|
|
{
|
|
throw new BadRequestException("Key Connector requires the Single Sign-On Authentication policy to be enabled.");
|
|
}
|
|
|
|
if (!config.Enabled)
|
|
{
|
|
throw new BadRequestException("You must enable SSO to use Key Connector.");
|
|
}
|
|
}
|
|
|
|
private async Task LogEventsAsync(SsoConfig config, SsoConfig oldConfig)
|
|
{
|
|
var organization = await _organizationRepository.GetByIdAsync(config.OrganizationId);
|
|
if (oldConfig?.Enabled != config.Enabled)
|
|
{
|
|
var e = config.Enabled ? EventType.Organization_EnabledSso : EventType.Organization_DisabledSso;
|
|
await _eventService.LogOrganizationEventAsync(organization, e);
|
|
}
|
|
|
|
var keyConnectorEnabled = config.GetData().MemberDecryptionType == MemberDecryptionType.KeyConnector;
|
|
var oldKeyConnectorEnabled = oldConfig?.GetData()?.MemberDecryptionType == MemberDecryptionType.KeyConnector;
|
|
if (oldKeyConnectorEnabled != keyConnectorEnabled)
|
|
{
|
|
var e = keyConnectorEnabled
|
|
? EventType.Organization_EnabledKeyConnector
|
|
: EventType.Organization_DisabledKeyConnector;
|
|
await _eventService.LogOrganizationEventAsync(organization, e);
|
|
}
|
|
}
|
|
}
|