Browse Source

updated RNG used for reset password token to secure one

pull/8089/head
dashevchenko 4 years ago
parent
commit
c15c4526aa
  1. 25
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  2. 3
      application/src/main/resources/thingsboard.yml
  3. 12
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

25
application/src/main/java/org/thingsboard/server/controller/AuthController.java

@ -18,8 +18,10 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.Getter;
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 +43,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 +66,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 +75,10 @@ import java.net.URISyntaxException;
@Slf4j
@RequiredArgsConstructor
public class AuthController extends BaseController {
@Value("${rate_limits.reset_password_per_user.configuration:5:300}")
@Getter
private String defaultLimitsConfiguration;
private final ConcurrentMap<UserId, TbRateLimits> resetPasswordRateLimits = new ConcurrentHashMap<>();
private final BCryptPasswordEncoder passwordEncoder;
private final JwtTokenFactory tokenFactory;
private final MailService mailService;
@ -211,6 +221,12 @@ public class AuthController extends BaseController {
HttpStatus responseStatus;
String resetURI = "/login/resetPassword";
UserCredentials userCredentials = userService.findUserCredentialsByResetToken(TenantId.SYS_TENANT_ID, resetToken);
TbRateLimits tbRateLimits = getTbRateLimits(userCredentials);
if (!tbRateLimits.tryConsume()) {
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
}
if (userCredentials != null) {
try {
URI location = new URI(resetURI + "?resetToken=" + resetToken);
@ -323,4 +339,13 @@ public class AuthController extends BaseController {
throw handleException(e);
}
}
private TbRateLimits getTbRateLimits(UserCredentials userCredentials) {
TbRateLimits rateLimit = resetPasswordRateLimits.get(userCredentials.getUserId());
if (rateLimit == null) {
rateLimit = new TbRateLimits(defaultLimitsConfiguration, true);
resetPasswordRateLimits.put(userCredentials.getUserId(), rateLimit);
}
return rateLimit;
}
}

3
application/src/main/resources/thingsboard.yml

@ -1209,3 +1209,6 @@ management:
exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}'
rate_limits:
reset_password_per_user:
configuration: "${RESET_PASSWORD_PER_USER_RATE_LIMIT_CONFIGURATION:5:5}"

12
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -47,6 +47,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.PaginatedRemover;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@ -192,10 +194,18 @@ 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());
return saveUserCredentials(tenantId, userCredentials);
}
private String generateSafeToken() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[DEFAULT_TOKEN_LENGTH];
random.nextBytes(bytes);
Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
return encoder.encodeToString(bytes);
}
@Override
public UserCredentials requestExpiredPasswordReset(TenantId tenantId, UserCredentialsId userCredentialsId) {
UserCredentials userCredentials = userCredentialsDao.findById(tenantId, userCredentialsId.getId());

Loading…
Cancel
Save