diff --git a/Service/src/main/java/org/gusdb/wdk/service/filter/CheckLoginFilter.java b/Service/src/main/java/org/gusdb/wdk/service/filter/CheckLoginFilter.java index 3cebf61f7..d7cdf4e37 100644 --- a/Service/src/main/java/org/gusdb/wdk/service/filter/CheckLoginFilter.java +++ b/Service/src/main/java/org/gusdb/wdk/service/filter/CheckLoginFilter.java @@ -7,6 +7,7 @@ import javax.inject.Provider; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.ForbiddenException; import javax.ws.rs.NotAuthorizedException; import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestFilter; @@ -57,9 +58,46 @@ public class CheckLoginFilter implements ContainerRequestFilter, ContainerRespon @Inject protected Provider _grizzlyRequest; + /*************** The following three methods control the default behavior for WDK endpoints ************/ + + /** + * @param path request URL path + * @return true if no authorization is required and no guest + * user should be created for this request, else false + */ + protected boolean isPathToSkip(String path) { + // skip user check for prometheus metrics requests + return SystemService.PROMETHEUS_ENDPOINT_PATH.equals(path); + } + + /** + * A return value of true indicates a valid bearer token is required; the token + * may be a guest depending on the value of isGuestUserAllowed(). If false is + * returned, no token is present, and isGuestUserAllowed() returns true, then a + * new guest token will be generated for this request and returned to the user. + * + * @param path request URL path + * @return true if a valid bearer token is required on the request, else false + */ + protected boolean isValidTokenRequired(String path) { + return false; + } + + /** + * A return value of true indicates a guest user is allowed to access this + * endpoint. If a sent token is absent and isValidTokenRequired() returns false, + * a new guest token will be generated for use on this request. + * + * @param path request URL path + * @return true if guests are allowed to access this endpoint, else false + */ + protected boolean isGuestUserAllowed(String path) { + return true; + } + @Override public void filter(ContainerRequestContext requestContext) throws IOException { - // skip endpoints which do not require a user; prevents guests from being unnecessarily created + // skip endpoints which do not require auth nor a guest user; prevents guests from being unnecessarily created String requestPath = requestContext.getUriInfo().getPath(); if (isPathToSkip(requestPath)) return; @@ -72,20 +110,45 @@ public void filter(ContainerRequestContext requestContext) throws IOException { try { if (rawToken == null) { - // no credentials submitted; automatically create a guest to use on this request - useNewGuest(factory, request, requestContext, requestPath); + // no credentials submitted; check requirements of this path + if (isValidTokenRequired(requestPath)) { + LOG.warn("Did not received bearer token as required for path:" + requestPath); + throw new NotAuthorizedException(Response.status(Status.UNAUTHORIZED).build()); + } + // if allowed, automatically create a guest to use on this request + if (isGuestUserAllowed(requestPath)) { + useNewGuest(factory, request, requestContext, requestPath); + } + else { + // no authentication provided, and guests are disallowed + throw new NotAuthorizedException(Response.status(Status.UNAUTHORIZED).build()); + } } else { try { // validate submitted token ValidatedToken token = factory.validateBearerToken(rawToken); User user = factory.convertToUser(token); - setRequestAttributes(request, token, user); - LOG.info("Validated successfully. Request will be processed for user " + user.getUserId()); + if (!user.isGuest() || isGuestUserAllowed(requestPath)) { + setRequestAttributes(request, token, user); + LOG.info("Validated successfully. Request will be processed for " + + (user.isGuest() ? "guest" : "registered") + " user " + user.getUserId()); + } + else { + // valid guest token submitted, but guests disallowed for this path + throw new ForbiddenException(); + } } catch (ExpiredTokenException e) { - // token is expired; use guest token for now which should inspire them to log back in - useNewGuest(factory, request, requestContext, requestPath); + if (isGuestUserAllowed(requestPath)) { + // token is expired, but a new guest token is allowed to be generated, + // which will hopefully inspire them to log back in + useNewGuest(factory, request, requestContext, requestPath); + } + else { + throw new NotAuthorizedException(Response.status(Status.UNAUTHORIZED) + .entity("Authorization token has expired.").build()); + } } catch (InvalidTokenException e) { // passed token is invalid; throw 401 @@ -94,10 +157,10 @@ public void filter(ContainerRequestContext requestContext) throws IOException { } } } - catch (Exception e) { + catch (WdkModelException | RuntimeException e) { // any other exception is fatal, but log first LOG.error("Unable to authenticate with Authorization header " + rawToken, e); - throw e instanceof RuntimeException ? (RuntimeException)e : new WdkRuntimeException(e); + throw e instanceof WdkModelException ? new WdkRuntimeException(e) : (RuntimeException)e; } } @@ -131,11 +194,6 @@ private String findRawBearerToken(RequestData request, ContainerRequestContext r return cookie == null ? null : cookie.getValue(); } - protected boolean isPathToSkip(String path) { - // skip user check for prometheus metrics requests - return SystemService.PROMETHEUS_ENDPOINT_PATH.equals(path); - } - @Override public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {