Merge pull request #8089 from dashevchenko/secureRNG
Updated RNG for reset password token to secure one
This commit is contained in:
commit
0ba8336ee9
@ -20,6 +20,7 @@ import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@ -41,11 +42,13 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
|
||||
import org.thingsboard.server.common.data.security.event.UserSessionInvalidationEvent;
|
||||
import org.thingsboard.server.common.data.security.model.SecuritySettings;
|
||||
import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
|
||||
import org.thingsboard.server.common.msg.tools.TbRateLimits;
|
||||
import org.thingsboard.server.dao.audit.AuditLogService;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
|
||||
@ -62,6 +65,8 @@ import org.thingsboard.server.service.security.system.SystemSecurityService;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
@RestController
|
||||
@TbCoreComponent
|
||||
@ -69,6 +74,10 @@ import java.net.URISyntaxException;
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class AuthController extends BaseController {
|
||||
|
||||
@Value("${server.rest.rate_limits.reset_password_per_user:5:3600}")
|
||||
private String defaultLimitsConfiguration;
|
||||
private final ConcurrentMap<UserId, TbRateLimits> resetPasswordRateLimits = new ConcurrentHashMap<>();
|
||||
private final BCryptPasswordEncoder passwordEncoder;
|
||||
private final JwtTokenFactory tokenFactory;
|
||||
private final MailService mailService;
|
||||
@ -211,7 +220,12 @@ public class AuthController extends BaseController {
|
||||
HttpStatus responseStatus;
|
||||
String resetURI = "/login/resetPassword";
|
||||
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken);
|
||||
|
||||
if (userCredentials != null) {
|
||||
TbRateLimits tbRateLimits = getTbRateLimits(userCredentials.getUserId());
|
||||
if (!tbRateLimits.tryConsume()) {
|
||||
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
|
||||
}
|
||||
try {
|
||||
URI location = new URI(resetURI + "?resetToken=" + resetToken);
|
||||
headers.setLocation(location);
|
||||
@ -323,4 +337,9 @@ public class AuthController extends BaseController {
|
||||
throw handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private TbRateLimits getTbRateLimits(UserId userId) {
|
||||
return resetPasswordRateLimits.computeIfAbsent(userId,
|
||||
key -> new TbRateLimits(defaultLimitsConfiguration, true));
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +73,8 @@ server:
|
||||
min_timeout: "${MIN_SERVER_SIDE_RPC_TIMEOUT:5000}"
|
||||
# Default value of the server side RPC timeout.
|
||||
default_timeout: "${DEFAULT_SERVER_SIDE_RPC_TIMEOUT:10000}"
|
||||
rate_limits:
|
||||
reset_password_per_user: "${RESET_PASSWORD_PER_USER_RATE_LIMIT_CONFIGURATION:5:3600}"
|
||||
|
||||
# Application info
|
||||
app:
|
||||
@ -1210,3 +1212,4 @@ management:
|
||||
exposure:
|
||||
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
|
||||
include: '${METRICS_ENDPOINTS_EXPOSE:info}'
|
||||
|
||||
|
||||
@ -18,9 +18,14 @@ package org.thingsboard.server.common.data;
|
||||
import com.google.common.base.Splitter;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.repeat;
|
||||
|
||||
public class StringUtils {
|
||||
public static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
public static final String EMPTY = "";
|
||||
|
||||
public static final int INDEX_NOT_FOUND = -1;
|
||||
@ -180,4 +185,11 @@ public class StringUtils {
|
||||
return RandomStringUtils.randomAlphabetic(count);
|
||||
}
|
||||
|
||||
public static String generateSafeToken(int length) {
|
||||
byte[] bytes = new byte[length];
|
||||
RANDOM.nextBytes(bytes);
|
||||
Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
|
||||
return encoder.encodeToString(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -29,7 +29,6 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
@ -40,7 +39,6 @@ import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||
import org.thingsboard.server.common.data.security.UserSettings;
|
||||
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
|
||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
|
||||
import org.thingsboard.server.dao.exception.IncorrectParameterException;
|
||||
@ -51,6 +49,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.thingsboard.server.common.data.StringUtils.generateSafeToken;
|
||||
import static org.thingsboard.server.dao.service.Validator.validateId;
|
||||
import static org.thingsboard.server.dao.service.Validator.validatePageLink;
|
||||
import static org.thingsboard.server.dao.service.Validator.validateString;
|
||||
@ -126,7 +125,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
|
||||
if (user.getId() == null) {
|
||||
UserCredentials userCredentials = new UserCredentials();
|
||||
userCredentials.setEnabled(false);
|
||||
userCredentials.setActivateToken(StringUtils.randomAlphanumeric(DEFAULT_TOKEN_LENGTH));
|
||||
userCredentials.setActivateToken(generateSafeToken(DEFAULT_TOKEN_LENGTH));
|
||||
userCredentials.setUserId(new UserId(savedUser.getUuidId()));
|
||||
saveUserCredentialsAndPasswordHistory(user.getTenantId(), userCredentials);
|
||||
}
|
||||
@ -192,7 +191,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
|
||||
if (!userCredentials.isEnabled()) {
|
||||
throw new DisabledException(String.format("User credentials not enabled [%s]", email));
|
||||
}
|
||||
userCredentials.setResetToken(StringUtils.randomAlphanumeric(DEFAULT_TOKEN_LENGTH));
|
||||
userCredentials.setResetToken(generateSafeToken(DEFAULT_TOKEN_LENGTH));
|
||||
return saveUserCredentials(tenantId, userCredentials);
|
||||
}
|
||||
|
||||
@ -202,7 +201,7 @@ public class UserServiceImpl extends AbstractEntityService implements UserServic
|
||||
if (!userCredentials.isEnabled()) {
|
||||
throw new IncorrectParameterException("Unable to reset password for inactive user");
|
||||
}
|
||||
userCredentials.setResetToken(StringUtils.randomAlphanumeric(DEFAULT_TOKEN_LENGTH));
|
||||
userCredentials.setResetToken(generateSafeToken(DEFAULT_TOKEN_LENGTH));
|
||||
return saveUserCredentials(tenantId, userCredentials);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user