package io.kaif.web.v1;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.*;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import io.kaif.model.exception.DomainException;
import io.kaif.oauth.InsufficientScopeException;
import io.kaif.oauth.InvalidTokenException;
import io.kaif.oauth.MissingBearerTokenException;
import io.kaif.oauth.OauthErrors;
import io.kaif.oauth.Oauths;
import io.kaif.web.support.AbstractRestExceptionHandler;
/**
* response error json for oauth api, the json format is different from {@link
* io.kaif.web.api.RestExceptionHandler}
*/
@ControllerAdvice(basePackageClasses = V1ExceptionHandler.class)
public class V1ExceptionHandler extends AbstractRestExceptionHandler<V1ErrorResponse> {
public static final String KAIF_API_REAM = "Kaif API";
private static String realm() {
return Oauths.OAUTH_HEADER_NAME + " " + pair(Oauths.WWWAuthHeader.REALM, KAIF_API_REAM);
}
private static String pair(String key, String value) {
return String.format("%s=\"%s\"", key, value);
}
@ExceptionHandler(DomainException.class)
@ResponseBody
public ResponseEntity<V1ErrorResponse> handleDomainException(final DomainException ex,
final WebRequest request) {
final HttpStatus status = HttpStatus.BAD_REQUEST;
String reason = i18n(request, ex.i18nKey(), ex.i18nArgs().toArray());
final V1ErrorResponse errorResponse = new V1ErrorResponse(status.value(),
reason,
ex.getClass().getSimpleName(),
true);
//note that domain exception do not use detail log
logger.warn("{} {}", guessUri(request), ex.getClass().getSimpleName());
return new ResponseEntity<>(errorResponse, status);
}
@ExceptionHandler(MissingBearerTokenException.class)
@ResponseBody
public ResponseEntity<V1ErrorResponse> handleMissingBearerTokenException(final MissingBearerTokenException ex,
final WebRequest request) {
final HttpStatus status = HttpStatus.UNAUTHORIZED;
final V1ErrorResponse errorResponse = createErrorResponse(status,
"missing Bearer token in Authorization header");
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.add(Oauths.HeaderType.WWW_AUTHENTICATE, realm());
return new ResponseEntity<>(errorResponse, responseHeaders, status);
}
@ExceptionHandler(InvalidTokenException.class)
@ResponseBody
public ResponseEntity<V1ErrorResponse> handleInvalidTokenException(final InvalidTokenException ex,
final WebRequest request) {
final HttpStatus status = HttpStatus.UNAUTHORIZED;
final V1ErrorResponse errorResponse = createErrorResponse(status, "invalid access token");
HttpHeaders responseHeaders = new HttpHeaders();
String error = pair(OauthErrors.OAUTH_ERROR, OauthErrors.ResourceResponse.INVALID_TOKEN);
String errorDesc = pair(OauthErrors.OAUTH_ERROR_DESCRIPTION, "invalid token");
responseHeaders.add(Oauths.HeaderType.WWW_AUTHENTICATE,
asList(realm(), error, errorDesc).stream().collect(joining(", ")));
return new ResponseEntity<>(errorResponse, responseHeaders, status);
}
@ExceptionHandler(InsufficientScopeException.class)
@ResponseBody
public ResponseEntity<V1ErrorResponse> handleInsufficientScopeException(final InsufficientScopeException ex,
final WebRequest request) {
final HttpStatus status = HttpStatus.FORBIDDEN;
String title = "require scope " + ex.getRequiredScope();
final V1ErrorResponse errorResponse = createErrorResponse(status, title);
HttpHeaders responseHeaders = new HttpHeaders();
String error = pair(OauthErrors.OAUTH_ERROR, OauthErrors.ResourceResponse.INSUFFICIENT_SCOPE);
String errorDesc = pair(OauthErrors.OAUTH_ERROR_DESCRIPTION, title);
String scope = pair("scope", ex.getRequiredScope().toString());
responseHeaders.add(Oauths.HeaderType.WWW_AUTHENTICATE,
asList(realm(), error, errorDesc, scope).stream().collect(joining(", ")));
return new ResponseEntity<>(errorResponse, responseHeaders, status);
}
@Override
protected V1ErrorResponse createErrorResponse(HttpStatus status, String reason) {
return new V1ErrorResponse(status.value(), reason);
}
}