1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-03 00:52:49 -05:00

[AC-1070] Enforce master password policy on login (#2714)

* [EC-1070] Add API endpoint to retrieve all policies for the current user

The additional API endpoint is required to avoid forcing a full sync call before every login for master password policy enforcement on login.

* [EC-1070] Add MasterPasswordPolicyData model

* [EC-1070] Move PolicyResponseModel to Core project

The response model is used by both the Identity and Api projects.

* [EC-1070] Supply master password polices as a custom identity token response

* [EC-1070] Include master password policies in 2FA token response

* [EC-1070] Add response model to verify-password endpoint that includes master password policies

* [AC-1070] Introduce MasterPasswordPolicyResponseModel

* [AC-1070] Add policy service method to retrieve a user's master password policy

* [AC-1070] User new policy service method

- Update BaseRequestValidator
- Update AccountsController for /verify-password endpoint
- Update VerifyMasterPasswordResponseModel to accept MasterPasswordPolicyData

* [AC-1070] Cleanup new policy service method

- Use User object instead of Guid
- Remove TODO message
- Use `PolicyRepository.GetManyByTypeApplicableToUserIdAsync` instead of filtering locally

* [AC-1070] Cleanup MasterPasswordPolicy models

- Remove default values from both models
- Add missing `RequireLower`
- Fix mismatched properties in `CombineWith` method
- Make properties nullable in response model

* [AC-1070] Remove now un-used GET /policies endpoint

* [AC-1070] Update policy service method to use GetManyByUserIdAsync

* [AC-1070] Ensure existing value is not null before comparison

* [AC-1070] Remove redundant VerifyMasterPasswordResponse model

* [AC-1070] Fix service typo in constructor
This commit is contained in:
Shane Melton
2023-04-17 07:35:47 -07:00
committed by GitHub
parent 972a500745
commit f2fad5513d
13 changed files with 149 additions and 12 deletions

View File

@ -11,6 +11,7 @@ using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Identity;
using Bit.Core.Models.Api;
using Bit.Core.Models.Api.Response;
using Bit.Core.Models.Data.Organizations;
using Bit.Core.Repositories;
using Bit.Core.Services;
@ -38,6 +39,7 @@ public abstract class BaseRequestValidator<T> where T : class
private readonly GlobalSettings _globalSettings;
private readonly IPolicyRepository _policyRepository;
private readonly IUserRepository _userRepository;
private readonly IPolicyService _policyService;
public BaseRequestValidator(
UserManager<User> userManager,
@ -54,7 +56,8 @@ public abstract class BaseRequestValidator<T> where T : class
ICurrentContext currentContext,
GlobalSettings globalSettings,
IPolicyRepository policyRepository,
IUserRepository userRepository)
IUserRepository userRepository,
IPolicyService policyService)
{
_userManager = userManager;
_deviceRepository = deviceRepository;
@ -71,6 +74,7 @@ public abstract class BaseRequestValidator<T> where T : class
_globalSettings = globalSettings;
_policyRepository = policyRepository;
_userRepository = userRepository;
_policyService = policyService;
}
protected async Task ValidateAsync(T context, ValidatedTokenRequest request,
@ -181,6 +185,7 @@ public abstract class BaseRequestValidator<T> where T : class
customResponse.Add("Key", user.Key);
}
customResponse.Add("MasterPasswordPolicy", await GetMasterPasswordPolicy(user));
customResponse.Add("ForcePasswordReset", user.ForcePasswordReset);
customResponse.Add("ResetMasterPassword", string.IsNullOrWhiteSpace(user.MasterPassword));
customResponse.Add("Kdf", (byte)user.Kdf);
@ -239,7 +244,8 @@ public abstract class BaseRequestValidator<T> where T : class
new Dictionary<string, object>
{
{ "TwoFactorProviders", providers.Keys },
{ "TwoFactorProviders2", providers }
{ "TwoFactorProviders2", providers },
{ "MasterPasswordPolicy", await GetMasterPasswordPolicy(user) }
});
if (enabledProviders.Count() == 1 && enabledProviders.First().Key == TwoFactorProviderType.Email)
@ -568,4 +574,18 @@ public abstract class BaseRequestValidator<T> where T : class
var failedLoginCount = user?.FailedLoginCount ?? 0;
return unknownDevice && failedLoginCeiling > 0 && failedLoginCount == failedLoginCeiling;
}
private async Task<MasterPasswordPolicyResponseModel> GetMasterPasswordPolicy(User user)
{
// Check current context/cache to see if user is in any organizations, avoids extra DB call if not
var orgs = (await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id))
.ToList();
if (!orgs.Any())
{
return null;
}
return new MasterPasswordPolicyResponseModel(await _policyService.GetMasterPasswordPolicyForUserAsync(user));
}
}

View File

@ -36,11 +36,12 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
GlobalSettings globalSettings,
IPolicyRepository policyRepository,
ISsoConfigRepository ssoConfigRepository,
IUserRepository userRepository)
IUserRepository userRepository,
IPolicyService policyService)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
applicationCacheService, mailService, logger, currentContext, globalSettings, policyRepository,
userRepository)
userRepository, policyService)
{
_userManager = userManager;
_ssoConfigRepository = ssoConfigRepository;

View File

@ -38,11 +38,12 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
IPolicyRepository policyRepository,
ICaptchaValidationService captchaValidationService,
IAuthRequestRepository authRequestRepository,
IUserRepository userRepository)
IUserRepository userRepository,
IPolicyService policyService)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
applicationCacheService, mailService, logger, currentContext, globalSettings, policyRepository,
userRepository)
userRepository, policyService)
{
_userManager = userManager;
_userService = userService;