Fix Share working with recent Repository improvements

This commit is contained in:
AFaust
2020-02-15 01:33:49 +01:00
parent 1a5c3400f6
commit 0f974c9f1d
32 changed files with 890 additions and 2861 deletions

View File

@@ -1,2 +1,4 @@
log4j.logger.${project.artifactId}=INFO
log4j.logger.${project.artifactId}.deps=ERROR
log4j.logger.${project.artifactId}.deps=ERROR
log4j.logger.${project.artifactId}.deps.keycloak=ERROR
log4j.logger.${project.artifactId}.deps.jboss=ERROR

View File

@@ -75,7 +75,7 @@
<constructor-arg value="cache.${moduleId}.ticketTokenCache" />
</bean>
<bean id="webscript.de.acosix.${moduleId}.roles.get" class="${project.artifactId}.web.scripts.RolesGet" parent="webscript">
<bean id="webscript.de.acosix.keycloak.roles.get" class="${project.artifactId}.web.scripts.RolesGet" parent="webscript">
<property name="roleService" ref="${moduleId}.RoleService" />
</bean>

View File

@@ -109,14 +109,21 @@
<property name="personService" ref="PersonService" />
</bean>
<bean id="webscriptAuthenticationFilter" class="org.alfresco.web.app.servlet.WebScriptSSOAuthenticationFilter">
<bean id="webscriptAuthenticationFilter" class="${project.artifactId}.authentication.KeycloakWebScriptSSOAuthenticationFilter">
<property name="active" value="${keycloak.authentication.enabled}" />
<property name="container" ref="webscripts.container" />
<!-- via inheritance, filter has way more fields, but only the above are actually needed -->
</bean>
<!-- need to override this to align userAttributeName in session with other SSO filters -->
<!-- for some reason, Alfresco is really inconsistent here between BaseAuthenticationFilter.AUTHENTICATION_USER and AuthenticationDriver.AUTHENTICATION_USER -->
<bean id="cookieBasedAuthenticationFilter" class="${project.artifactId}.authentication.KeycloakWebScriptCookieAuthenticationFilter">
<property name="authenticationService" ref="AuthenticationService" />
<property name="authenticationComponent" ref="AuthenticationComponent" />
<property name="personService" ref="personService" />
<property name="personService" ref="PersonService" />
<property name="nodeService" ref="NodeService" />
<property name="transactionService" ref="TransactionService" />
<property name="container" ref="webscripts.container" />
<property name="authenticationComponent" ref="AuthenticationComponent" />
<property name="remoteUserMapper" ref="RemoteUserMapper" />
</bean>
<bean id="globalAuthenticationFilter" class="${project.artifactId}.authentication.KeycloakAuthenticationFilter">

View File

@@ -484,10 +484,6 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter
final SessionUser sessionUser = this.createUserEnvironment(session, userId);
this.keycloakAuthenticationComponent.handleUserTokens(accessToken, keycloakSecurityContext.getIdToken(), true);
// need different attribute name than default for integration with web scripts framework
// default attribute name seems to be no longer used
session.setAttribute(AuthenticationDriver.AUTHENTICATION_USER, sessionUser);
this.authenticationListener.userAuthenticated(new KeycloakCredentials(accessToken));
// store tokens in cache as well for ticket validation
@@ -529,6 +525,41 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter
chain.doFilter(requestWrapper, res);
}
/**
*
* {@inheritDoc}
*/
@Override
protected SessionUser createUserEnvironment(final HttpSession session, final String userName) throws IOException, ServletException
{
final SessionUser sessionUser = super.createUserEnvironment(session, userName);
// ensure all common attribute names are mapped
// Alfresco is really inconsistent with these attribute names
session.setAttribute(AuthenticationDriver.AUTHENTICATION_USER, sessionUser);
session.setAttribute(BaseAuthenticationFilter.AUTHENTICATION_USER, sessionUser);
return sessionUser;
}
/**
*
* {@inheritDoc}
*/
@Override
protected SessionUser createUserEnvironment(final HttpSession session, final String userName, final String ticket,
final boolean externalAuth) throws IOException, ServletException
{
final SessionUser sessionUser = super.createUserEnvironment(session, userName, ticket, externalAuth);
// ensure all common attribute names are mapped
// Alfresco is really inconsistent with these attribute names
session.setAttribute(AuthenticationDriver.AUTHENTICATION_USER, sessionUser);
session.setAttribute(BaseAuthenticationFilter.AUTHENTICATION_USER, sessionUser);
return sessionUser;
}
/**
* Processes a failed authentication via Keycloak.
*
@@ -607,10 +638,6 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter
LOGGER.trace("Skipping doFilter as filter is not active");
skip = true;
}
else if (req.getAttribute(NO_AUTH_REQUIRED) != null)
{
LOGGER.trace("Skipping doFilter as filter higher up in chain determined authentication as not required");
}
else if (servletRequestUri.matches(KEYCLOAK_ACTION_URL_PATTERN))
{
LOGGER.trace("Explicitly not skipping doFilter as Keycloak action URL is being called");
@@ -636,6 +663,14 @@ public class KeycloakAuthenticationFilter extends BaseAuthenticationFilter
else if (this.allowTicketLogon && this.checkForTicketParameter(context, req, res))
{
LOGGER.trace("Skipping doFilter as user was authenticated by ticket URL parameter");
skip = true;
}
// check no-auth flag (derived e.g. from checking if target web script requires authentication) only after all pre-emptive auth
// request details have been checked
else if (Boolean.TRUE.equals(req.getAttribute(NO_AUTH_REQUIRED)))
{
LOGGER.trace("Skipping doFilter as filter higher up in chain determined authentication as not required");
skip = true;
}
else if (sessionUser != null)
{

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2019 - 2020 Acosix GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.acosix.alfresco.keycloak.repo.authentication;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpSession;
import org.alfresco.repo.SessionUser;
import org.alfresco.repo.webdav.auth.AuthenticationDriver;
import org.alfresco.repo.webdav.auth.BaseAuthenticationFilter;
import org.alfresco.web.app.servlet.WebscriptCookieAuthenticationFilter;
/**
* This sub-class of the default web script cookie filter only exists to ensure the proper session attribute names are used for mapping the
* authenticated session user.
*
* @author Axel Faust
*/
public class KeycloakWebScriptCookieAuthenticationFilter extends WebscriptCookieAuthenticationFilter
{
/**
*
* {@inheritDoc}
*/
@Override
protected SessionUser createUserEnvironment(final HttpSession session, final String userName) throws IOException, ServletException
{
final SessionUser sessionUser = super.createUserEnvironment(session, userName);
// ensure all common attribute names are mapped
// Alfresco is really inconsistent with these attribute names
session.setAttribute(AuthenticationDriver.AUTHENTICATION_USER, sessionUser);
session.setAttribute(BaseAuthenticationFilter.AUTHENTICATION_USER, sessionUser);
return sessionUser;
}
/**
*
* {@inheritDoc}
*/
@Override
protected SessionUser createUserEnvironment(final HttpSession session, final String userName, final String ticket,
final boolean externalAuth) throws IOException, ServletException
{
final SessionUser sessionUser = super.createUserEnvironment(session, userName, ticket, externalAuth);
// ensure all common attribute names are mapped
// Alfresco is really inconsistent with these attribute names
session.setAttribute(AuthenticationDriver.AUTHENTICATION_USER, sessionUser);
session.setAttribute(BaseAuthenticationFilter.AUTHENTICATION_USER, sessionUser);
return sessionUser;
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright 2019 - 2020 Acosix GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.acosix.alfresco.keycloak.repo.authentication;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.alfresco.repo.management.subsystems.ActivateableBean;
import org.alfresco.repo.web.filter.beans.DependencyInjectedFilter;
import org.alfresco.repo.webdav.auth.BaseAuthenticationFilter;
import org.alfresco.util.PropertyCheck;
import org.alfresco.web.app.servlet.WebScriptSSOAuthenticationFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.surf.util.URLDecoder;
import org.springframework.extensions.webscripts.Description.RequiredAuthentication;
import org.springframework.extensions.webscripts.Match;
import org.springframework.extensions.webscripts.RuntimeContainer;
/**
* This web script SSO authentication filter class is used instead of {@link WebScriptSSOAuthenticationFilter default Alfresco filter} in
* order to properly handle unauthenticated and guest access, especially since the later is performed by Alfresco Share to load edition
* details and potentially other data needed for determining which customisations are active, even before a user has had a chance to
* authenticate.
*
* @author Axel Faust
*/
public class KeycloakWebScriptSSOAuthenticationFilter extends BaseAuthenticationFilter
implements DependencyInjectedFilter, InitializingBean, ActivateableBean
{
// copied from WebScriptRequestImpl due to accessible constraints
private static final String ARG_GUEST = "guest";
private static final Logger LOGGER = LoggerFactory.getLogger(KeycloakWebScriptSSOAuthenticationFilter.class);
protected RuntimeContainer container;
protected boolean isActive = true;
/**
*
* {@inheritDoc}
*/
@Override
public void afterPropertiesSet()
{
PropertyCheck.mandatory(this, "container", this.container);
}
/**
* @param container
* the container to set
*/
public void setContainer(final RuntimeContainer container)
{
this.container = container;
}
/**
* Activates or deactivates the bean
*
* @param active
* <code>true</code> if the bean is active and initialisation should complete
*/
public final void setActive(final boolean active)
{
this.isActive = active;
}
/**
*
* {@inheritDoc}
*/
@Override
public final boolean isActive()
{
return this.isActive;
}
/**
*
* {@inheritDoc}
*/
@Override
public void doFilter(final ServletContext context, final ServletRequest sreq, final ServletResponse sresp, final FilterChain chain)
throws IOException, ServletException
{
final HttpServletRequest req = (HttpServletRequest) sreq;
final String requestURI = req.getRequestURI();
final String pathInfo = requestURI.substring((req.getContextPath() + req.getServletPath()).length());
LOGGER.trace("Processing request: {} SID: {}", requestURI, req.getSession(false) != null ? req.getSession().getId() : null);
final Match match = this.container.getRegistry().findWebScript(req.getMethod(), URLDecoder.decode(pathInfo));
if (match != null && match.getWebScript() != null)
{
final RequiredAuthentication reqAuth = match.getWebScript().getDescription().getRequiredAuthentication();
if (RequiredAuthentication.none == reqAuth)
{
LOGGER.debug("Found webscript with no authentication - set NO_AUTH_REQUIRED flag.");
req.setAttribute(NO_AUTH_REQUIRED, Boolean.TRUE);
}
else if (RequiredAuthentication.guest == reqAuth && Boolean.parseBoolean(sreq.getParameter(ARG_GUEST)))
{
LOGGER.debug("Found webscript with guest authentication and request with set guest parameter - set NO_AUTH_REQUIRED flag.");
req.setAttribute(NO_AUTH_REQUIRED, Boolean.TRUE);
}
}
chain.doFilter(sreq, sresp);
}
/**
*
* {@inheritDoc}
*/
@Override
// ugh - Commons Logging - why does base class not have a sensible default??
protected Log getLogger()
{
return LogFactory.getLog(this.getClass());
}
}

View File

@@ -312,7 +312,7 @@ public class RoleServiceImpl implements InitializingBean, RoleService
}
else
{
LOGGER.debug("No role mapper defined for resource {}", roleNameMapper);
LOGGER.debug("No role mapper defined for resource {}", resourceName);
roles = Collections.emptyList();
}
}

File diff suppressed because it is too large Load Diff