diff --git a/pom.xml b/pom.xml index 403a32e..be5cec8 100644 --- a/pom.xml +++ b/pom.xml @@ -21,12 +21,12 @@ de.acosix.alfresco.maven de.acosix.alfresco.maven.project.parent-6.0.7 - 1.3.1 + 1.3.3 de.acosix.alfresco.keycloak de.acosix.alfresco.keycloak.parent - 1.1.0-rc3 + 1.1.0-rc4-SNAPSHOT pom Acosix Alfresco Keycloak - Parent @@ -79,7 +79,7 @@ 4.5.1 4.4.3 - 1.2.1 + 1.2.3-SNAPSHOT 1.1.0.0 diff --git a/repository-dependencies/pom.xml b/repository-dependencies/pom.xml index a3e5116..65b4a07 100644 --- a/repository-dependencies/pom.xml +++ b/repository-dependencies/pom.xml @@ -21,7 +21,7 @@ de.acosix.alfresco.keycloak de.acosix.alfresco.keycloak.parent - 1.1.0-rc3 + 1.1.0-rc4-SNAPSHOT de.acosix.alfresco.keycloak.repo.deps diff --git a/repository/module.properties b/repository/module.properties index 184584e..85caf06 100644 --- a/repository/module.properties +++ b/repository/module.properties @@ -5,4 +5,4 @@ module.version=${noSnapshotVersion} module.repo.version.min=5 -module.depends.acosix-utility=1.1.0-* \ No newline at end of file +module.depends.acosix-utility=1.2.3-* \ No newline at end of file diff --git a/repository/pom.xml b/repository/pom.xml index 810f2e3..f043952 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -21,7 +21,7 @@ de.acosix.alfresco.keycloak de.acosix.alfresco.keycloak.parent - 1.1.0-rc3 + 1.1.0-rc4-SNAPSHOT de.acosix.alfresco.keycloak.repo diff --git a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationFilter.java b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationFilter.java index 290ec88..48c9066 100644 --- a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationFilter.java +++ b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationFilter.java @@ -693,7 +693,14 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter } LOGGER.trace("Resetting session and state cookie before continueing with filter chain"); - req.getSession().invalidate(); + try + { + req.getSession().invalidate(); + } + catch (final IllegalStateException ignore) + { + // Keycloak authenticator may have already invalidated it - no way to check and avoid exception + } this.resetStateCookies(context, req, res); @@ -723,13 +730,13 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter boolean skip = false; final String authHeader = req.getHeader(HEADER_AUTHORIZATION); - final String noKeycloakLoginRedirectHeader = req.getHeader(this.noKeycloakHandlingHeaderName); + final String noKeycloakHandlingRedirectHeader = req.getHeader(this.noKeycloakHandlingHeaderName); final String servletPath = req.getServletPath(); final String pathInfo = req.getPathInfo(); final String servletRequestUri = servletPath + (pathInfo != null ? pathInfo : ""); - final SessionUser sessionUser = this.getSessionUser(context, req, res, true); + SessionUser sessionUser = this.getSessionUser(context, req, res, true); HttpSession session = req.getSession(); final boolean publicRestApi = API_SERVLET_PATH.equals(servletPath); @@ -743,6 +750,7 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter LOGGER.debug("Session {} for Keycloak-authenticated user {} was invalidated by back-channel logout", session.getId(), AlfrescoCompatibilityUtil.maskUsername(sessionUser.getUserName())); this.invalidateSession(req); + sessionUser = null; session = req.getSession(false); } @@ -908,7 +916,7 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter "Skipping processKeycloakAuthenticationAndActions as filter higher up in chain determined authentication as not required"); skip = true; } - else if (Boolean.parseBoolean(noKeycloakLoginRedirectHeader)) + else if (Boolean.parseBoolean(noKeycloakHandlingRedirectHeader)) { LOGGER.trace( "Skipping processKeycloakAuthenticationAndActions as client provided custom 'no Keycloak handling' header {} with value that resolves to 'true'", diff --git a/share-dependencies/pom.xml b/share-dependencies/pom.xml index 135ff94..4b970a1 100644 --- a/share-dependencies/pom.xml +++ b/share-dependencies/pom.xml @@ -21,7 +21,7 @@ de.acosix.alfresco.keycloak de.acosix.alfresco.keycloak.parent - 1.1.0-rc3 + 1.1.0-rc4-SNAPSHOT de.acosix.alfresco.keycloak.share.deps diff --git a/share/pom.xml b/share/pom.xml index 6df536b..bf3a03f 100644 --- a/share/pom.xml +++ b/share/pom.xml @@ -21,7 +21,7 @@ de.acosix.alfresco.keycloak de.acosix.alfresco.keycloak.parent - 1.1.0-rc3 + 1.1.0-rc4-SNAPSHOT de.acosix.alfresco.keycloak.share diff --git a/share/src/main/config/module-context.xml b/share/src/main/config/module-context.xml index 7b4e970..4e81fd7 100644 --- a/share/src/main/config/module-context.xml +++ b/share/src/main/config/module-context.xml @@ -53,6 +53,7 @@ + diff --git a/share/src/main/java/de/acosix/alfresco/keycloak/share/remote/AccessTokenAwareSlingshotAlfrescoConnector.java b/share/src/main/java/de/acosix/alfresco/keycloak/share/remote/AccessTokenAwareSlingshotAlfrescoConnector.java index 590333d..6b41df7 100644 --- a/share/src/main/java/de/acosix/alfresco/keycloak/share/remote/AccessTokenAwareSlingshotAlfrescoConnector.java +++ b/share/src/main/java/de/acosix/alfresco/keycloak/share/remote/AccessTokenAwareSlingshotAlfrescoConnector.java @@ -32,6 +32,7 @@ import de.acosix.alfresco.keycloak.share.deps.keycloak.adapters.OidcKeycloakAcco import de.acosix.alfresco.keycloak.share.deps.keycloak.adapters.spi.KeycloakAccount; import de.acosix.alfresco.keycloak.share.util.RefreshableAccessTokenHolder; import de.acosix.alfresco.keycloak.share.web.KeycloakAuthenticationFilter; +import de.acosix.alfresco.utility.share.connector.MutableSlingshotRemoteClient; /** * @author Axel Faust @@ -72,13 +73,26 @@ public class AccessTokenAwareSlingshotAlfrescoConnector extends SlingshotAlfresc ? session.getAttribute(KeycloakAuthenticationFilter.BACKEND_ACCESS_TOKEN_SESSION_KEY) : null); + final MutableSlingshotRemoteClient mrc = remoteClient instanceof MutableSlingshotRemoteClient + ? (MutableSlingshotRemoteClient) remoteClient + : null; + if (endpointSpecificAccessToken != null) { if (endpointSpecificAccessToken.isActive()) { LOGGER.debug("Using access token for backend found in session for request"); final String tokenString = endpointSpecificAccessToken.getToken(); - remoteClient.setRequestProperties(Collections.singletonMap("Authorization", "Bearer " + tokenString)); + if (mrc != null) + { + mrc.addRemoveResponseHeader("WWW-Authenticate"); + // must be request property to be a final override (other Alfresco components sets Authorization=null) + mrc.addRequestProperty("Authorization", "Bearer " + tokenString); + } + else + { + remoteClient.setRequestProperties(Collections.singletonMap("Authorization", "Bearer " + tokenString)); + } } else { @@ -91,14 +105,32 @@ public class AccessTokenAwareSlingshotAlfrescoConnector extends SlingshotAlfresc "Did not find access token for backend in session - using regularly authenticated Keycloak account access token for request instead"); final KeycloakSecurityContext keycloakSecurityContext = ((OidcKeycloakAccount) keycloakAccount).getKeycloakSecurityContext(); final String tokenString = keycloakSecurityContext.getTokenString(); - remoteClient.setRequestProperties(Collections.singletonMap("Authorization", "Bearer " + tokenString)); + if (mrc != null) + { + mrc.addRemoveResponseHeader("WWW-Authenticate"); + // must be request property to be a final override (other Alfresco components sets Authorization=null) + mrc.addRequestProperty("Authorization", "Bearer " + tokenString); + } + else + { + remoteClient.setRequestProperties(Collections.singletonMap("Authorization", "Bearer " + tokenString)); + } } else if (accessToken != null) { LOGGER.debug( "Did not find access token for backend in session - using Bearer access token provided in original authentication request for request instead"); final String tokenString = accessToken.getToken(); - remoteClient.setRequestProperties(Collections.singletonMap("Authorization", "Bearer " + tokenString)); + if (mrc != null) + { + mrc.addRemoveResponseHeader("WWW-Authenticate"); + // must be request property to be a final override (other Alfresco components sets Authorization=null) + mrc.addRequestProperty("Authorization", "Bearer " + tokenString); + } + else + { + remoteClient.setRequestProperties(Collections.singletonMap("Authorization", "Bearer " + tokenString)); + } } else { diff --git a/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java b/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java index 472c71c..af68f5e 100644 --- a/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java +++ b/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java @@ -1025,7 +1025,14 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I authError != null ? authError : ""); LOGGER.debug("Resetting session and state cookie before continueing with filter chain"); - req.getSession().invalidate(); + try + { + req.getSession().invalidate(); + } + catch (final IllegalStateException ignore) + { + // Keycloak authenticator may have already invalidated it - no way to check and avoid exception + } this.resetStateCookies(context, req, res); @@ -1126,6 +1133,10 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I { // variant with request AND response is only available in Spring 5+ RequestContextHolder.setRequestAttributes(new ServletRequestAttributes((HttpServletRequest) request)); + // a bit redundant since request attributes already holds the request, but hey, that's Alfresco + // ServletUtil uses an attribute in the request attributes object separate from the main request + // see ServletUtil.VIEW_REQUEST_ATTRIBUTE_NAME + ServletUtil.setRequest((HttpServletRequest) request); } try @@ -1589,7 +1600,8 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I token = (RefreshableAccessTokenHolder) backendAccessTokenCandidate; } - // not really feasible to synchronise / lock concurrent refresh on token + // not really feasible to synchronise / lock concurrent refresh on token, especially given that we cannot lock across + // potentially multiple Share instances // not a big problem - apart from wasted CPU cycles / latency - since each concurrently refreshed token is valid // independently if (token == null || !token.isActive()