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

View File

@ -115,7 +115,7 @@ public class AuthController extends BaseController {
if (!passwordEncoder.matches(currentPassword, userCredentials.getPassword())) {
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())) {
throw new ThingsboardException("New password should be different from existing!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
@ -135,7 +135,7 @@ public class AuthController extends BaseController {
@ResponseBody
public UserPasswordPolicy getUserPasswordPolicy() throws ThingsboardException {
SecuritySettings securitySettings =
checkNotNull(systemSecurityService.getSecuritySettings(TenantId.SYS_TENANT_ID));
checkNotNull(systemSecurityService.getSecuritySettings());
return securitySettings.getPasswordPolicy();
}
@ -237,7 +237,7 @@ public class AuthController extends BaseController {
HttpServletRequest request) throws ThingsboardException {
String activateToken = activateRequest.getActivateToken();
String password = activateRequest.getPassword();
systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, null);
systemSecurityService.validatePassword(password, null);
String encodedPassword = passwordEncoder.encode(password);
UserCredentials credentials = userService.activateUserCredentials(TenantId.SYS_TENANT_ID, activateToken, encodedPassword);
User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId());
@ -274,7 +274,7 @@ public class AuthController extends BaseController {
String password = resetPasswordRequest.getPassword();
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken);
if (userCredentials != null) {
systemSecurityService.validatePassword(TenantId.SYS_TENANT_ID, password, userCredentials);
systemSecurityService.validatePassword(password, userCredentials);
if (passwordEncoder.matches(password, userCredentials.getPassword())) {
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.security.Authority;
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.exception.DataValidationException;
import org.thingsboard.server.dao.user.UserService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.MfaAuthenticationToken;
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.UserPrincipal;
import org.thingsboard.server.service.security.system.SystemSecurityService;
@ -83,6 +87,17 @@ public class RestAuthenticationProvider implements AuthenticationProvider {
if (userPrincipal.getType() == UserPrincipal.Type.USER_NAME) {
String username = userPrincipal.getValue();
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);
if (twoFactorAuthService.isTwoFaEnabled(securityUser.getTenantId(), securityUser.getId())) {
return new MfaAuthenticationToken(securityUser);

View File

@ -15,9 +15,9 @@
*/
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) {
super(msg);

View File

@ -95,9 +95,9 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
@Cacheable(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'")
@Override
public SecuritySettings getSecuritySettings(TenantId tenantId) {
public SecuritySettings getSecuritySettings() {
SecuritySettings securitySettings = null;
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings");
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "securitySettings");
if (adminSettings != null) {
try {
securitySettings = JacksonUtil.convertValue(adminSettings.getJsonValue(), SecuritySettings.class);
@ -115,15 +115,15 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
@CacheEvict(cacheNames = SECURITY_SETTINGS_CACHE, key = "'securitySettings'")
@Override
public SecuritySettings saveSecuritySettings(TenantId tenantId, SecuritySettings securitySettings) {
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(tenantId, "securitySettings");
public SecuritySettings saveSecuritySettings(SecuritySettings securitySettings) {
AdminSettings adminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "securitySettings");
if (adminSettings == null) {
adminSettings = new AdminSettings();
adminSettings.setTenantId(tenantId);
adminSettings.setTenantId(TenantId.SYS_TENANT_ID);
adminSettings.setKey("securitySettings");
}
adminSettings.setJsonValue(JacksonUtil.valueToTree(securitySettings));
AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(tenantId, adminSettings);
AdminSettings savedAdminSettings = adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings);
try {
return JacksonUtil.convertValue(savedAdminSettings.getJsonValue(), SecuritySettings.class);
} catch (Exception e) {
@ -133,19 +133,9 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
@Override
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())) {
int failedLoginAttempts = userService.increaseFailedLoginAttempts(tenantId, userCredentials.getUserId());
SecuritySettings securitySettings = self.getSecuritySettings();
if (securitySettings.getMaxFailedLoginAttempts() != null && securitySettings.getMaxFailedLoginAttempts() > 0) {
if (failedLoginAttempts > securitySettings.getMaxFailedLoginAttempts() && userCredentials.isEnabled()) {
lockAccount(userCredentials.getUserId(), username, securitySettings.getUserLockoutNotificationEmail(), securitySettings.getMaxFailedLoginAttempts());
@ -161,6 +151,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
userService.resetFailedLoginAttempts(tenantId, userCredentials.getUserId());
SecuritySettings securitySettings = self.getSecuritySettings();
if (isPositiveInteger(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) {
if ((userCredentials.getCreatedTime()
+ TimeUnit.DAYS.toMillis(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays()))
@ -188,7 +179,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
if (maxVerificationFailures != null && maxVerificationFailures > 0
&& failedVerificationAttempts >= maxVerificationFailures) {
userService.setUserCredentialsEnabled(TenantId.SYS_TENANT_ID, userId, false);
SecuritySettings securitySettings = self.getSecuritySettings(tenantId);
SecuritySettings securitySettings = self.getSecuritySettings();
lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), maxVerificationFailures);
throw new LockedException("User account was locked due to exceeded 2FA verification attempts");
}
@ -206,8 +197,8 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
}
@Override
public void validatePassword(TenantId tenantId, String password, UserCredentials userCredentials) throws DataValidationException {
SecuritySettings securitySettings = self.getSecuritySettings(tenantId);
public void validatePassword(String password, UserCredentials userCredentials) throws DataValidationException {
SecuritySettings securitySettings = self.getSecuritySettings();
UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy();
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<>();
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.security.UserCredentials;
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.dao.exception.DataValidationException;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -30,15 +31,17 @@ import javax.servlet.http.HttpServletRequest;
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 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);