diff --git a/commons-error/src/main/resources/messages_en.properties.template b/commons-error/src/main/resources/messages_en.properties.template index 0dd17a14..74239174 100644 --- a/commons-error/src/main/resources/messages_en.properties.template +++ b/commons-error/src/main/resources/messages_en.properties.template @@ -5,6 +5,7 @@ error.empty_apikey=Empty apiKey! error.missing_apikey=The apiKey must be provided in the request! error.invalid_api_name=Invalid api name: {0} error.401_token_invalid=Please acquire a new token or get in contact with the Europeana APIs customer support via api@europeana.eu +error.403_user_not_authorised=The user for which the token was granted for does not have sufficient rights to access the resource #400 error.invalid_param_value=Invalid request. Parameter value not supported or not allowed! {0}:{1} diff --git a/commons-web/src/main/java/eu/europeana/api/commons/config/ErrorConfig.java b/commons-web/src/main/java/eu/europeana/api/commons/config/ErrorConfig.java new file mode 100644 index 00000000..0b49d636 --- /dev/null +++ b/commons-web/src/main/java/eu/europeana/api/commons/config/ErrorConfig.java @@ -0,0 +1,6 @@ +package eu.europeana.api.commons.config; + +public class ErrorConfig { + public static final String TOKEN_INVALID = "error.401_token_invalid"; + public static final String USER_NOT_AUTHORISED= "error.403_user_not_authorised"; +} \ No newline at end of file diff --git a/commons-web/src/main/java/eu/europeana/api/commons/config/ErrorMessage.java b/commons-web/src/main/java/eu/europeana/api/commons/config/ErrorMessage.java new file mode 100644 index 00000000..cb0e03af --- /dev/null +++ b/commons-web/src/main/java/eu/europeana/api/commons/config/ErrorMessage.java @@ -0,0 +1,20 @@ +package eu.europeana.api.commons.config; + +import static eu.europeana.api.commons.config.ErrorConfig.*; +public enum ErrorMessage { + TOKEN_INVALID_401("401_token_invalid", "Token is invalid", TOKEN_INVALID), + USER_NOT_AUTHORISED_403("403_user_not_authorised", "User not authorised to access the resource", USER_NOT_AUTHORISED); + + private final String code; + private final String error; + private final String i18nKey; + + ErrorMessage(String code, String error, String i18nKey) { + this.code = code; + this.error = error; + this.i18nKey = i18nKey; + } + public String getCode() {return code;} + public String getError() {return error; } + public String getI18nKey() {return i18nKey;} +} \ No newline at end of file diff --git a/commons-web/src/main/java/eu/europeana/api/commons/service/authorization/BaseAuthorizationService.java b/commons-web/src/main/java/eu/europeana/api/commons/service/authorization/BaseAuthorizationService.java index 2bef8610..01a1c82b 100644 --- a/commons-web/src/main/java/eu/europeana/api/commons/service/authorization/BaseAuthorizationService.java +++ b/commons-web/src/main/java/eu/europeana/api/commons/service/authorization/BaseAuthorizationService.java @@ -1,5 +1,6 @@ package eu.europeana.api.commons.service.authorization; +import eu.europeana.api.commons.config.ErrorMessage; import eu.europeana.api.commons.exception.EuropeanaClientRegistrationException; import java.util.ArrayList; import java.util.Arrays; @@ -150,8 +151,8 @@ private Authentication authorizeOperation(HttpServletRequest request, String ope //invalid configurations if (getSignatureVerifier() == null) { getLog().error("No signature key configured for verification of JWT Token"); - throw new ApplicationAuthenticationException(I18nConstants.TOKEN_INVALID, - I18nConstants.TOKEN_INVALID,null, HttpStatus.UNAUTHORIZED); + throw new ApplicationAuthenticationException(ErrorMessage.TOKEN_INVALID_401, + null, HttpStatus.UNAUTHORIZED); } List authenticationList; boolean verifyResourceAccess = isResourceAccessVerificationRequired(operation); @@ -160,14 +161,14 @@ private Authentication authorizeOperation(HttpServletRequest request, String ope authenticationList = OAuthUtils.processJwtToken(request, getSignatureVerifier(), getApiName(), verifyResourceAccess); } catch (ApiKeyExtractionException | AuthorizationExtractionException e) { - throw new ApplicationAuthenticationException(I18nConstants.TOKEN_INVALID, - I18nConstants.TOKEN_INVALID,null,HttpStatus.UNAUTHORIZED, e); + throw new ApplicationAuthenticationException(ErrorMessage.TOKEN_INVALID_401, + null,HttpStatus.UNAUTHORIZED, e); } if(authenticationList == null || authenticationList.isEmpty()) { getLog().error("Invalid token or ApiKey, resource access not granted! "); - throw new ApplicationAuthenticationException(I18nConstants.TOKEN_INVALID, - I18nConstants.TOKEN_INVALID,null,HttpStatus.UNAUTHORIZED); + throw new ApplicationAuthenticationException(ErrorMessage.USER_NOT_AUTHORISED_403, + null,HttpStatus.FORBIDDEN); } if(verifyResourceAccess) { @@ -198,8 +199,8 @@ protected Authentication checkPermissions(List authent if(isResourceAccessVerificationRequired(operation)){ //access verification required but getLog().error("No or invalid authorization provided. "); - throw new ApplicationAuthenticationException(I18nConstants.TOKEN_INVALID, - I18nConstants.TOKEN_INVALID,null,HttpStatus.UNAUTHORIZED); + throw new ApplicationAuthenticationException(ErrorMessage.USER_NOT_AUTHORISED_403, + null,HttpStatus.FORBIDDEN); } else { //TODO: return null; @@ -224,8 +225,8 @@ && isOperationAuthorized(operation, authorityList)) { // not authorized getLog().error("Operation not permitted or not GrantedAuthority found for operation:" + operation); - throw new ApplicationAuthenticationException(I18nConstants.TOKEN_INVALID, - I18nConstants.TOKEN_INVALID,null,HttpStatus.UNAUTHORIZED); + throw new ApplicationAuthenticationException(ErrorMessage.USER_NOT_AUTHORISED_403, + null,HttpStatus.FORBIDDEN); } diff --git a/commons-web/src/main/java/eu/europeana/api/commons/web/exception/ApplicationAuthenticationException.java b/commons-web/src/main/java/eu/europeana/api/commons/web/exception/ApplicationAuthenticationException.java index 5ca1aa15..2ed8342e 100644 --- a/commons-web/src/main/java/eu/europeana/api/commons/web/exception/ApplicationAuthenticationException.java +++ b/commons-web/src/main/java/eu/europeana/api/commons/web/exception/ApplicationAuthenticationException.java @@ -1,6 +1,7 @@ package eu.europeana.api.commons.web.exception; +import eu.europeana.api.commons.config.ErrorMessage; import eu.europeana.api.commons.oauth2.model.KeyValidationResult; import org.springframework.http.HttpStatus; @@ -8,10 +9,14 @@ public class ApplicationAuthenticationException extends HttpException{ private static final long serialVersionUID = -8994054535719881829L; + ErrorMessage error; KeyValidationResult result; public KeyValidationResult getResult() { return result; } + public ErrorMessage getError() { + return error; + } public ApplicationAuthenticationException(String message, String i18nKey){ super(message, i18nKey, null, HttpStatus.UNAUTHORIZED); @@ -30,9 +35,23 @@ public ApplicationAuthenticationException(String message, String i18nKey, String super(message, i18nKey, i18nParams, status); } + //Support validation result from keycloak public ApplicationAuthenticationException(String message, String i18nKey, String[] i18nParams,HttpStatus status, Throwable th, KeyValidationResult result) { super(message, i18nKey, i18nParams, status, th); this.result =result; } + + // Support ErrorMessage object + public ApplicationAuthenticationException(ErrorMessage errorMessage, String[] i18nParams, HttpStatus status) { + super(errorMessage.getI18nKey(), errorMessage.getI18nKey(), i18nParams, status); + this.error = errorMessage; + } + + public ApplicationAuthenticationException(ErrorMessage errorMessage, + String[] i18nParams,HttpStatus status, Throwable th) { + super(errorMessage.getI18nKey(), errorMessage.getI18nKey(), i18nParams, status, th); + this.error = errorMessage; + } + } \ No newline at end of file diff --git a/commons-web/src/main/java/eu/europeana/api/commons/web/exception/EuropeanaGlobalExceptionHandler.java b/commons-web/src/main/java/eu/europeana/api/commons/web/exception/EuropeanaGlobalExceptionHandler.java index d113d284..7824958f 100644 --- a/commons-web/src/main/java/eu/europeana/api/commons/web/exception/EuropeanaGlobalExceptionHandler.java +++ b/commons-web/src/main/java/eu/europeana/api/commons/web/exception/EuropeanaGlobalExceptionHandler.java @@ -1,5 +1,6 @@ package eu.europeana.api.commons.web.exception; +import eu.europeana.api.commons.config.ErrorMessage; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -358,13 +359,21 @@ public ResponseEntity clientRegistrationExceptionHand .setCode(result.getValidationError().getCode()) .build()); } else { - return ResponseEntity.status(HttpServletResponse.SC_UNAUTHORIZED) + + int status = ee.getStatus()!=null? ee.getStatus().value(): HttpServletResponse.SC_UNAUTHORIZED; + + ErrorMessage errorDetails = ee.getError(); + String error = (errorDetails != null) ? errorDetails.getError() : "Unauthorized"; + String code = (errorDetails != null) ? errorDetails.getCode() + : StringUtils.substringAfter(ee.getI18nKey(), "."); + + return ResponseEntity.status(status) .headers(createHttpHeaders(request)) .body( new EuropeanaApiErrorResponse.Builder(request, ee, stackTraceEnabled()) - .setStatus(HttpServletResponse.SC_UNAUTHORIZED) - .setError("Unauthorized") + .setStatus(status) + .setError(error) .setMessage(buildResponseMessage(ee, ee.getI18nKey(), ee.getI18nParams())) - .setCode(StringUtils.substringAfter(ee.getI18nKey(), ".")) + .setCode(code) .build()); } }