mirror of
https://github.com/bmlong137/alfresco-keycloak.git
synced 2025-09-10 14:11:09 +00:00
Handle alfRedirectUrl parameter on login page
- some features (like QuickShare) may use it to trigger login with a pre-defined post authentication location
This commit is contained in:
@@ -33,6 +33,15 @@
|
|||||||
<class>de.acosix.alfresco.keycloak.share.remote.AccessTokenAwareAlfrescoAuthenticator</class>
|
<class>de.acosix.alfresco.keycloak.share.remote.AccessTokenAwareAlfrescoAuthenticator</class>
|
||||||
</authenticator>
|
</authenticator>
|
||||||
|
|
||||||
|
<endpoint>
|
||||||
|
<id>alfresco-noauth</id>
|
||||||
|
<name>Alfresco - unauthenticated access</name>
|
||||||
|
<description>Access to Alfresco Repository WebScripts that do not require authentication</description>
|
||||||
|
<connector-id>alfresco</connector-id>
|
||||||
|
<endpoint-url>http://repository:8080/alfresco/s</endpoint-url>
|
||||||
|
<identity>none</identity>
|
||||||
|
</endpoint>
|
||||||
|
|
||||||
<endpoint>
|
<endpoint>
|
||||||
<id>alfresco</id>
|
<id>alfresco</id>
|
||||||
<name>Alfresco - user access</name>
|
<name>Alfresco - user access</name>
|
||||||
@@ -71,7 +80,7 @@
|
|||||||
<keycloak-auth-config>
|
<keycloak-auth-config>
|
||||||
<enhance-login-form>true</enhance-login-form>
|
<enhance-login-form>true</enhance-login-form>
|
||||||
<enable-sso-filter>true</enable-sso-filter>
|
<enable-sso-filter>true</enable-sso-filter>
|
||||||
<force-keycloak-sso>false</force-keycloak-sso>
|
<force-keycloak-sso>true</force-keycloak-sso>
|
||||||
<perform-token-exchange>true</perform-token-exchange>
|
<perform-token-exchange>true</perform-token-exchange>
|
||||||
</keycloak-auth-config>
|
</keycloak-auth-config>
|
||||||
<keycloak-adapter-config>
|
<keycloak-adapter-config>
|
||||||
@@ -88,4 +97,61 @@
|
|||||||
</keycloak-adapter-config>
|
</keycloak-adapter-config>
|
||||||
</config>
|
</config>
|
||||||
|
|
||||||
|
<!-- Must be specified (typically provided as part of packaging) -->
|
||||||
|
<config evaluator="string-compare" condition="DocumentLibrary" replace="true">
|
||||||
|
<tree>
|
||||||
|
<evaluate-child-folders>false</evaluate-child-folders>
|
||||||
|
<maximum-folder-count>1000</maximum-folder-count>
|
||||||
|
<timeout>7000</timeout>
|
||||||
|
</tree>
|
||||||
|
<aspects>
|
||||||
|
<!-- Aspects that a user can see -->
|
||||||
|
<visible>
|
||||||
|
<aspect name="cm:generalclassifiable" />
|
||||||
|
<aspect name="cm:complianceable" />
|
||||||
|
<aspect name="cm:dublincore" />
|
||||||
|
<aspect name="cm:effectivity" />
|
||||||
|
<aspect name="cm:summarizable" />
|
||||||
|
<aspect name="cm:versionable" />
|
||||||
|
<aspect name="cm:templatable" />
|
||||||
|
<aspect name="cm:emailed" />
|
||||||
|
<aspect name="emailserver:aliasable" />
|
||||||
|
<aspect name="cm:taggable" />
|
||||||
|
<aspect name="app:inlineeditable" />
|
||||||
|
<aspect name="cm:geographic" />
|
||||||
|
<aspect name="exif:exif" />
|
||||||
|
<aspect name="audio:audio" />
|
||||||
|
<aspect name="cm:indexControl" />
|
||||||
|
<aspect name="dp:restrictable" />
|
||||||
|
<aspect name="smf:customConfigSmartFolder" />
|
||||||
|
<aspect name="smf:systemConfigSmartFolder" />
|
||||||
|
</visible>
|
||||||
|
<addable>
|
||||||
|
</addable>
|
||||||
|
<removeable>
|
||||||
|
</removeable>
|
||||||
|
</aspects>
|
||||||
|
<types>
|
||||||
|
<type name="cm:content">
|
||||||
|
<subtype name="smf:smartFolderTemplate" />
|
||||||
|
</type>
|
||||||
|
<type name="cm:folder">
|
||||||
|
</type>
|
||||||
|
<type name="trx:transferTarget">
|
||||||
|
<subtype name="trx:fileTransferTarget" />
|
||||||
|
</type>
|
||||||
|
</types>
|
||||||
|
<repository-url>http://repository:8080/alfresco</repository-url>
|
||||||
|
<google-docs>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
<creatable-types>
|
||||||
|
<creatable type="doc">application/vnd.openxmlformats-officedocument.wordprocessingml.document</creatable>
|
||||||
|
<creatable type="xls">application/vnd.openxmlformats-officedocument.spreadsheetml.sheet</creatable>
|
||||||
|
<creatable type="ppt">application/vnd.ms-powerpoint</creatable>
|
||||||
|
</creatable-types>
|
||||||
|
</google-docs>
|
||||||
|
<file-upload>
|
||||||
|
<adobe-flash-enabled>false</adobe-flash-enabled>
|
||||||
|
</file-upload>
|
||||||
|
</config>
|
||||||
</alfresco-config>
|
</alfresco-config>
|
@@ -179,6 +179,9 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
|
|
||||||
private static final String PAGE_TYPE_PARAMETER_NAME = "pt";
|
private static final String PAGE_TYPE_PARAMETER_NAME = "pt";
|
||||||
|
|
||||||
|
// used on some login page redirects - see PageView ALF_REDIRECT_URL constant
|
||||||
|
private static final String ALF_REDIRECT_URL = "alfRedirectUrl";
|
||||||
|
|
||||||
private static final String LOGIN_PATH_INFORMATION = "/dologin";
|
private static final String LOGIN_PATH_INFORMATION = "/dologin";
|
||||||
|
|
||||||
private static final String LOGOUT_PATH_INFORMATION = "/dologout";
|
private static final String LOGOUT_PATH_INFORMATION = "/dologout";
|
||||||
@@ -527,13 +530,20 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
this.keycloakDeployment = KeycloakDeploymentBuilder.build(adapterConfiguration);
|
this.keycloakDeployment = KeycloakDeploymentBuilder.build(adapterConfiguration);
|
||||||
|
|
||||||
// we need to recreate the HttpClient to configure the forced route URL
|
// we need to recreate the HttpClient to configure the forced route URL
|
||||||
this.keycloakDeployment.setClient(new Callable<HttpClient>() {
|
this.keycloakDeployment.setClient(new Callable<HttpClient>()
|
||||||
|
{
|
||||||
|
|
||||||
private HttpClient client;
|
private HttpClient client;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpClient call() throws Exception {
|
public HttpClient call() throws Exception
|
||||||
if (this.client == null) {
|
{
|
||||||
synchronized (this) {
|
if (this.client == null)
|
||||||
if (this.client == null) {
|
{
|
||||||
|
synchronized (this)
|
||||||
|
{
|
||||||
|
if (this.client == null)
|
||||||
|
{
|
||||||
this.client = new HttpClientBuilder()
|
this.client = new HttpClientBuilder()
|
||||||
.routePlanner(KeycloakAuthenticationFilter.this.createForcedRoutePlanner(adapterConfiguration))
|
.routePlanner(KeycloakAuthenticationFilter.this.createForcedRoutePlanner(adapterConfiguration))
|
||||||
.build(adapterConfiguration);
|
.build(adapterConfiguration);
|
||||||
@@ -845,11 +855,7 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
return cookie;
|
return cookie;
|
||||||
}).forEach(res::addCookie);
|
}).forEach(res::addCookie);
|
||||||
|
|
||||||
final List<String> redirects = captureFacade.getHeaders().get("Location");
|
setRedirectFromCaptureFacade(req, captureFacade);
|
||||||
if (redirects != null && !redirects.isEmpty())
|
|
||||||
{
|
|
||||||
LOGIN_REDIRECT_URL.set(redirects.get(0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -881,6 +887,14 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
{
|
{
|
||||||
// no query parameters, so no code= and no error=
|
// no query parameters, so no code= and no error=
|
||||||
// this will cause login redirect challenge to be generated
|
// this will cause login redirect challenge to be generated
|
||||||
|
|
||||||
|
// but use the alfRedirectUrl if present in request
|
||||||
|
final String alfRedirectUrl = req.getParameter(ALF_REDIRECT_URL);
|
||||||
|
if (alfRedirectUrl != null && !alfRedirectUrl.isBlank())
|
||||||
|
{
|
||||||
|
LOGGER.debug("Found {} query parameter with value {}", ALF_REDIRECT_URL, alfRedirectUrl);
|
||||||
|
return ALF_REDIRECT_URL + "=" + alfRedirectUrl;
|
||||||
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,10 +929,17 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
return cookie;
|
return cookie;
|
||||||
}).forEach(res::addCookie);
|
}).forEach(res::addCookie);
|
||||||
|
|
||||||
|
setRedirectFromCaptureFacade(req, captureFacade);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setRedirectFromCaptureFacade(final HttpServletRequest req,
|
||||||
|
final ResponseHeaderCookieCaptureServletHttpFacade captureFacade)
|
||||||
|
{
|
||||||
final List<String> redirects = captureFacade.getHeaders().get("Location");
|
final List<String> redirects = captureFacade.getHeaders().get("Location");
|
||||||
if (redirects != null && !redirects.isEmpty())
|
if (redirects != null && !redirects.isEmpty())
|
||||||
{
|
{
|
||||||
LOGIN_REDIRECT_URL.set(redirects.get(0));
|
final String redirectPath = redirects.get(0);
|
||||||
|
LOGIN_REDIRECT_URL.set(redirectPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -961,9 +982,19 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
this.handleAlfrescoResourceAccessToken(session);
|
this.handleAlfrescoResourceAccessToken(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String alfRedirectUrl = req.getParameter(ALF_REDIRECT_URL);
|
||||||
|
|
||||||
if (facade.isEnded())
|
if (facade.isEnded())
|
||||||
{
|
{
|
||||||
LOGGER.debug("Authenticator already handled response");
|
LOGGER.debug("Authenticator already handled response");
|
||||||
|
|
||||||
|
if (alfRedirectUrl != null && !alfRedirectUrl.isBlank())
|
||||||
|
{
|
||||||
|
LOGGER.debug("Found {} query parameter - redirecting to {}", ALF_REDIRECT_URL, alfRedirectUrl);
|
||||||
|
// this may override any redirect set by the authenticator
|
||||||
|
res.sendRedirect(alfRedirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1022,9 +1053,19 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
session.setAttribute(UserFactory.SESSION_ATTRIBUTE_EXTERNAL_AUTH, Boolean.TRUE);
|
session.setAttribute(UserFactory.SESSION_ATTRIBUTE_EXTERNAL_AUTH, Boolean.TRUE);
|
||||||
session.setAttribute(UserFactory.SESSION_ATTRIBUTE_KEY_USER_ID, userId);
|
session.setAttribute(UserFactory.SESSION_ATTRIBUTE_KEY_USER_ID, userId);
|
||||||
|
|
||||||
|
final String alfRedirectUrl = req.getParameter(ALF_REDIRECT_URL);
|
||||||
|
|
||||||
if (facade.isEnded())
|
if (facade.isEnded())
|
||||||
{
|
{
|
||||||
LOGGER.debug("Authenticator already handled response");
|
LOGGER.debug("Authenticator already handled response");
|
||||||
|
|
||||||
|
if (alfRedirectUrl != null && !alfRedirectUrl.isBlank())
|
||||||
|
{
|
||||||
|
LOGGER.debug("Found {} query parameter - redirecting to {}", ALF_REDIRECT_URL, alfRedirectUrl);
|
||||||
|
// this may override any redirect set by the authenticator
|
||||||
|
res.sendRedirect(alfRedirectUrl);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1042,8 +1083,6 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.completeRequestContext(req);
|
|
||||||
|
|
||||||
LOGGER.debug("Continueing with filter chain processing");
|
LOGGER.debug("Continueing with filter chain processing");
|
||||||
this.continueFilterChain(context, req, res, chain);
|
this.continueFilterChain(context, req, res, chain);
|
||||||
}
|
}
|
||||||
@@ -1773,10 +1812,8 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
|
|
||||||
final List<Header> headers = new LinkedList<>();
|
final List<Header> headers = new LinkedList<>();
|
||||||
|
|
||||||
ClientCredentialsProviderUtils.setClientCredentials(
|
ClientCredentialsProviderUtils.setClientCredentials(this.keycloakDeployment.getAdapterConfig(),
|
||||||
this.keycloakDeployment.getAdapterConfig(),
|
this.keycloakDeployment.getClientAuthenticator(), new NameValueMapAdapter<>(headers, BasicHeader.class),
|
||||||
this.keycloakDeployment.getClientAuthenticator(),
|
|
||||||
new NameValueMapAdapter<>(headers, BasicHeader.class),
|
|
||||||
new NameValueMapAdapter<>(formParams, BasicNameValuePair.class));
|
new NameValueMapAdapter<>(formParams, BasicNameValuePair.class));
|
||||||
|
|
||||||
for (final Header header : headers)
|
for (final Header header : headers)
|
||||||
@@ -1900,41 +1937,54 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
|
|||||||
params.setParameter(ConnRoutePNames.FORCED_ROUTE, route);
|
params.setParameter(ConnRoutePNames.FORCED_ROUTE, route);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpRoute createRoute(final ExtendedAdapterConfig adapterConfig, final HttpHost routeHost) throws UnknownHostException, MalformedURLException {
|
protected HttpRoute createRoute(final ExtendedAdapterConfig adapterConfig, final HttpHost routeHost)
|
||||||
|
throws UnknownHostException, MalformedURLException
|
||||||
|
{
|
||||||
final boolean secure = "https".equalsIgnoreCase(routeHost.getSchemeName());
|
final boolean secure = "https".equalsIgnoreCase(routeHost.getSchemeName());
|
||||||
|
|
||||||
if (adapterConfig.getProxyUrl() != null) {
|
if (adapterConfig.getProxyUrl() != null)
|
||||||
|
{
|
||||||
// useful in parsing the URL for just what is needed for HttpHost
|
// useful in parsing the URL for just what is needed for HttpHost
|
||||||
final URL proxyUrl = new URL(adapterConfig.getProxyUrl());
|
final URL proxyUrl = new URL(adapterConfig.getProxyUrl());
|
||||||
final HttpHost proxyHost = new HttpHost(proxyUrl.getHost(), proxyUrl.getPort(), proxyUrl.getProtocol());
|
final HttpHost proxyHost = new HttpHost(proxyUrl.getHost(), proxyUrl.getPort(), proxyUrl.getProtocol());
|
||||||
return new HttpRoute(routeHost, InetAddress.getLocalHost(), proxyHost, secure);
|
return new HttpRoute(routeHost, InetAddress.getLocalHost(), proxyHost, secure);
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return new HttpRoute(routeHost, InetAddress.getLocalHost(), secure);
|
return new HttpRoute(routeHost, InetAddress.getLocalHost(), secure);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpRoute createForcedRoute(final ExtendedAdapterConfig adapterConfig) throws UnknownHostException, MalformedURLException {
|
protected HttpRoute createForcedRoute(final ExtendedAdapterConfig adapterConfig) throws UnknownHostException, MalformedURLException
|
||||||
|
{
|
||||||
// useful in parsing the URL for just what is needed for HttpHost
|
// useful in parsing the URL for just what is needed for HttpHost
|
||||||
final URL forcedRouteUrl = new URL(adapterConfig.getForcedRouteUrl());
|
final URL forcedRouteUrl = new URL(adapterConfig.getForcedRouteUrl());
|
||||||
final HttpHost forcedRouteHost = new HttpHost(forcedRouteUrl.getHost(), forcedRouteUrl.getPort(), forcedRouteUrl.getProtocol());
|
final HttpHost forcedRouteHost = new HttpHost(forcedRouteUrl.getHost(), forcedRouteUrl.getPort(), forcedRouteUrl.getProtocol());
|
||||||
return this.createRoute(adapterConfig, forcedRouteHost);
|
return this.createRoute(adapterConfig, forcedRouteHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpRoutePlanner createForcedRoutePlanner(final ExtendedAdapterConfig adapterConfig) throws MalformedURLException {
|
protected HttpRoutePlanner createForcedRoutePlanner(final ExtendedAdapterConfig adapterConfig) throws MalformedURLException
|
||||||
|
{
|
||||||
final URL authServerUrl = new URL(adapterConfig.getAuthServerUrl());
|
final URL authServerUrl = new URL(adapterConfig.getAuthServerUrl());
|
||||||
final HttpHost authServerHost = new HttpHost(authServerUrl.getHost(), authServerUrl.getPort(), authServerUrl.getProtocol());
|
final HttpHost authServerHost = new HttpHost(authServerUrl.getHost(), authServerUrl.getPort(), authServerUrl.getProtocol());
|
||||||
|
|
||||||
return (target, request, context) -> {
|
return (target, request, context) -> {
|
||||||
try {
|
try
|
||||||
if (authServerHost.equals(target)) {
|
{
|
||||||
|
if (authServerHost.equals(target))
|
||||||
|
{
|
||||||
LOGGER.trace("Rerouting to forced route");
|
LOGGER.trace("Rerouting to forced route");
|
||||||
final HttpRoute route = KeycloakAuthenticationFilter.this.createForcedRoute(adapterConfig);
|
final HttpRoute route = KeycloakAuthenticationFilter.this.createForcedRoute(adapterConfig);
|
||||||
LOGGER.trace("Rerouting to forced route: {}", route);
|
LOGGER.trace("Rerouting to forced route: {}", route);
|
||||||
return route;
|
return route;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return KeycloakAuthenticationFilter.this.createRoute(adapterConfig, target);
|
return KeycloakAuthenticationFilter.this.createRoute(adapterConfig, target);
|
||||||
}
|
}
|
||||||
} catch (final IOException ie) {
|
}
|
||||||
|
catch (final IOException ie)
|
||||||
|
{
|
||||||
throw new HttpException(ie.getMessage(), ie);
|
throw new HttpException(ie.getMessage(), ie);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -43,10 +43,20 @@ function main()
|
|||||||
{
|
{
|
||||||
parameterModel.value = decodeURIComponent(parameter.substring(parameter.indexOf('=') + 1));
|
parameterModel.value = decodeURIComponent(parameter.substring(parameter.indexOf('=') + 1));
|
||||||
if (parameterModel.value.indexOf('?') !== -1)
|
if (parameterModel.value.indexOf('?') !== -1)
|
||||||
|
{
|
||||||
|
if (parameterModel.value.indexOf('?alfRedirectUrl=') !== -1)
|
||||||
|
{
|
||||||
|
if (parameterModel.value.indexOf('&') !== -1)
|
||||||
|
{
|
||||||
|
parameterModel.value = parameterModel.value.substring(0, parameterModel.value.indexOf('&'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
parameterModel.value = parameterModel.value.substring(0, parameterModel.value.indexOf('?'));
|
parameterModel.value = parameterModel.value.substring(0, parameterModel.value.indexOf('?'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parameterModel.value = parameter.substring(parameter.indexOf('=') + 1);
|
parameterModel.value = parameter.substring(parameter.indexOf('=') + 1);
|
||||||
|
Reference in New Issue
Block a user