Merge branch 'feature/acl-unauthorized-response' into 'release'
Correcting the error response returned by the AclFilter to match the ErrorDTO... Correcting the error response returned by the AclFilter to match the ErrorDTO returned from controller functions This ensures consistent responses from different parts of our application. The client can then rely on the server to provide a constistent response structure. See merge request theappsmith/internal-tools-server!194
This commit is contained in:
commit
b28073cd4b
|
|
@ -30,7 +30,7 @@ public enum AppsmithError {
|
|||
JSON_PROCESSING_ERROR(400, 4022, "Json processing error with error {0}"),
|
||||
INVALID_CREDENTIALS(200, 4023, "Invalid credentials provided. Did you input the credentials correctly?"),
|
||||
DUPLICATE_KEY(409, 4024, "Duplicate key error"),
|
||||
UNAUTHORIZED_ACCESS(401, 4025, "Unauthorized access"),
|
||||
UNAUTHORIZED_ACCESS(403, 4025, "Unauthorized access"),
|
||||
INVALID_DATASOURCE_NAME(400, 4026, "Datasource name is invalid"),
|
||||
NO_RESOURCE_FOUND(404, 4027, "Unable to find {0} with id {1}"),
|
||||
GENERIC_BAD_REQUEST(400, 4028, "Bad Request: {0}"),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.segment.analytics.Analytics;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
|
@ -14,6 +15,7 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
import org.springframework.web.server.ServerWebInputException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
|
||||
/**
|
||||
* This class catches all the Exceptions and formats them into a proper ResponseDTO<ErrorDTO> object before
|
||||
* sending it to the client.
|
||||
|
|
@ -51,43 +53,57 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public Mono<ResponseDTO<ErrorDTO>> catchDuplicateKeyException(org.springframework.dao.DuplicateKeyException e, ServerWebExchange exchange) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
AppsmithError appsmithError = AppsmithError.DUPLICATE_KEY;
|
||||
exchange.getResponse().setStatusCode(HttpStatus.resolve(appsmithError.getHttpErrorCode()));
|
||||
log.error("", e);
|
||||
rollbar.log(e);
|
||||
return Mono.just(new ResponseDTO<>(HttpStatus.BAD_REQUEST.value(), new ErrorDTO(AppsmithError.DUPLICATE_KEY.getAppErrorCode(),
|
||||
AppsmithError.DUPLICATE_KEY.getMessage(e.getMessage()))));
|
||||
return Mono.just(new ResponseDTO<>(appsmithError.getHttpErrorCode(), new ErrorDTO(appsmithError.getAppErrorCode(),
|
||||
appsmithError.getMessage(e.getMessage()))));
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public Mono<ResponseDTO<ErrorDTO>> catchTimeoutException(java.util.concurrent.TimeoutException e, ServerWebExchange exchange) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.GATEWAY_TIMEOUT);
|
||||
AppsmithError appsmithError = AppsmithError.PLUGIN_EXECUTION_TIMEOUT;
|
||||
exchange.getResponse().setStatusCode(HttpStatus.resolve(appsmithError.getHttpErrorCode()));
|
||||
log.error("", e);
|
||||
rollbar.log(e);
|
||||
return Mono.just(new ResponseDTO<>(HttpStatus.GATEWAY_TIMEOUT.value(), new ErrorDTO(AppsmithError.PLUGIN_EXECUTION_TIMEOUT.getHttpErrorCode(),
|
||||
AppsmithError.PLUGIN_EXECUTION_TIMEOUT.getMessage())));
|
||||
return Mono.just(new ResponseDTO<>(appsmithError.getHttpErrorCode(), new ErrorDTO(appsmithError.getAppErrorCode(),
|
||||
appsmithError.getMessage())));
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public Mono<ResponseDTO<ErrorDTO>> catchServerWebInputException(ServerWebInputException e, ServerWebExchange exchange) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
|
||||
AppsmithError appsmithError = AppsmithError.GENERIC_BAD_REQUEST;
|
||||
exchange.getResponse().setStatusCode(HttpStatus.resolve(appsmithError.getHttpErrorCode()));
|
||||
log.error("", e);
|
||||
rollbar.log(e);
|
||||
return Mono.just(new ResponseDTO<>(HttpStatus.BAD_REQUEST.value(), new ErrorDTO(AppsmithError.GENERIC_BAD_REQUEST.getHttpErrorCode(),
|
||||
AppsmithError.GENERIC_BAD_REQUEST.getMessage(e.getReason()))));
|
||||
return Mono.just(new ResponseDTO<>(appsmithError.getHttpErrorCode(), new ErrorDTO(appsmithError.getAppErrorCode(),
|
||||
appsmithError.getMessage(e.getReason()))));
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public Mono<ResponseDTO<ErrorDTO>> catchPluginException(AppsmithPluginException e, ServerWebExchange exchange) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
AppsmithError appsmithError = AppsmithError.INTERNAL_SERVER_ERROR;
|
||||
exchange.getResponse().setStatusCode(HttpStatus.resolve(appsmithError.getHttpErrorCode()));
|
||||
log.error("", e);
|
||||
rollbar.log(e);
|
||||
return Mono.just(new ResponseDTO<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), new ErrorDTO(AppsmithError.INTERNAL_SERVER_ERROR.getHttpErrorCode(),
|
||||
return Mono.just(new ResponseDTO<>(appsmithError.getHttpErrorCode(), new ErrorDTO(appsmithError.getAppErrorCode(),
|
||||
e.getLocalizedMessage())));
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public Mono<ResponseDTO<ErrorDTO>> catchAccessDeniedException(AccessDeniedException e, ServerWebExchange exchange) {
|
||||
AppsmithError appsmithError = AppsmithError.UNAUTHORIZED_ACCESS;
|
||||
exchange.getResponse().setStatusCode(HttpStatus.resolve(appsmithError.getHttpErrorCode()));
|
||||
log.error("", e);
|
||||
rollbar.log(e);
|
||||
return Mono.just(new ResponseDTO<>(appsmithError.getHttpErrorCode(), new ErrorDTO(appsmithError.getAppErrorCode(),
|
||||
appsmithError.getMessage())));
|
||||
}
|
||||
/**
|
||||
* This function catches the generic Exception class and is meant to be a catch all to ensure that we don't leak
|
||||
* any information to the client. Ideally, the function #catchAppsmithException should be used
|
||||
|
|
@ -99,10 +115,11 @@ public class GlobalExceptionHandler {
|
|||
@ExceptionHandler
|
||||
@ResponseBody
|
||||
public Mono<ResponseDTO<ErrorDTO>> catchException(Exception e, ServerWebExchange exchange) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
AppsmithError appsmithError = AppsmithError.INTERNAL_SERVER_ERROR;
|
||||
exchange.getResponse().setStatusCode(HttpStatus.resolve(appsmithError.getHttpErrorCode()));
|
||||
log.error("", e);
|
||||
rollbar.log(e);
|
||||
return Mono.just(new ResponseDTO<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), new ErrorDTO(AppsmithError.INTERNAL_SERVER_ERROR.getHttpErrorCode(),
|
||||
AppsmithError.INTERNAL_SERVER_ERROR.getMessage())));
|
||||
return Mono.just(new ResponseDTO<>(appsmithError.getHttpErrorCode(), new ErrorDTO(appsmithError.getAppErrorCode(),
|
||||
appsmithError.getMessage())));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,17 @@ package com.appsmith.server.filters;
|
|||
|
||||
import com.appsmith.server.acl.AclService;
|
||||
import com.appsmith.server.acl.OpaResponse;
|
||||
import com.appsmith.server.dtos.ResponseDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import com.appsmith.server.exceptions.ErrorDTO;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
|
@ -19,10 +25,12 @@ import reactor.core.publisher.Mono;
|
|||
public class AclFilter implements WebFilter {
|
||||
|
||||
final AclService aclService;
|
||||
final ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
public AclFilter(AclService aclService) {
|
||||
public AclFilter(AclService aclService, ObjectMapper objectMapper) {
|
||||
this.aclService = aclService;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -62,8 +70,28 @@ public class AclFilter implements WebFilter {
|
|||
log.debug("Got ACL response: {}", acl);
|
||||
return acl;
|
||||
})
|
||||
.filter(acl -> acl.getResult())
|
||||
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_ACCESS)))
|
||||
.flatMap(acl -> chain.filter(exchange));
|
||||
.flatMap(acl -> {
|
||||
if (acl != null && acl.getResult()) {
|
||||
// Acl returned true. Continue with the filter chain
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// The Acl response is false. Return unauthorized exception to the client
|
||||
// We construct the error response JSON here because throwing an exception here doesn't get caught
|
||||
// in the {@see GlobalExceptionHandler}.
|
||||
AppsmithError error = AppsmithError.UNAUTHORIZED_ACCESS;
|
||||
exchange.getResponse().setStatusCode(HttpStatus.resolve(error.getHttpErrorCode()));
|
||||
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
|
||||
try {
|
||||
ResponseDTO<ErrorDTO> responseBody = new ResponseDTO<>(error.getHttpErrorCode(), new ErrorDTO(error.getAppErrorCode(),
|
||||
error.getMessage()));
|
||||
String responseStr = objectMapper.writeValueAsString(responseBody);
|
||||
DataBuffer buffer = exchange.getResponse().bufferFactory().allocateBuffer().write(responseStr.getBytes());
|
||||
return exchange.getResponse().writeWith(Mono.just(buffer));
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Exception caught while serializing JSON in AclFilter. Cause: ", e);
|
||||
return exchange.getResponse().writeWith(Mono.empty());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user