From f394254f381f972f3dc1dd2dff564925b02529c3 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Mon, 15 Jan 2024 23:56:25 +0100 Subject: [PATCH] [DEV] select Bearer to be more standard --- .../kar/archidata/catcher/JacksonCatcher.java | 8 +-- .../filter/AuthenticationFilter.java | 51 +++++++++---------- .../archidata/filter/MySecurityContext.java | 18 +++---- src/org/kar/archidata/tools/RESTApi.java | 8 +-- 4 files changed, 41 insertions(+), 44 deletions(-) diff --git a/src/org/kar/archidata/catcher/JacksonCatcher.java b/src/org/kar/archidata/catcher/JacksonCatcher.java index 1f3fd72..7111ceb 100644 --- a/src/org/kar/archidata/catcher/JacksonCatcher.java +++ b/src/org/kar/archidata/catcher/JacksonCatcher.java @@ -11,7 +11,7 @@ import jakarta.ws.rs.ext.ExceptionMapper; public class JacksonCatcher implements ExceptionMapper { private static final Logger LOGGER = LoggerFactory.getLogger(JacksonCatcher.class); - + @Override public Response toResponse(final JsonProcessingException exception) { LOGGER.warn("Catch exception Input data parsing:"); @@ -20,9 +20,9 @@ public class JacksonCatcher implements ExceptionMapper exception.printStackTrace(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(ret).type(MediaType.APPLICATION_JSON).build(); } - + private RestErrorResponse build(final Exception exception) { - return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch Unknown Exception", exception.getMessage()); + return new RestErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, "Catch JSON Exception", exception.getMessage()); } - + } diff --git a/src/org/kar/archidata/filter/AuthenticationFilter.java b/src/org/kar/archidata/filter/AuthenticationFilter.java index 7582de5..4601532 100644 --- a/src/org/kar/archidata/filter/AuthenticationFilter.java +++ b/src/org/kar/archidata/filter/AuthenticationFilter.java @@ -43,8 +43,8 @@ public class AuthenticationFilter implements ContainerRequestFilter { private ResourceInfo resourceInfo; protected final String applicationName; - private static final String AUTHENTICATION_SCHEME = "Yota"; - private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota"; + private static final String AUTHENTICATION_SCHEME = "Bearer"; + private static final String APIKEY = "ApiKey"; public AuthenticationFilter(final String applicationName) { this.applicationName = applicationName; @@ -58,10 +58,10 @@ public class AuthenticationFilter implements ContainerRequestFilter { // Access denied for all if (method.isAnnotationPresent(DenyAll.class)) { LOGGER.debug(" ==> deny all {}", requestContext.getUriInfo().getPath()); - requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build()); + abortWithForbidden(requestContext, "Access blocked !!!"); return; } - + // Access allowed for all if (method.isAnnotationPresent(PermitAll.class)) { // logger.debug(" ==> permit all " + requestContext.getUriInfo().getPath()); @@ -71,28 +71,28 @@ public class AuthenticationFilter implements ContainerRequestFilter { // this is a security guard, all the API must define their access level: if (!method.isAnnotationPresent(RolesAllowed.class)) { LOGGER.error(" ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath()); - requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build()); + abortWithForbidden(requestContext, "Access ILLEGAL !!!"); return; } - + // Get the Authorization header from the request String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); + String apikeyHeader = requestContext.getHeaderString(APIKEY); // logger.debug("authorizationHeader: {}", authorizationHeader); - if (authorizationHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) { + if (authorizationHeader == null && apikeyHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) { final MultivaluedMap quaryparam = requestContext.getUriInfo().getQueryParameters(); for (final Entry> item : quaryparam.entrySet()) { - if (item.getKey().equals(HttpHeaders.AUTHORIZATION)) { - if (!item.getValue().isEmpty()) { - authorizationHeader = item.getValue().get(0); - } - break; + if ((authorizationHeader == null && HttpHeaders.AUTHORIZATION.equals(item.getKey())) && !item.getValue().isEmpty()) { + authorizationHeader = item.getValue().get(0); + } + if ((apikeyHeader == null && APIKEY.equals(item.getKey())) && !item.getValue().isEmpty()) { + apikeyHeader = item.getValue().get(0); } } } // logger.debug("authorizationHeader: {}", authorizationHeader); - final boolean isApplicationToken = isApplicationTokenBasedAuthentication(authorizationHeader); + final boolean isApplicationToken = apikeyHeader != null; final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader); - // Validate the Authorization header data Model "Yota jwt.to.ken" "Zota tokenId:hash(token)" if (!isApplicationToken && !isJwtToken) { LOGGER.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath()); abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath()); @@ -100,7 +100,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { } UserByToken userByToken = null; if (isJwtToken) { - // Extract the token from the Authorization header (Remove "Yota ") + // Extract the token from the Authorization header (Remove "Bearer ") final String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim(); // logger.debug("token: {}", token); try { @@ -116,9 +116,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { return; } } else { - // Extract the token from the Authorization header (Remove "Zota ") - final String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim(); - // logger.debug("token: {}", token); + final String token = apikeyHeader.trim(); try { userByToken = validateToken(token); } catch (final Exception e) { @@ -131,7 +129,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { abortWithUnauthorized(requestContext, "get a NULL application ..."); return; } - + } // create the security context model: final String scheme = requestContext.getUriInfo().getRequestUri().getScheme(); @@ -163,14 +161,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { // The authentication scheme comparison must be case-insensitive return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " "); } - - private boolean isApplicationTokenBasedAuthentication(final String authorizationHeader) { - // Check if the Authorization header is valid - // It must not be null and must be prefixed with "Bearer" plus a whitespace - // The authentication scheme comparison must be case-insensitive - return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_TOKEN_SCHEME.toLowerCase() + " "); - } - + private void abortWithUnauthorized(final ContainerRequestContext requestContext, final String message) { // Abort the filter chain with a 401 status code response @@ -182,6 +173,12 @@ public class AuthenticationFilter implements ContainerRequestFilter { .type(MediaType.APPLICATION_JSON).build()); } + private void abortWithForbidden(final ContainerRequestContext requestContext, final String message) { + final RestErrorResponse ret = new RestErrorResponse(Response.Status.FORBIDDEN, "FORBIDDEN", message); + LOGGER.error("Error UUID={}", ret.uuid); + requestContext.abortWith(Response.status(ret.status).header(HttpHeaders.WWW_AUTHENTICATE, message).entity(ret).type(MediaType.APPLICATION_JSON).build()); + } + protected UserByToken validateToken(final String authorization) throws Exception { LOGGER.info("Must be Override by the application implmentation, otherwise it dose not work"); return null; diff --git a/src/org/kar/archidata/filter/MySecurityContext.java b/src/org/kar/archidata/filter/MySecurityContext.java index b1b9614..de5ec31 100644 --- a/src/org/kar/archidata/filter/MySecurityContext.java +++ b/src/org/kar/archidata/filter/MySecurityContext.java @@ -8,20 +8,20 @@ import jakarta.ws.rs.core.SecurityContext; // https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/ class MySecurityContext implements SecurityContext { - + private final GenericContext contextPrincipale; private final String sheme; - + public MySecurityContext(final UserByToken userByToken, final String sheme) { this.contextPrincipale = new GenericContext(userByToken); this.sheme = sheme; } - + @Override public Principal getUserPrincipal() { return this.contextPrincipale; } - + @Override public boolean isUserInRole(final String role) { if (this.contextPrincipale.userByToken != null) { @@ -32,18 +32,18 @@ class MySecurityContext implements SecurityContext { } return false; } - + @Override public boolean isSecure() { - return this.sheme.equalsIgnoreCase("https"); + return "https".equalsIgnoreCase(this.sheme); } - + @Override public String getAuthenticationScheme() { if (this.contextPrincipale.userByToken != null) { - return "Zota"; + return "Bearer"; } return null; } - + } \ No newline at end of file diff --git a/src/org/kar/archidata/tools/RESTApi.java b/src/org/kar/archidata/tools/RESTApi.java index a7f84be..2ada911 100644 --- a/src/org/kar/archidata/tools/RESTApi.java +++ b/src/org/kar/archidata/tools/RESTApi.java @@ -40,7 +40,7 @@ public class RESTApi { final HttpClient client = HttpClient.newHttpClient(); Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset)); if (this.token != null) { - requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); + requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + this.token); } final HttpRequest request = requestBuilding.GET().build(); final HttpResponse httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); @@ -111,7 +111,7 @@ public class RESTApi { LOGGER.trace("call {}: {}", model, URI.create(this.baseUrl + urlOffset)); LOGGER.trace("DATA: {}", body); if (this.token != null) { - requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); + requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + this.token); } if (body == null) { body = ""; @@ -143,7 +143,7 @@ public class RESTApi { String body = null; Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset)); if (this.token != null) { - requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); + requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + this.token); } if (data == null) { body = ""; @@ -171,7 +171,7 @@ public class RESTApi { final HttpClient client = HttpClient.newHttpClient(); Builder requestBuilding = HttpRequest.newBuilder().version(Version.HTTP_1_1).uri(URI.create(this.baseUrl + urlOffset)); if (this.token != null) { - requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); + requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Bearer " + this.token); } final HttpRequest request = requestBuilding.DELETE().build(); final HttpResponse httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());