|
|
@ -57,6 +57,7 @@ import org.thingsboard.server.dao.user.UserService; |
|
|
import org.thingsboard.server.dao.user.UserServiceImpl; |
|
|
import org.thingsboard.server.dao.user.UserServiceImpl; |
|
|
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; |
|
|
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; |
|
|
import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; |
|
|
import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; |
|
|
|
|
|
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.utils.MiscUtils; |
|
|
import org.thingsboard.server.utils.MiscUtils; |
|
|
import ua_parser.Client; |
|
|
import ua_parser.Client; |
|
|
@ -107,6 +108,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService { |
|
|
securitySettings = new SecuritySettings(); |
|
|
securitySettings = new SecuritySettings(); |
|
|
securitySettings.setPasswordPolicy(new UserPasswordPolicy()); |
|
|
securitySettings.setPasswordPolicy(new UserPasswordPolicy()); |
|
|
securitySettings.getPasswordPolicy().setMinimumLength(6); |
|
|
securitySettings.getPasswordPolicy().setMinimumLength(6); |
|
|
|
|
|
securitySettings.getPasswordPolicy().setMaximumLength(72); |
|
|
} |
|
|
} |
|
|
return securitySettings; |
|
|
return securitySettings; |
|
|
} |
|
|
} |
|
|
@ -131,9 +133,19 @@ 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(tenantId); |
|
|
|
|
|
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()); |
|
|
@ -149,7 +161,6 @@ public class DefaultSystemSecurityService implements SystemSecurityService { |
|
|
|
|
|
|
|
|
userService.resetFailedLoginAttempts(tenantId, userCredentials.getUserId()); |
|
|
userService.resetFailedLoginAttempts(tenantId, userCredentials.getUserId()); |
|
|
|
|
|
|
|
|
SecuritySettings securitySettings = self.getSecuritySettings(tenantId); |
|
|
|
|
|
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())) |
|
|
@ -199,8 +210,31 @@ public class DefaultSystemSecurityService implements SystemSecurityService { |
|
|
SecuritySettings securitySettings = self.getSecuritySettings(tenantId); |
|
|
SecuritySettings securitySettings = self.getSecuritySettings(tenantId); |
|
|
UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy(); |
|
|
UserPasswordPolicy passwordPolicy = securitySettings.getPasswordPolicy(); |
|
|
|
|
|
|
|
|
|
|
|
validatePasswordByPolicy(password, passwordPolicy); |
|
|
|
|
|
|
|
|
|
|
|
if (userCredentials != null && isPositiveInteger(passwordPolicy.getPasswordReuseFrequencyDays())) { |
|
|
|
|
|
long passwordReuseFrequencyTs = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(passwordPolicy.getPasswordReuseFrequencyDays()); |
|
|
|
|
|
JsonNode additionalInfo = userCredentials.getAdditionalInfo(); |
|
|
|
|
|
if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) { |
|
|
|
|
|
JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY); |
|
|
|
|
|
Map<String, String> userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {}); |
|
|
|
|
|
for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) { |
|
|
|
|
|
if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) { |
|
|
|
|
|
throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void validatePasswordByPolicy(String password, UserPasswordPolicy passwordPolicy) { |
|
|
List<Rule> passwordRules = new ArrayList<>(); |
|
|
List<Rule> passwordRules = new ArrayList<>(); |
|
|
passwordRules.add(new LengthRule(passwordPolicy.getMinimumLength(), Integer.MAX_VALUE)); |
|
|
|
|
|
|
|
|
Integer maximumLength = passwordPolicy.getMaximumLength(); |
|
|
|
|
|
Integer minLengthBound = passwordPolicy.getMinimumLength(); |
|
|
|
|
|
int maxLengthBound = (maximumLength != null && maximumLength > passwordPolicy.getMinimumLength()) ? maximumLength : Integer.MAX_VALUE; |
|
|
|
|
|
|
|
|
|
|
|
passwordRules.add(new LengthRule(minLengthBound, maxLengthBound)); |
|
|
if (isPositiveInteger(passwordPolicy.getMinimumUppercaseLetters())) { |
|
|
if (isPositiveInteger(passwordPolicy.getMinimumUppercaseLetters())) { |
|
|
passwordRules.add(new CharacterRule(EnglishCharacterData.UpperCase, passwordPolicy.getMinimumUppercaseLetters())); |
|
|
passwordRules.add(new CharacterRule(EnglishCharacterData.UpperCase, passwordPolicy.getMinimumUppercaseLetters())); |
|
|
} |
|
|
} |
|
|
@ -223,21 +257,6 @@ public class DefaultSystemSecurityService implements SystemSecurityService { |
|
|
String message = String.join("\n", validator.getMessages(result)); |
|
|
String message = String.join("\n", validator.getMessages(result)); |
|
|
throw new DataValidationException(message); |
|
|
throw new DataValidationException(message); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (userCredentials != null && isPositiveInteger(passwordPolicy.getPasswordReuseFrequencyDays())) { |
|
|
|
|
|
long passwordReuseFrequencyTs = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(passwordPolicy.getPasswordReuseFrequencyDays()); |
|
|
|
|
|
JsonNode additionalInfo = userCredentials.getAdditionalInfo(); |
|
|
|
|
|
if (additionalInfo instanceof ObjectNode && additionalInfo.has(UserServiceImpl.USER_PASSWORD_HISTORY)) { |
|
|
|
|
|
JsonNode userPasswordHistoryJson = additionalInfo.get(UserServiceImpl.USER_PASSWORD_HISTORY); |
|
|
|
|
|
Map<String, String> userPasswordHistoryMap = JacksonUtil.convertValue(userPasswordHistoryJson, new TypeReference<>() {}); |
|
|
|
|
|
for (Map.Entry<String, String> entry : userPasswordHistoryMap.entrySet()) { |
|
|
|
|
|
if (encoder.matches(password, entry.getValue()) && Long.parseLong(entry.getKey()) > passwordReuseFrequencyTs) { |
|
|
|
|
|
throw new DataValidationException("Password was already used for the last " + passwordPolicy.getPasswordReuseFrequencyDays() + " days"); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Override |
|
|
@Override |
|
|
|