diff --git a/source/java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapper.java b/source/java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapper.java index f454cb0ab6..5591e5584d 100644 --- a/source/java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapper.java +++ b/source/java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapper.java @@ -19,6 +19,7 @@ package org.alfresco.repo.security.authentication.external; import java.io.UnsupportedEncodingException; +import java.security.cert.X509Certificate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -152,9 +153,40 @@ public class DefaultRemoteUserMapper implements RemoteUserMapper, ActivateableBe } else if (remoteUserId == null) { + String normalizedUserId = null; + // Try to extract the remote user from SSL certificate + // MNT-13989 + X509Certificate[] certs = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); + if (request.getScheme().toLowerCase().equals("https") && certs != null && certs.length > 0) + { + if (logger.isDebugEnabled()) + { + logger.debug("Checking SSL certificate subject DN to match " + this.proxyUserName); + } + for (int i = 0; i < certs.length; i++) + { + String subjectDN = certs[i].getSubjectX500Principal().getName(); + if (logger.isDebugEnabled()) + { + logger.debug("Found subject DN " + subjectDN); + } + if (subjectDN.equals(this.proxyUserName)) + { + if (logger.isDebugEnabled()) + { + logger.debug("The subject DN " + subjectDN + " matches " + this.proxyUserName); + } + // Found the subject distinguished name + remoteUserId = subjectDN; + // Normalize the user ID taking into account case sensitivity settings + normalizedUserId = normalizeUserId(headerUserId != null ? headerUserId : remoteUserId); + break; + } + } + } if (logger.isDebugEnabled()) - logger.debug("Returning null"); - return null; + logger.debug("Returning " + normalizedUserId); + return normalizedUserId; } else { diff --git a/source/test-java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapperTest.java b/source/test-java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapperTest.java index b7ae51bd20..a85dd279aa 100644 --- a/source/test-java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapperTest.java +++ b/source/test-java/org/alfresco/repo/security/authentication/external/DefaultRemoteUserMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2015 Alfresco Software Limited. * * This file is part of Alfresco * @@ -21,15 +21,17 @@ package org.alfresco.repo.security.authentication.external; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import javax.security.auth.x500.X500Principal; 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.repo.security.authentication.external.RemoteUserMapper; import org.alfresco.util.ApplicationContextHelper; import org.springframework.context.ApplicationContext; +import java.security.cert.X509Certificate; + /** * @author dward @@ -82,7 +84,7 @@ public class DefaultRemoteUserMapperTest extends AbstractChainedSubsystemTest // Mock an unauthenticated request when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn(null); assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean( - "remoteUserMapper")).getRemoteUser(mockRequest)); + "remoteUserMapper")).getRemoteUser(mockRequest)); // Mock a remote user request when(mockRequest.getRemoteUser()).thenReturn("ADMIN"); @@ -99,6 +101,7 @@ public class DefaultRemoteUserMapperTest extends AbstractChainedSubsystemTest // Mock a request with both a user and a header HttpServletRequest mockRequest = mock(HttpServletRequest.class); + when(mockRequest.getScheme()).thenReturn("http"); when(mockRequest.getRemoteUser()).thenReturn("bob"); when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn("AdMiN"); assertEquals("admin", ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean( @@ -120,6 +123,48 @@ public class DefaultRemoteUserMapperTest extends AbstractChainedSubsystemTest when(mockRequest.getRemoteUser()).thenReturn(null); assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean( "remoteUserMapper")).getRemoteUser(mockRequest)); - } - + } + + /** + * MNT-13989 + * Test simulates the extraction of subject distinguished name from a client SSL certificate + * + * @throws Exception + */ + public void testRemoteUserFromCert() throws Exception + { + childApplicationContextFactory.stop(); + childApplicationContextFactory.setProperty("external.authentication.proxyUserName", "CN=alfresco-system"); + childApplicationContextFactory.setProperty("external.authentication.proxyHeader", "X-Alfresco-Remote-User"); + + X509Certificate cert = mock(X509Certificate.class); + X500Principal principal = new X500Principal("CN=alfresco-system"); + when(cert.getSubjectX500Principal()).thenReturn(principal); + X509Certificate[] certs = {cert}; + HttpServletRequest mockRequest = mock(HttpServletRequest.class); + + // Mock a request with a header and a cert + when(mockRequest.getRemoteUser()).thenReturn(null); + when(mockRequest.getScheme()).thenReturn("https"); + when(mockRequest.getAttribute("javax.servlet.request.X509Certificate")).thenReturn(certs); + when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn("admin"); + assertEquals("admin", ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean( + "remoteUserMapper")).getRemoteUser(mockRequest)); + + // Mock a request with a cert and no header + when(mockRequest.getRemoteUser()).thenReturn(null); + when(mockRequest.getScheme()).thenReturn("https"); + when(mockRequest.getAttribute("javax.servlet.request.X509Certificate")).thenReturn(certs); + when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn(null); + assertEquals("CN=alfresco-system", ((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean( + "remoteUserMapper")).getRemoteUser(mockRequest)); + + // Mock a request with no cert and a header + when(mockRequest.getRemoteUser()).thenReturn(null); + when(mockRequest.getScheme()).thenReturn("http"); + when(mockRequest.getAttribute("javax.servlet.request.X509Certificate")).thenReturn(null); + when(mockRequest.getHeader("X-Alfresco-Remote-User")).thenReturn("admin"); + assertNull(((RemoteUserMapper) childApplicationContextFactory.getApplicationContext().getBean( + "remoteUserMapper")).getRemoteUser(mockRequest)); + } }