Merge pull request #13347 from thingsboard/fix/auth-db-error
Handle uncaught auth exceptions
This commit is contained in:
commit
f03f41fb72
@ -47,6 +47,7 @@ import org.springframework.web.filter.ShallowEtagHeaderFilter;
|
||||
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
|
||||
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.security.auth.AuthExceptionHandler;
|
||||
import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider;
|
||||
import org.thingsboard.server.service.security.auth.jwt.JwtTokenAuthenticationProcessingFilter;
|
||||
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider;
|
||||
@ -129,6 +130,9 @@ public class ThingsboardSecurityConfiguration {
|
||||
@Autowired
|
||||
private RateLimitProcessingFilter rateLimitProcessingFilter;
|
||||
|
||||
@Autowired
|
||||
private AuthExceptionHandler authExceptionHandler;
|
||||
|
||||
@Bean
|
||||
protected PayloadSizeFilter payloadSizeFilter() {
|
||||
return new PayloadSizeFilter(maxPayloadSizeConfig);
|
||||
@ -235,7 +239,8 @@ public class ThingsboardSecurityConfiguration {
|
||||
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterBefore(payloadSizeFilter(), UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
.addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterBefore(authExceptionHandler, buildRestLoginProcessingFilter().getClass());
|
||||
if (oauth2Configuration != null) {
|
||||
http.oauth2Login(login -> login
|
||||
.authorizationEndpoint(config -> config
|
||||
|
||||
@ -437,14 +437,7 @@ public abstract class BaseController {
|
||||
} else if (exception instanceof AsyncRequestTimeoutException) {
|
||||
return new ThingsboardException("Request timeout", ThingsboardErrorCode.GENERAL);
|
||||
} else if (exception instanceof DataAccessException) {
|
||||
if (!logControllerErrorStackTrace) { // not to log the error twice
|
||||
log.warn("Database error: {} - {}", exception.getClass().getSimpleName(), ExceptionUtils.getRootCauseMessage(exception));
|
||||
}
|
||||
if (cause instanceof ConstraintViolationException) {
|
||||
return new ThingsboardException(ExceptionUtils.getRootCause(exception).getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||
} else {
|
||||
return new ThingsboardException("Database error", ThingsboardErrorCode.GENERAL);
|
||||
}
|
||||
return new ThingsboardException(exception, ThingsboardErrorCode.DATABASE);
|
||||
} else if (exception instanceof EntityVersionMismatchException) {
|
||||
return new ThingsboardException(exception.getMessage(), exception, ThingsboardErrorCode.VERSION_CONFLICT);
|
||||
}
|
||||
|
||||
@ -21,7 +21,9 @@ import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.HttpStatusCode;
|
||||
@ -139,6 +141,8 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
|
||||
ThingsboardException thingsboardException = (ThingsboardException) exception;
|
||||
if (thingsboardException.getErrorCode() == ThingsboardErrorCode.SUBSCRIPTION_VIOLATION) {
|
||||
handleSubscriptionException((ThingsboardException) exception, response);
|
||||
} else if (thingsboardException.getErrorCode() == ThingsboardErrorCode.DATABASE) {
|
||||
handleDatabaseException(thingsboardException.getCause(), response);
|
||||
} else {
|
||||
handleThingsboardException((ThingsboardException) exception, response);
|
||||
}
|
||||
@ -148,8 +152,10 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
|
||||
handleAccessDeniedException(response);
|
||||
} else if (exception instanceof AuthenticationException) {
|
||||
handleAuthenticationException((AuthenticationException) exception, response);
|
||||
} else if (exception instanceof MaxPayloadSizeExceededException) {
|
||||
} else if (exception instanceof MaxPayloadSizeExceededException) {
|
||||
handleMaxPayloadSizeExceededException(response, (MaxPayloadSizeExceededException) exception);
|
||||
} else if (exception instanceof DataAccessException e) {
|
||||
handleDatabaseException(e, response);
|
||||
} else {
|
||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
JacksonUtil.writeValue(response.getWriter(), ThingsboardErrorResponse.of(exception.getMessage(),
|
||||
@ -201,6 +207,17 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
|
||||
JacksonUtil.fromBytes(((HttpClientErrorException) subscriptionException.getCause()).getResponseBodyAsByteArray(), Object.class));
|
||||
}
|
||||
|
||||
private void handleDatabaseException(Throwable databaseException, HttpServletResponse response) throws IOException {
|
||||
ThingsboardErrorResponse errorResponse;
|
||||
if (databaseException instanceof ConstraintViolationException) {
|
||||
errorResponse = ThingsboardErrorResponse.of(ExceptionUtils.getRootCause(databaseException).getMessage(), ThingsboardErrorCode.BAD_REQUEST_PARAMS, HttpStatus.BAD_REQUEST);
|
||||
} else {
|
||||
log.warn("Database error: {} - {}", databaseException.getClass().getSimpleName(), ExceptionUtils.getRootCauseMessage(databaseException));
|
||||
errorResponse = ThingsboardErrorResponse.of("Database error", ThingsboardErrorCode.DATABASE, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
writeResponse(errorResponse, response);
|
||||
}
|
||||
|
||||
private void handleAccessDeniedException(HttpServletResponse response) throws IOException {
|
||||
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||||
JacksonUtil.writeValue(response.getWriter(),
|
||||
@ -233,4 +250,11 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: refactor this class to use this method instead of boilerplate JacksonUtil.writeValue(response.getWriter(), ...
|
||||
private void writeResponse(ThingsboardErrorResponse errorResponse, HttpServletResponse response) throws IOException {
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setStatus(errorResponse.getStatus());
|
||||
JacksonUtil.writeValue(response.getWriter(), errorResponse);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.security.auth;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.thingsboard.server.exception.ThingsboardErrorResponseHandler;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class AuthExceptionHandler extends OncePerRequestFilter {
|
||||
|
||||
private final ThingsboardErrorResponseHandler errorResponseHandler;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
|
||||
try {
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (AuthenticationException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
errorResponseHandler.handle(e, response);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -31,7 +31,8 @@ public enum ThingsboardErrorCode {
|
||||
TOO_MANY_UPDATES(34),
|
||||
VERSION_CONFLICT(35),
|
||||
SUBSCRIPTION_VIOLATION(40),
|
||||
PASSWORD_VIOLATION(45);
|
||||
PASSWORD_VIOLATION(45),
|
||||
DATABASE(46);
|
||||
|
||||
private int errorCode;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user