From 49c93a59787913ee91b0f532d8316e84f1e0d89e Mon Sep 17 00:00:00 2001 From: Alan Davis Date: Fri, 16 May 2014 16:29:37 +0000 Subject: [PATCH] Merged HEAD-BUG-FIX (4.3/Cloud) to HEAD (4.3/Cloud) 68527: Merged V4.2-BUG-FIX (4.2.3) to HEAD-BUG-FIX (4.3/Cloud) 68302: Merged V4.1-BUG-FIX (4.1.9) to V4.2-BUG-FIX (4.2.3) 68117: MNT-11315: Merged V4.1.6 (4.1.6.17) to V4.1-BUG-FIX (4.1.9) 64849: Merged DEV to PATCHES/V4.1.6 (4.1.6.11) 63976 : MNT-10797 : Support HTTP Basic auth for /alfresco/cmisatom CMIS binding when using kerberos - Added fallback authentication mechanism for webdav authentication filter for kerberos. So it should be possible to login via BASIC authentication when kerbeross SSO enabled. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@70409 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../BaseKerberosAuthenticationFilter.java | 10 + .../auth/BaseSSOAuthenticationFilter.java | 67 +++++++ .../SSOFallbackBasicAuthenticationDriver.java | 184 ++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 source/java/org/alfresco/repo/webdav/auth/SSOFallbackBasicAuthenticationDriver.java diff --git a/source/java/org/alfresco/repo/webdav/auth/BaseKerberosAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/BaseKerberosAuthenticationFilter.java index bf7a5a5d51..bdb4138012 100644 --- a/source/java/org/alfresco/repo/webdav/auth/BaseKerberosAuthenticationFilter.java +++ b/source/java/org/alfresco/repo/webdav/auth/BaseKerberosAuthenticationFilter.java @@ -274,6 +274,10 @@ public abstract class BaseKerberosAuthenticationFilter extends BaseSSOAuthentica restartLoginChallenge(context, req, resp); return false; } + else if (isFallbackEnabled()) + { + return performFallbackAuthentication(context, req, resp); + } } // Check if the user is already authenticated @@ -631,6 +635,12 @@ public abstract class BaseKerberosAuthenticationFilter extends BaseSSOAuthentica getLogger().debug("Issuing login challenge to browser."); // Force the logon to start again resp.setHeader("WWW-Authenticate", "Negotiate"); + + if (isFallbackEnabled()) + { + includeFallbackAuth(context, req, resp); + } + resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); writeLoginPageLink(context, req, resp); diff --git a/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java b/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java index 368608fefe..34d160fa53 100644 --- a/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java +++ b/source/java/org/alfresco/repo/webdav/auth/BaseSSOAuthenticationFilter.java @@ -73,6 +73,8 @@ public abstract class BaseSSOAuthenticationFilter extends BaseAuthenticationFilt private boolean m_isActive = true; + private AuthenticationDriver fallbackDelegate; + protected static final String MIME_HTML_TEXT = "text/html"; /** @@ -104,6 +106,24 @@ public abstract class BaseSSOAuthenticationFilter extends BaseAuthenticationFilt return m_isActive; } + /** + * Activates or deactivates the fallback authentication support for this filter + * + * @param delegate + */ + public final void setFallback(AuthenticationDriver delegate) + { + this.fallbackDelegate = delegate; + } + + /** + * @return true if fallback authentication enabled + */ + public final boolean isFallbackEnabled() + { + return fallbackDelegate != null; + } + /* * (non-Javadoc) * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() @@ -551,6 +571,53 @@ public abstract class BaseSSOAuthenticationFilter extends BaseAuthenticationFilt } } + /** + * Include into response authentication method that is supported by fallback mechanism + * + * @param context ServletContext + * @param req HttpServletRequest + * @param resp HttpServletResponse + * @throws IOException + */ + protected void includeFallbackAuth(ServletContext context, HttpServletRequest req, HttpServletResponse resp) throws IOException + { + fallbackDelegate.restartLoginChallenge(context, req, resp); + } + + /** + * Delegate authentication to the fallback mechanism + * + * @param context ServletContext + * @param req HttpServletRequest + * @param resp HttpServletResponse + * @return + * @throws IOException + * @throws ServletException + */ + protected boolean performFallbackAuthentication(ServletContext context, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException + { + if (getLogger().isDebugEnabled()) + { + getLogger().debug("Performing fallback authentication..."); + } + + boolean fallbackSuccess = fallbackDelegate.authenticateRequest(context, req, resp); + + if (!fallbackSuccess) + { + restartLoginChallenge(context, req, resp); + if (getLogger().isDebugEnabled()) + { + getLogger().debug("Fallback authentication failed. Restarting login..."); + } + } + if (fallbackSuccess && getLogger().isDebugEnabled()) + { + getLogger().debug("Fallback authentication succeeded."); + } + + return fallbackSuccess; + } } diff --git a/source/java/org/alfresco/repo/webdav/auth/SSOFallbackBasicAuthenticationDriver.java b/source/java/org/alfresco/repo/webdav/auth/SSOFallbackBasicAuthenticationDriver.java new file mode 100644 index 0000000000..b41081045a --- /dev/null +++ b/source/java/org/alfresco/repo/webdav/auth/SSOFallbackBasicAuthenticationDriver.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.webdav.auth; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.SessionUser; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + *

+ * Basic HTTP web authentication implementation. Main purpose to use as fallback authentication with SSO filters. + *

+ * + * @author pavel.yurkevich + */ +public class SSOFallbackBasicAuthenticationDriver implements AuthenticationDriver +{ + public static final String AUTHENTICATION_USER = "_alfAuthTicket"; + + private Log logger = LogFactory.getLog(SSOFallbackBasicAuthenticationDriver.class); + + private AuthenticationService authenticationService; + private PersonService personService; + private NodeService nodeService; + private TransactionService transactionService; + + private String userAttributeName = AUTHENTICATION_USER; + + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setUserAttributeName(String userAttributeName) + { + this.userAttributeName = userAttributeName; + } + + @Override + public boolean authenticateRequest(ServletContext context, HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException + { + String authHdr = request.getHeader("Authorization"); + HttpSession session = request.getSession(false); + SessionUser user = session == null ? null : (SessionUser) session.getAttribute(userAttributeName); + if (user == null) + { + if (authHdr != null && authHdr.length() > 5 && authHdr.substring(0, 5).equalsIgnoreCase("Basic")) + { + String basicAuth = new String(Base64.decodeBase64(authHdr.substring(5).getBytes())); + String username = null; + String password = null; + + int pos = basicAuth.indexOf(":"); + if (pos != -1) + { + username = basicAuth.substring(0, pos); + password = basicAuth.substring(pos + 1); + } + else + { + username = basicAuth; + password = ""; + } + + try + { + if (logger.isDebugEnabled()) + logger.debug("Authenticating user '" + username + "'"); + + authenticationService.authenticate(username, password.toCharArray()); + + final RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + @Override + public SessionUser execute() throws Throwable + { + NodeRef personNodeRef = personService.getPerson(authenticationService.getCurrentUserName()); + String username = (String) nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); + NodeRef homeSpaceRef = (NodeRef) nodeService.getProperty(personNodeRef, ContentModel.PROP_HOMEFOLDER); + + return new WebDAVUser(username, authenticationService.getCurrentTicket(), homeSpaceRef); + } + }; + + user = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public SessionUser doWork() throws Exception + { + return transactionService.getRetryingTransactionHelper().doInTransaction(callback, true); + } + }, AuthenticationUtil.SYSTEM_USER_NAME); + + if (logger.isDebugEnabled()) + logger.debug("Authenticated user '" + username + "'"); + + request.getSession().setAttribute(userAttributeName, user); + return true; + } + catch (AuthenticationException ex) + { + // Do nothing, user object will be null + } + } + } + else + { + try + { + authenticationService.validate(user.getTicket()); + return true; + } + catch (AuthenticationException ex) + { + session.invalidate(); + } + } + + return false; + } + + @Override + public void restartLoginChallenge(ServletContext context, HttpServletRequest request, HttpServletResponse response) + throws IOException + { + if (logger.isDebugEnabled()) + { + logger.debug("Including Basic HTTP authentication into response headers..."); + } + + response.addHeader("WWW-Authenticate", "Basic realm=\"Alfresco Server\""); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } + +}