From 57d32b648164c736fe16788dc446c29e126fc73b Mon Sep 17 00:00:00 2001 From: Jad El-khoury <121724599+Jad-el-khoury@users.noreply.github.com> Date: Mon, 31 Jul 2023 13:08:21 +0200 Subject: [PATCH] Introduce capability to set the servletUri to be used by the OAuthConfiguration (#375) * * Introducing log traces. * Introducing capability to set the servletUri to be used by the OAuthConfiguration * plus change log * newline fix * a change from "error" to "trace" in one of the logs. * Changed log level for some logs. * javadocs fix * Provide a default implementation for getServletUri() --- CHANGELOG.md | 1 + server/oauth-core/pom.xml | 6 ++ .../lyo/server/oauth/core/OAuthRequest.java | 8 +++ .../AbstractAdapterCredentialsFilter.java | 59 ++++++++++++++++--- .../oauth/webapp/services/CSRFPrevent.java | 9 ++- .../oauth/webapp/services/OAuthService.java | 5 +- 6 files changed, 77 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a2c4827b..8e0e2da4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [5.2.0-SNAPSHOT] ### Added +- Introducing capability to set the servletUri to be used by the OAuthConfiguration ### Changed diff --git a/server/oauth-core/pom.xml b/server/oauth-core/pom.xml index 2913e595f..748ca2357 100644 --- a/server/oauth-core/pom.xml +++ b/server/oauth-core/pom.xml @@ -17,6 +17,12 @@ + + + org.eclipse.lyo.oslc4j.core + lyo-core-settings + ${v.lyo} + jakarta.ws.rs diff --git a/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java b/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java index 4b73d291d..b1d4d1e8e 100644 --- a/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java +++ b/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java @@ -29,6 +29,8 @@ import net.oauth.server.OAuthServlet; import org.eclipse.lyo.server.oauth.core.consumer.LyoOAuthConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Validates that a request is authorized. The request must contain a valid @@ -51,6 +53,8 @@ * @author Samuel Padgett */ public class OAuthRequest { + private static final Logger log = LoggerFactory.getLogger(OAuthRequest.class); + private HttpServletRequest httpRequest; private OAuthMessage message; private OAuthAccessor accessor; @@ -122,12 +126,16 @@ public LyoOAuthConsumer getConsumer() { * @throws OAuthException if the request fails validation */ public void validate() throws OAuthException, IOException, ServletException { + log.trace("validating the request."); try { OAuthConfiguration config = OAuthConfiguration.getInstance(); config.getValidator().validateMessage(message, accessor); config.getTokenStrategy().validateAccessToken(this); } catch (URISyntaxException e) { throw new ServletException(e); + } catch (OAuthProblemException e) { + log.warn("OAuthProblemException caught when validating the request. {}", e.toString()); + throw e; } } } diff --git a/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/utils/AbstractAdapterCredentialsFilter.java b/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/utils/AbstractAdapterCredentialsFilter.java index 9ddff3613..b30b66b82 100644 --- a/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/utils/AbstractAdapterCredentialsFilter.java +++ b/server/oauth-core/src/main/java/org/eclipse/lyo/server/oauth/core/utils/AbstractAdapterCredentialsFilter.java @@ -37,6 +37,7 @@ import net.oauth.http.HttpMessage; import net.oauth.server.OAuthServlet; +import org.eclipse.lyo.oslc4j.core.OSLC4JUtils; import org.eclipse.lyo.server.oauth.core.Application; import org.eclipse.lyo.server.oauth.core.AuthenticationException; import org.eclipse.lyo.server.oauth.core.OAuthConfiguration; @@ -184,6 +185,18 @@ protected void logout(Connection loginSession, HttpSession session) { */ abstract protected ConsumerStore createConsumerStore() throws Exception; + /** + * Gets the official servlet URL + * in case this can differ from that in the individual requests. + * @see org.eclipse.lyo.server.oauth.core.OAuthConfiguration#setServletUri(String) + * This is the typical implementation, which assumes {@link OSLC4JUtils#setPublicURI(String) OSLC4JUtils.setPublicURI(String)} + * and {@link OSLC4JUtils#setServletPath(String) OSLC4JUtils.setServletPath(String)} + * are first used to set the servlet URL. + */ + protected String getServletUri() { + return OSLC4JUtils.getServletURI(); + } + /** * Check if the resource is protected * @@ -288,52 +301,67 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo boolean isTwoLeggedOAuthRequest = false; String twoLeggedOAuthConsumerKey = null; + log.trace("CredentialsFilter - RequestURL: {} {} on session {}", request.getMethod(), request.getRequestURL().toString(), request.getSession().getId()); + //TODO: possibly do this in web.xml if (isProtectedResource(request)) { + log.trace("{} on session {} - A protected resource", request.getPathInfo(), request.getSession().getId()); // First check if this is an OAuth request. try { try { OAuthMessage message = OAuthServlet.getMessage(request, null); + log.trace("{} on session {} - checking for oauth1 authentication", request.getPathInfo(), request.getSession().getId()); // test if this is a valid two-legged oauth request if ("".equals(message.getToken())) { + log.trace("{} on session {} - an oauth1 token with empty-value is found. checking for TwoLeggedOAuth", request.getPathInfo(), request.getSession().getId()); validateTwoLeggedOAuthMessage(message); + log.trace("{} on session {} - It is a TwoLeggedOAuthRequest", request.getPathInfo(), request.getSession().getId()); isTwoLeggedOAuthRequest = true; twoLeggedOAuthConsumerKey = message.getConsumerKey(); } if (!isTwoLeggedOAuthRequest && message.getToken() != null) { + log.trace("{} on session {} - an oauth1 token is found. Validating it.", request.getPathInfo(), request.getSession().getId()); OAuthRequest oAuthRequest = new OAuthRequest(request); oAuthRequest.validate(); + log.debug("{} on session {} - an oauth1 token is valid", request.getPathInfo(), request.getSession().getId()); Connection connector = tokenToConnectionCache.get(message.getToken()); if (connector == null) { + log.debug("{} on session {} - an oauth1 token is valid, but no Connector is associated with it. Raising an exception TOKEN_REJECTED", request.getPathInfo(), request.getSession().getId()); throw new OAuthProblemException( OAuth.Problems.TOKEN_REJECTED); } + log.debug("{} on session {} - oauth1 authentication is valid. Done. Binding the Connector to the session", request.getPathInfo(), request.getSession().getId()); request.getSession().setAttribute(CONNECTOR_ATTRIBUTE, connector); } } catch (OAuthProblemException e) { + log.warn("{} on session {} - OAuthProblemException caught. {}", request.getPathInfo(), e.toString()); if (OAuth.Problems.TOKEN_REJECTED.equals(e.getProblem())) throwInvalidExpiredException(e); else throw e; } } catch (OAuthException e) { + log.error("{} on session {} - OOAuthException caught.", request.getPathInfo(), request.getSession().getId()); OAuthServlet.handleException(response, e, getOAuthRealm()); return; } catch (URISyntaxException e) { + log.error("{} on session {} - URISyntaxException caught.", request.getPathInfo(), request.getSession().getId()); throw new ServletException(e); } - // Check for Basic authentication if this is not an OAuth request HttpSession session = request.getSession(); Connection connector = (Connection) session.getAttribute(CONNECTOR_ATTRIBUTE); if (connector == null) { + // Check for Basic authentication. This is not an OAuth request (If it was, then earlier code would have set the connector to a non-null value) + log.trace("{} on session {} - checking for basic authentication", request.getPathInfo(), request.getSession().getId()); try { Credentials credentials; if (isTwoLeggedOAuthRequest) { + log.trace("{} on session {} - This is TwoLeggedOAuthRequest. Dealing wiht it.", request.getPathInfo(), request.getSession().getId()); connector = tokenToConnectionCache.get(""); if (connector == null) { credentials = getCredentialsForOAuth(OAUTH_EMPTY_TOKEN_KEY, twoLeggedOAuthConsumerKey); @@ -341,32 +369,42 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo tokenToConnectionCache.put("", connector); } credentials = null; // TODO; Do we need to keep the credentials for this path ?? + log.debug("{} on session {} - This is TwoLeggedOAuthRequest. Done dealing wiht it.", request.getPathInfo(), request.getSession().getId()); } else { + log.trace("{} on session {} - This is NOT TwoLeggedOAuthRequest.", request.getPathInfo(), request.getSession().getId()); + log.trace("{} on session {} - Tring to find credentials in session or request", request.getPathInfo(), request.getSession().getId()); credentials = (Credentials) request.getSession().getAttribute(CREDENTIALS_ATTRIBUTE); if (credentials == null) { + log.trace("{} on session {} - No credentials found in session.", request.getPathInfo(), request.getSession().getId()); credentials = getCredentialsFromRequest(request); if (credentials == null) { + log.trace("{} on session {} - No credentials found in request.", request.getPathInfo(), request.getSession().getId()); + log.debug("{} on session {} - This is an UnauthorizedRequest. Handing the request as such", request.getPathInfo(), request.getSession().getId()); boolean interruptFilterChain = handleUnauthorizedRequest(request, response); if (interruptFilterChain) { return; } } } + log.debug("{} on session {} - Credentials found in session or request.", request.getPathInfo(), request.getSession().getId()); + log.trace("{} on session {} - Use Credentials to login and create a Connector.", request.getPathInfo(), request.getSession().getId()); connector = login(credentials, request); } + + log.debug("{} on session {} - Authentication is valid. Done. Binding the Connector & Credentials to the session", request.getPathInfo(), request.getSession().getId()); session.setAttribute(CONNECTOR_ATTRIBUTE, connector); session.setAttribute(CREDENTIALS_ATTRIBUTE, credentials); } catch (UnauthorizedException e) { - sendUnauthorizedResponse(response, e); - //TODO: Change to a log message. - log.debug("UnauthorizedException occured while checking for Basic authentication"); - //Do not call chain.doFilter(). - return; + log.debug("{} - UnauthorizedException occured while checking for Basic authentication", request.getPathInfo(), request.getSession().getId()); + sendUnauthorizedResponse(response, e); + //Do not call chain.doFilter(). + return; } catch (ServletException ce) { + log.trace("{} - ServletException occured while checking for Basic authentication", request.getPathInfo(), request.getSession().getId()); throw ce; } } @@ -377,6 +415,9 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo } } + else { + log.trace("{} on session {} - A unprotected resource", request.getPathInfo(), request.getSession().getId()); + } } chain.doFilter(servletRequest, servletResponse); @@ -432,6 +473,7 @@ private void validateTwoLeggedOAuthMessage(OAuthMessage message) accessor.tokenSecret = ""; config.getValidator().validateMessage(message, accessor); } else { + log.error("OAuth.Problems.TOKEN_REJECTED"); throw new OAuthProblemException( OAuth.Problems.TOKEN_REJECTED); } @@ -540,8 +582,11 @@ public void generateAccessToken(OAuthRequest oAuthRequest) // For now, hard-code the consumers. config.setConsumerStore(createConsumerStore()); } catch (Throwable t) { - System.err.println("Error initializing the OAuth consumer store: " + t.getMessage()); + log.error("Error initializing the OAuth consumer store: " + t.getMessage()); } + + config.setServletUri(getServletUri()); + log.info("OauthConfig is working with ServletUri: {}", config.getServletUri()); } /** diff --git a/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/CSRFPrevent.java b/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/CSRFPrevent.java index 67f25268f..8b212e168 100644 --- a/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/CSRFPrevent.java +++ b/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/CSRFPrevent.java @@ -19,6 +19,9 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Checks requests to see if they have the right X-CSRF-Prevent header values. * @@ -26,11 +29,13 @@ */ public class CSRFPrevent { private static final String CSRF_PREVENT_HEADER = "X-CSRF-Prevent"; - - public static void check(HttpServletRequest httpRequest) { + private static final Logger log = LoggerFactory.getLogger(CSRFPrevent.class); + + public static void check(HttpServletRequest httpRequest) { String csrfPrevent = httpRequest.getHeader(CSRF_PREVENT_HEADER); String sessionId = httpRequest.getSession().getId(); if (!sessionId.equals(csrfPrevent)) { + log.warn("Request denied due to possible CSRF attack. Expected X-CSRF-Prevent header: {}. Received: {}", sessionId, csrfPrevent); throw new WebApplicationException(Response.status(Status.FORBIDDEN) .entity("Request denied due to possible CSRF attack.").type(MediaType.TEXT_PLAIN).build()); } diff --git a/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java b/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java index b769cac53..cde971f07 100644 --- a/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java +++ b/server/oauth-webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java @@ -460,12 +460,13 @@ protected boolean confirmCallback(OAuthRequest oAuthRequest) throws OAuthExcepti protected OAuthRequest validateRequest() throws OAuthException, IOException { OAuthRequest oAuthRequest = new OAuthRequest(httpRequest); try { -// log.trace("baseString: {} signature: {}", OAuthSignatureMethod.getBaseString(oAuthRequest.getMessage()), -// oAuthRequest.getMessage().getSignature()); OAuthValidator validator = OAuthConfiguration.getInstance().getValidator(); validator.validateMessage(oAuthRequest.getMessage(), oAuthRequest.getAccessor()); } catch (URISyntaxException e) { throw new WebApplicationException(e, Status.INTERNAL_SERVER_ERROR); + } catch (OAuthProblemException e) { + log.error("OAuthProblemException caught when validating the request. {}", e.toString()); + throw e; } return oAuthRequest; }