Merge pull request #9831 from dashevchenko/forcedPasswordPolicyCheck

Moved password policy check before user info rest calls
This commit is contained in:
Andrew Shvayka 2023-12-14 18:39:54 +02:00 committed by GitHub
commit 5946bde4c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 42 additions and 32 deletions

View File

@ -170,7 +170,7 @@ public class AdminController extends BaseController {
@ResponseBody @ResponseBody
public SecuritySettings getSecuritySettings() throws ThingsboardException { public SecuritySettings getSecuritySettings() throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ); accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.READ);
return checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); return checkNotNull(systemSecurityService.getSecuritySettings());
} }
@ApiOperation(value = "Update Security Settings (saveSecuritySettings)", @ApiOperation(value = "Update Security Settings (saveSecuritySettings)",
@ -182,7 +182,7 @@ public class AdminController extends BaseController {
@ApiParam(value = "A JSON value representing the Security Settings.") @ApiParam(value = "A JSON value representing the Security Settings.")
@RequestBody SecuritySettings securitySettings) throws ThingsboardException { @RequestBody SecuritySettings securitySettings) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE); accessControlService.checkPermission(getCurrentUser(), Resource.ADMIN_SETTINGS, Operation.WRITE);
securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(TenantId.SYS_TENANT_ID, securitySettings)); securitySettings = checkNotNull(systemSecurityService.saveSecuritySettings(securitySettings));
return securitySettings; return securitySettings;
} }

View File

@ -115,7 +115,7 @@ public class AuthController extends BaseController {
if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) { if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) {
throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("Current password doesn't match!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
systemSecurityService.validatePassword(securityUser.getTenantId(), newPassword, userCredentials); systemSecurityService.validatePassword(newPassword, userCredentials);
if (passwordEncoder.matches(newPassword, userCredentials.getPassword())) { if (passwordEncoder.matches(newPassword, userCredentials.getPassword())) {
throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }
@ -135,7 +135,7 @@ public class AuthController extends BaseController {
@ResponseBody @ResponseBody
public UserPasswordPolicy getUserPasswordPolicy() throws ThingsboardException { public UserPasswordPolicy getUserPasswordPolicy() throws ThingsboardException {
SecuritySettings securitySettings = SecuritySettings securitySettings =
checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID)); checkNotNull(systemSecurityService.getSecuritySettings());
return securitySettings.getPasswordPolicy(); return securitySettings.getPasswordPolicy();
} }
@ -237,7 +237,7 @@ public class AuthController extends BaseController {
HttpServletRequest request) throws ThingsboardException { HttpServletRequest request) throws ThingsboardException {
String activateToken = activateRequest.getActivateToken(); String activateToken = activateRequest.getActivateToken();
String password = activateRequest.getPassword(); String password = activateRequest.getPassword();
systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, null); systemSecurityService.validatePassword(password, null);
String encodedPassword = passwordEncoder.encode(password); String encodedPassword = passwordEncoder.encode(password);
UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword); UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword);
User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId()); User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId());
@ -274,7 +274,7 @@ public class AuthController extends BaseController {
String password = resetPasswordRequest.getPassword(); String password = resetPasswordRequest.getPassword();
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken); UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken);
if (userCredentials != null) { if (userCredentials != null) {
systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, userCredentials); systemSecurityService.validatePassword(password, userCredentials);
if (passwordEncoder.matches(password, userCredentials.getPassword())) { if (passwordEncoder.matches(password, userCredentials.getPassword())) {
throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
} }

View File

@ -36,11 +36,15 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.model.SecuritySettings;
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.customer.CustomerService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.MfaAuthenticationToken; import org.thingsboard.server.service.security.auth.MfaAuthenticationToken;
import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService; import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
import org.thingsboard.server.service.security.exception.UserPasswordNotValidException;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.UserPrincipal;
import org.thingsboard.server.service.security.system.SystemSecurityService; import org.thingsboard.server.service.security.system.SystemSecurityService;
@ -83,6 +87,17 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
if (userPrincipal.getType() == UserPrincipal.Type.USER_NAME) { if (userPrincipal.getType() == UserPrincipal.Type.USER_NAME) {
String username = userPrincipal.getValue(); String username = userPrincipal.getValue();
String password = (String) authentication.getCredentials(); String password = (String) authentication.getCredentials();
SecuritySettings securitySettings = systemSecurityService.getSecuritySettings();
UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy();
if (Boolean.TRUE.equals(passwordPolicy.getForceUserToResetPasswordIfNotValid())) {
try {
systemSecurityService.validatePasswordByPolicy(password, passwordPolicy);
} catch (DataValidationException e) {
throw new UserPasswordNotValidException("The entered password violates our policies. If this is your real password, please reset it.");
}
}
securityUser = authenticateByUsernameAndPassword(authentication, userPrincipal, username, password); securityUser = authenticateByUsernameAndPassword(authentication, userPrincipal, username, password);
if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser.getId())) { if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser.getId())) {
return new MfaAuthenticationToken(securityUser); return new MfaAuthenticationToken(securityUser);

View File

@ -15,9 +15,9 @@
*/ */
package org.thingsboard.server.service.security.exception; package org.thingsboard.server.service.security.exception;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.authentication.AccountStatusException;
public class UserPasswordNotValidException extends AuthenticationException { public class UserPasswordNotValidException extends AccountStatusException {
public UserPasswordNotValidException(String msg) { public UserPasswordNotValidException(String msg) {
super(msg); super(msg);

View File

@ -95,9 +95,9 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
@Cacheable(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'") @Cacheable(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'")
@Override @Override
public SecuritySettings getSecuritySettings(TenantId tenantId) { public SecuritySettings getSecuritySettings() {
SecuritySettings securitySettings = null; SecuritySettings securitySettings = null;
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings"); AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "securitySettings");
if (adminSettings != null) { if (adminSettings != null) {
try { try {
securitySettings = JacksonUtil.convertValue(adminSettings.getJsonValue(), SecuritySettings.class); securitySettings = JacksonUtil.convertValue(adminSettings.getJsonValue(), SecuritySettings.class);
@ -115,15 +115,15 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
@CacheEvict(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'") @CacheEvict(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'")
@Override @Override
public SecuritySettings saveSecuritySettings(TenantId tenantId, SecuritySettings securitySettings) { public SecuritySettings saveSecuritySettings(SecuritySettings securitySettings) {
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings"); AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "securitySettings");
if (adminSettings == null) { if (adminSettings == null) {
adminSettings = new AdminSettings(); adminSettings = new AdminSettings();
adminSettings.setTenantId(tenantId); adminSettings.setTenantId(TenantId.SYS_TENANT_ID);
adminSettings.setKey("securitySettings"); adminSettings.setKey("securitySettings");
} }
adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings)); adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings));
AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings); AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings);
try { try {
return JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), SecuritySettings.class); return JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), SecuritySettings.class);
} catch (Exception e) { } catch (Exception e) {
@ -133,19 +133,9 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
@Override @Override
public void validateUserCredentials(TenantId tenantId, UserCredentials userCredentials, String username, String password) throws AuthenticationException { public void validateUserCredentials(TenantId tenantId, UserCredentials userCredentials, String username, String password) throws AuthenticationException {
SecuritySettings securitySettings = self.getSecuritySettings(tenantId);
UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy();
if (!tenantId.isSysTenantId() && Boolean.TRUE.equals(passwordPolicy.getForceUserToResetPasswordIfNotValid())) {
try {
validatePasswordByPolicy(password, passwordPolicy);
} catch (DataValidationException e) {
throw new UserPasswordNotValidException("The entered password violates our policies. If this is your real password, please reset it.");
}
}
if (!encoder.matches(password, userCredentials.getPassword())) { if (!encoder.matches(password, userCredentials.getPassword())) {
int failedLoginAttempts = userService.increaseFailedLoginAttempts(tenantId, userCredentials.getUserId()); int failedLoginAttempts = userService.increaseFailedLoginAttempts(tenantId, userCredentials.getUserId());
SecuritySettings securitySettings = self.getSecuritySettings();
if (securitySettings.getMaxFailedLoginAttempts() != null && securitySettings.getMaxFailedLoginAttempts() > 0) { if (securitySettings.getMaxFailedLoginAttempts() != null && securitySettings.getMaxFailedLoginAttempts() > 0) {
if (failedLoginAttempts > securitySettings.getMaxFailedLoginAttempts() && userCredentials.isEnabled()) { if (failedLoginAttempts > securitySettings.getMaxFailedLoginAttempts() && userCredentials.isEnabled()) {
lockAccount(userCredentials.getUserId(), username, securitySettings.getUserLockoutNotificationEmail(), securitySettings.getMaxFailedLoginAttempts()); lockAccount(userCredentials.getUserId(), username, securitySettings.getUserLockoutNotificationEmail(), securitySettings.getMaxFailedLoginAttempts());
@ -161,6 +151,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
userService.resetFailedLoginAttempts(tenantId, userCredentials.getUserId()); userService.resetFailedLoginAttempts(tenantId, userCredentials.getUserId());
SecuritySettings securitySettings = self.getSecuritySettings();
if (isPositiveInteger(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) { if (isPositiveInteger(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) {
if ((userCredentials.getCreatedTime() if ((userCredentials.getCreatedTime()
+ TimeUnit.DAYS.toMillis(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) + TimeUnit.DAYS.toMillis(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays()))
@ -188,7 +179,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
if (maxVerificationFailures != null && maxVerificationFailures > 0 if (maxVerificationFailures != null && maxVerificationFailures > 0
&& failedVerificationAttempts >= maxVerificationFailures) { && failedVerificationAttempts >= maxVerificationFailures) {
userService.setUserCredentialsEnabled(TenantId.SYS_TENANT_ID, userId, false); userService.setUserCredentialsEnabled(TenantId.SYS_TENANT_ID, userId, false);
SecuritySettings securitySettings = self.getSecuritySettings(tenantId); SecuritySettings securitySettings = self.getSecuritySettings();
lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), maxVerificationFailures); lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), maxVerificationFailures);
throw new LockedException("User account was locked due to exceeded 2FA verification attempts"); throw new LockedException("User account was locked due to exceeded 2FA verification attempts");
} }
@ -206,8 +197,8 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
} }
@Override @Override
public void validatePassword(TenantId tenantId, String password, UserCredentials userCredentials) throws DataValidationException { public void validatePassword(String password, UserCredentials userCredentials) throws DataValidationException {
SecuritySettings securitySettings = self.getSecuritySettings(tenantId); SecuritySettings securitySettings = self.getSecuritySettings();
UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy(); UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy();
validatePasswordByPolicy(password, passwordPolicy); validatePasswordByPolicy(password, passwordPolicy);
@ -227,7 +218,8 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
} }
} }
private void validatePasswordByPolicy(String password, UserPasswordPolicy passwordPolicy) { @Override
public void validatePasswordByPolicy(String password, UserPasswordPolicy passwordPolicy) {
List<Rule> passwordRules = new ArrayList<>(); List<Rule> passwordRules = new ArrayList<>();
Integer maximumLength = passwordPolicy.getMaximumLength(); Integer maximumLength = passwordPolicy.getMaximumLength();

View File

@ -22,6 +22,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.UserCredentials; import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.data.security.model.SecuritySettings; import org.thingsboard.server.common.data.security.model.SecuritySettings;
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings; import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -30,15 +31,17 @@ import javax.servlet.http.HttpServletRequest;
public interface SystemSecurityService { public interface SystemSecurityService {
SecuritySettings getSecuritySettings(TenantId tenantId); SecuritySettings getSecuritySettings();
SecuritySettings saveSecuritySettings(TenantId tenantId, SecuritySettings securitySettings); SecuritySettings saveSecuritySettings(SecuritySettings securitySettings);
void validatePasswordByPolicy(String password, UserPasswordPolicy passwordPolicy);
void validateUserCredentials(TenantId tenantId, UserCredentials userCredentials, String username, String password) throws AuthenticationException; void validateUserCredentials(TenantId tenantId, UserCredentials userCredentials, String username, String password) throws AuthenticationException;
void validateTwoFaVerification(SecurityUser securityUser, boolean verificationSuccess, PlatformTwoFaSettings twoFaSettings); void validateTwoFaVerification(SecurityUser securityUser, boolean verificationSuccess, PlatformTwoFaSettings twoFaSettings);
void validatePassword(TenantId tenantId, String password, UserCredentials userCredentials) throws DataValidationException; void validatePassword(String password, UserCredentials userCredentials) throws DataValidationException;
String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest); String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest);