diff --git a/config/alfresco/subsystems/Authentication/external/external-filter-context.xml b/config/alfresco/subsystems/Authentication/external/external-filter-context.xml
index 2a89b9e61a..40e39cfef2 100644
--- a/config/alfresco/subsystems/Authentication/external/external-filter-context.xml
+++ b/config/alfresco/subsystems/Authentication/external/external-filter-context.xml
@@ -16,6 +16,9 @@
${external.authentication.userIdPattern}
+
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/web/app/servlet/DefaultRemoteUserMapper.java b/source/java/org/alfresco/web/app/servlet/DefaultRemoteUserMapper.java
index 5a0c740a1a..c50f82298f 100644
--- a/source/java/org/alfresco/web/app/servlet/DefaultRemoteUserMapper.java
+++ b/source/java/org/alfresco/web/app/servlet/DefaultRemoteUserMapper.java
@@ -24,14 +24,18 @@ import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.alfresco.repo.management.subsystems.ActivateableBean;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.service.cmr.security.PersonService;
/**
- * A default {@link RemoteUserMapper} implementation. Extracts the user ID using
- * {@link HttpServletRequest#getRemoteUser()}. If it matches the configured proxy user name or the configured proxy user
- * name is null, it extracts the user ID from the configured proxy request header. Otherwise returns the remote user
- * name. An optional regular expression defining how to convert the header to a user ID can be configured using
- * {@link #setUserIdPattern(String)}. This allows for the secure proxying of requests from a Surf client such as
- * Alfresco Share using SSL client certificates.
+ * A default {@link RemoteUserMapper} implementation. Extracts a user ID using
+ * {@link HttpServletRequest#getRemoteUser()} and optionally from a configured request header. If there is no configured
+ * proxy user name, it returns the request header user name if there is one, or the remote user name otherwise. If there
+ * is a configured proxy user, then it returns the request header user name if the remote user matches the proxy user,
+ * or the remote user otherwise. An optional regular expression defining how to convert the header to a user ID can be
+ * configured using {@link #setUserIdPattern(String)}. This allows for the secure proxying of requests from a Surf
+ * client such as Alfresco Share using SSL client certificates.
*
* @author dward
*/
@@ -49,6 +53,9 @@ public class DefaultRemoteUserMapper implements RemoteUserMapper, ActivateableBe
/** Regular expression for extracting a user ID from the header. */
private Pattern userIdPattern;
+ /** The person service. */
+ private PersonService personService;
+
/**
* Sets the name of the remote user used to 'proxy' requests securely in the name of another user. Typically this
* remote identity will be protected by an SSL client certificate.
@@ -70,7 +77,7 @@ public class DefaultRemoteUserMapper implements RemoteUserMapper, ActivateableBe
*/
public void setProxyHeader(String proxyHeader)
{
- this.proxyHeader = proxyHeader;
+ this.proxyHeader = proxyHeader == null || proxyHeader.length() == 0 ? null : proxyHeader;
}
/**
@@ -98,6 +105,17 @@ public class DefaultRemoteUserMapper implements RemoteUserMapper, ActivateableBe
.compile(userIdPattern);
}
+ /**
+ * Sets the person service.
+ *
+ * @param personService
+ * the person service
+ */
+ public void setPersonService(PersonService personService)
+ {
+ this.personService = personService;
+ }
+
/*
* (non-Javadoc)
* @see org.alfresco.web.app.servlet.RemoteUserMapper#getRemoteUser(javax.servlet.http.HttpServletRequest)
@@ -108,26 +126,49 @@ public class DefaultRemoteUserMapper implements RemoteUserMapper, ActivateableBe
{
return null;
}
+ String remoteUserId = request.getRemoteUser();
+ String headerUserId = extractUserFromProxyHeader(request);
if (this.proxyUserName == null)
{
- return extractUserFromProxyHeader(request);
+ // Normalize the user ID taking into account case sensitivity settings
+ return normalizeUserId(headerUserId != null ? headerUserId : remoteUserId);
+ }
+ else if (remoteUserId == null)
+ {
+ return null;
}
else
{
- String userId = request.getRemoteUser();
- if (userId == null)
- {
- return null;
- }
- if (userId.equals(this.proxyUserName))
- {
- userId = extractUserFromProxyHeader(request);
- }
- return userId;
+ // Normalize the user ID taking into account case sensitivity settings
+ return normalizeUserId(remoteUserId.equals(this.proxyUserName) ? headerUserId : remoteUserId);
}
}
- /* (non-Javadoc)
+ /**
+ * Normalizes a user id, taking into account existing user accounts and case sensitivity settings.
+ *
+ * @param userId
+ * the user id
+ * @return the string
+ */
+ private String normalizeUserId(final String userId)
+ {
+ if (userId == null)
+ {
+ return null;
+ }
+ String normalized = AuthenticationUtil.runAs(new RunAsWork()
+ {
+ public String doWork() throws Exception
+ {
+ return personService.getUserIdentifier(userId);
+ }
+ }, AuthenticationUtil.getSystemUserName());
+ return normalized == null ? userId : normalized;
+ }
+
+ /*
+ * (non-Javadoc)
* @see org.alfresco.repo.management.subsystems.ActivateableBean#isActive()
*/
public boolean isActive()
@@ -146,6 +187,10 @@ public class DefaultRemoteUserMapper implements RemoteUserMapper, ActivateableBe
*/
private String extractUserFromProxyHeader(HttpServletRequest request)
{
+ if (this.proxyHeader == null)
+ {
+ return null;
+ }
String userId = request.getHeader(this.proxyHeader);
if (userId == null)
{
@@ -160,7 +205,7 @@ public class DefaultRemoteUserMapper implements RemoteUserMapper, ActivateableBe
Matcher matcher = this.userIdPattern.matcher(userId);
if (matcher.matches())
{
- userId = matcher.group().trim();
+ userId = matcher.group(1).trim();
}
}
return userId.length() == 0 ? null : userId;
diff --git a/source/java/org/alfresco/web/app/servlet/DefaultRemoteUserMapperTest.java b/source/java/org/alfresco/web/app/servlet/DefaultRemoteUserMapperTest.java
new file mode 100644
index 0000000000..cb2d0261ca
--- /dev/null
+++ b/source/java/org/alfresco/web/app/servlet/DefaultRemoteUserMapperTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have received a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+package org.alfresco.web.app.servlet;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.alfresco.repo.management.subsystems.AbstractChainedSubsystemTest;
+import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
+import org.alfresco.repo.management.subsystems.DefaultChildApplicationContextManager;
+import org.alfresco.util.ApplicationContextHelper;
+import org.springframework.context.ApplicationContext;
+
+
+/**
+ * @author dward
+ *
+ */
+public class DefaultRemoteUserMapperTest extends AbstractChainedSubsystemTest
+{
+ ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+ DefaultChildApplicationContextManager childApplicationContextManager;
+ ChildApplicationContextFactory childApplicationContextFactory;
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#setUp()
+ */
+ @Override
+ protected void setUp() throws Exception
+ {
+ childApplicationContextManager = (DefaultChildApplicationContextManager) ctx.getBean("Authentication");
+ childApplicationContextManager.stop();
+ childApplicationContextManager.setProperty("chain", "external1:external");
+ childApplicationContextFactory = getChildApplicationContextFactory(childApplicationContextManager, "external1");
+ }
+
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception
+ {
+ childApplicationContextManager.destroy();
+ childApplicationContextManager = null;
+ childApplicationContextFactory = null;
+ }
+
+
+ public void testUnproxiedHeader() throws Exception
+ {
+ // Clear the proxy user name
+ childApplicationContextFactory.stop();
+ childApplicationContextFactory.setProperty("external.authentication.proxyUserName", "");
+
+ // Mock a request with a username in the header
+ HttpServletRequest mockRequest = mock(HttpServletRequest.class);
+ when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn("AdMiN");
+ assertEquals("admin", ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
+ "remoteUserMapper")).getRemoteUser(mockRequest));
+
+ // Mock an unauthenticated request
+ when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn(null);
+ assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
+ "remoteUserMapper")).getRemoteUser(mockRequest));
+
+ // Mock a remote user request
+ when(mockRequest.getRemoteUser()).thenReturn("ADMIN");
+ assertEquals("admin", ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
+ "remoteUserMapper")).getRemoteUser(mockRequest));
+ }
+
+
+ public void testProxiedHeader() throws Exception
+ {
+ // Set the proxy user name
+ childApplicationContextFactory.stop();
+ childApplicationContextFactory.setProperty("external.authentication.proxyUserName", "bob");
+
+ // Mock a request with both a user and a header
+ HttpServletRequest mockRequest = mock(HttpServletRequest.class);
+ when(mockRequest.getRemoteUser()).thenReturn("bob");
+ when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn("AdMiN");
+ assertEquals("admin", ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
+ "remoteUserMapper")).getRemoteUser(mockRequest));
+
+ // Now try header pattern matching
+ childApplicationContextFactory.stop();
+ childApplicationContextFactory.setProperty("external.authentication.userIdPattern", "abc-(.*)-999");
+ when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn("abc-AdMiN-999");
+ assertEquals("admin", ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
+ "remoteUserMapper")).getRemoteUser(mockRequest));
+
+ // Try a request without the remote user
+ when(mockRequest.getRemoteUser()).thenReturn(null);
+ assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean(
+ "remoteUserMapper")).getRemoteUser(mockRequest));
+
+ }
+
+}
diff --git a/source/java/org/alfresco/web/sharepoint/auth/BasicAuthenticationHandler.java b/source/java/org/alfresco/web/sharepoint/auth/BasicAuthenticationHandler.java
index 36a6aa5a9a..dc541bbfea 100644
--- a/source/java/org/alfresco/web/sharepoint/auth/BasicAuthenticationHandler.java
+++ b/source/java/org/alfresco/web/sharepoint/auth/BasicAuthenticationHandler.java
@@ -67,10 +67,16 @@ public class BasicAuthenticationHandler extends AbstractAuthenticationHandler
try
{
if (logger.isDebugEnabled())
- logger.debug("Authenticate the user '" + username + "'");
+ logger.debug("Authenticating user '" + username + "'");
authenticationService.authenticate(username, password.toCharArray());
+ // Normalize the user ID taking into account case sensitivity settings
+ username = authenticationService.getCurrentUserName();
+
+ if (logger.isDebugEnabled())
+ logger.debug("Authenticated user '" + username + "'");
+
if (mapper.isSiteMember(request, alfrescoContext, username))
{
user = new User(username, authenticationService.getCurrentTicket(session.getId()), personService.getPerson(username));
diff --git a/source/java/org/alfresco/web/sharepoint/auth/ntlm/NtlmAuthenticationHandler.java b/source/java/org/alfresco/web/sharepoint/auth/ntlm/NtlmAuthenticationHandler.java
index 4c263a6f4f..472662bee2 100644
--- a/source/java/org/alfresco/web/sharepoint/auth/ntlm/NtlmAuthenticationHandler.java
+++ b/source/java/org/alfresco/web/sharepoint/auth/ntlm/NtlmAuthenticationHandler.java
@@ -417,7 +417,7 @@ public class NtlmAuthenticationHandler extends AbstractAuthenticationHandler imp
}
// Check if the user has been authenticated, if so then setup the user environment
- if (authenticated == true && callback.isSiteMember(request, alfrescoContext, userName.toLowerCase()))
+ if (authenticated == true && callback.isSiteMember(request, alfrescoContext, userName))
{
String uri = request.getRequestURI();