mirror of
https://github.com/bmlong137/alfresco-keycloak.git
synced 2025-09-10 14:11:09 +00:00
Share backend token handling + NPE fix
This commit is contained in:
6
pom.xml
6
pom.xml
@@ -21,12 +21,12 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>de.acosix.alfresco.maven</groupId>
|
<groupId>de.acosix.alfresco.maven</groupId>
|
||||||
<artifactId>de.acosix.alfresco.maven.project.parent-6.0.7</artifactId>
|
<artifactId>de.acosix.alfresco.maven.project.parent-6.0.7</artifactId>
|
||||||
<version>1.3.1</version>
|
<version>1.3.3</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>de.acosix.alfresco.keycloak</groupId>
|
<groupId>de.acosix.alfresco.keycloak</groupId>
|
||||||
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
||||||
<version>1.1.0-rc3</version>
|
<version>1.1.0-rc4-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>Acosix Alfresco Keycloak - Parent</name>
|
<name>Acosix Alfresco Keycloak - Parent</name>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<apache.httpclient.version>4.5.1</apache.httpclient.version>
|
<apache.httpclient.version>4.5.1</apache.httpclient.version>
|
||||||
<apache.httpcore.version>4.4.3</apache.httpcore.version>
|
<apache.httpcore.version>4.4.3</apache.httpcore.version>
|
||||||
|
|
||||||
<acosix.utility.version>1.2.1</acosix.utility.version>
|
<acosix.utility.version>1.2.3-SNAPSHOT</acosix.utility.version>
|
||||||
<ootbee.support-tools.version>1.1.0.0</ootbee.support-tools.version>
|
<ootbee.support-tools.version>1.1.0.0</ootbee.support-tools.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>de.acosix.alfresco.keycloak</groupId>
|
<groupId>de.acosix.alfresco.keycloak</groupId>
|
||||||
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
||||||
<version>1.1.0-rc3</version>
|
<version>1.1.0-rc4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>de.acosix.alfresco.keycloak.repo.deps</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.repo.deps</artifactId>
|
||||||
|
@@ -5,4 +5,4 @@ module.version=${noSnapshotVersion}
|
|||||||
|
|
||||||
module.repo.version.min=5
|
module.repo.version.min=5
|
||||||
|
|
||||||
module.depends.acosix-utility=1.1.0-*
|
module.depends.acosix-utility=1.2.3-*
|
@@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>de.acosix.alfresco.keycloak</groupId>
|
<groupId>de.acosix.alfresco.keycloak</groupId>
|
||||||
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
||||||
<version>1.1.0-rc3</version>
|
<version>1.1.0-rc4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>de.acosix.alfresco.keycloak.repo</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.repo</artifactId>
|
||||||
|
@@ -693,7 +693,14 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter
|
|||||||
}
|
}
|
||||||
LOGGER.trace("Resetting session and state cookie before continueing with filter chain");
|
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);
|
this.resetStateCookies(context, req, res);
|
||||||
|
|
||||||
@@ -723,13 +730,13 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter
|
|||||||
boolean skip = false;
|
boolean skip = false;
|
||||||
|
|
||||||
final String authHeader = req.getHeader(HEADER_AUTHORIZATION);
|
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 servletPath = req.getServletPath();
|
||||||
final String pathInfo = req.getPathInfo();
|
final String pathInfo = req.getPathInfo();
|
||||||
final String servletRequestUri = servletPath + (pathInfo != null ? pathInfo : "");
|
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();
|
HttpSession session = req.getSession();
|
||||||
|
|
||||||
final boolean publicRestApi = API_SERVLET_PATH.equals(servletPath);
|
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(),
|
LOGGER.debug("Session {} for Keycloak-authenticated user {} was invalidated by back-channel logout", session.getId(),
|
||||||
AlfrescoCompatibilityUtil.maskUsername(sessionUser.getUserName()));
|
AlfrescoCompatibilityUtil.maskUsername(sessionUser.getUserName()));
|
||||||
this.invalidateSession(req);
|
this.invalidateSession(req);
|
||||||
|
sessionUser = null;
|
||||||
session = req.getSession(false);
|
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");
|
"Skipping processKeycloakAuthenticationAndActions as filter higher up in chain determined authentication as not required");
|
||||||
skip = true;
|
skip = true;
|
||||||
}
|
}
|
||||||
else if (Boolean.parseBoolean(noKeycloakLoginRedirectHeader))
|
else if (Boolean.parseBoolean(noKeycloakHandlingRedirectHeader))
|
||||||
{
|
{
|
||||||
LOGGER.trace(
|
LOGGER.trace(
|
||||||
"Skipping processKeycloakAuthenticationAndActions as client provided custom 'no Keycloak handling' header {} with value that resolves to 'true'",
|
"Skipping processKeycloakAuthenticationAndActions as client provided custom 'no Keycloak handling' header {} with value that resolves to 'true'",
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>de.acosix.alfresco.keycloak</groupId>
|
<groupId>de.acosix.alfresco.keycloak</groupId>
|
||||||
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
||||||
<version>1.1.0-rc3</version>
|
<version>1.1.0-rc4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>de.acosix.alfresco.keycloak.share.deps</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.share.deps</artifactId>
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>de.acosix.alfresco.keycloak</groupId>
|
<groupId>de.acosix.alfresco.keycloak</groupId>
|
||||||
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.parent</artifactId>
|
||||||
<version>1.1.0-rc3</version>
|
<version>1.1.0-rc4-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>de.acosix.alfresco.keycloak.share</artifactId>
|
<artifactId>de.acosix.alfresco.keycloak.share</artifactId>
|
||||||
|
@@ -53,6 +53,7 @@
|
|||||||
<property name="propertyName" value="requestHeaders" />
|
<property name="propertyName" value="requestHeaders" />
|
||||||
<property name="valueMap">
|
<property name="valueMap">
|
||||||
<map>
|
<map>
|
||||||
|
<!-- unless we pro-actively provide a bearer token, repository should not handle Keycloak on remote API for requests from Share -->
|
||||||
<entry key="x-${moduleId}-no-keycloak-handling" value="true" />
|
<entry key="x-${moduleId}-no-keycloak-handling" value="true" />
|
||||||
</map>
|
</map>
|
||||||
</property>
|
</property>
|
||||||
|
@@ -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.deps.keycloak.adapters.spi.KeycloakAccount;
|
||||||
import de.acosix.alfresco.keycloak.share.util.RefreshableAccessTokenHolder;
|
import de.acosix.alfresco.keycloak.share.util.RefreshableAccessTokenHolder;
|
||||||
import de.acosix.alfresco.keycloak.share.web.KeycloakAuthenticationFilter;
|
import de.acosix.alfresco.keycloak.share.web.KeycloakAuthenticationFilter;
|
||||||
|
import de.acosix.alfresco.utility.share.connector.MutableSlingshotRemoteClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Axel Faust
|
* @author Axel Faust
|
||||||
@@ -72,13 +73,26 @@ public class AccessTokenAwareSlingshotAlfrescoConnector extends SlingshotAlfresc
|
|||||||
? session.getAttribute(KeycloakAuthenticationFilter.BACKEND_ACCESS_TOKEN_SESSION_KEY)
|
? session.getAttribute(KeycloakAuthenticationFilter.BACKEND_ACCESS_TOKEN_SESSION_KEY)
|
||||||
: null);
|
: null);
|
||||||
|
|
||||||
|
final MutableSlingshotRemoteClient mrc = remoteClient instanceof MutableSlingshotRemoteClient
|
||||||
|
? (MutableSlingshotRemoteClient) remoteClient
|
||||||
|
: null;
|
||||||
|
|
||||||
if (endpointSpecificAccessToken != null)
|
if (endpointSpecificAccessToken != null)
|
||||||
{
|
{
|
||||||
if (endpointSpecificAccessToken.isActive())
|
if (endpointSpecificAccessToken.isActive())
|
||||||
{
|
{
|
||||||
LOGGER.debug("Using access token for backend found in session for request");
|
LOGGER.debug("Using access token for backend found in session for request");
|
||||||
final String tokenString = endpointSpecificAccessToken.getToken();
|
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
|
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");
|
"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 KeycloakSecurityContext keycloakSecurityContext = ((OidcKeycloakAccount) keycloakAccount).getKeycloakSecurityContext();
|
||||||
final String tokenString = keycloakSecurityContext.getTokenString();
|
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)
|
else if (accessToken != null)
|
||||||
{
|
{
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Did not find access token for backend in session - using Bearer access token provided in original authentication request for request instead");
|
"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();
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@@ -1025,7 +1025,14 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
authError != null ? authError : "<missing AuthenticationError details in request context>");
|
authError != null ? authError : "<missing AuthenticationError details in request context>");
|
||||||
LOGGER.debug("Resetting session and state cookie before continueing with filter chain");
|
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);
|
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+
|
// variant with request AND response is only available in Spring 5+
|
||||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes((HttpServletRequest) request));
|
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
|
try
|
||||||
@@ -1589,7 +1600,8 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
token = (RefreshableAccessTokenHolder) backendAccessTokenCandidate;
|
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
|
// not a big problem - apart from wasted CPU cycles / latency - since each concurrently refreshed token is valid
|
||||||
// independently
|
// independently
|
||||||
if (token == null || !token.isActive()
|
if (token == null || !token.isActive()
|
||||||
|
Reference in New Issue
Block a user