Merged DEV to 5.2.N (5.2.1)

133903 sglover: MNT-17247 "Disabled user can log into Alfresco Share using external authentication" take user disabled status in to account for external authentication subsystem + tests
   133907 sglover: MNT-17247 "Disabled user can log into Alfresco Share using external authentication" don't propagate user disabled exception
   133930 sglover: MNT-17247 "Disabled user can log into Alfresco Share using external authentication" move test class and add to a test suite
   134295 amukha: MNT-17247: Disabled user can log into Alfresco Share using external authentication
      - Added a test to simulate creation of missing person during external auth log in.
   134315 amukha: MNT-17247: Disabled user can log into Alfresco Share using external authentication
      - Added a fallback to supprt the logging in by non provisioned users.
   134354 amukha: MNT-17247: Disabled user can log into Alfresco Share using external authentication
      - Added a test with deauthorized user. Refactored existing test to start context once.
   134359 jvonka: REPO-1227: External authentication - prevent disabled user from authenticating
      - add log warning (with masked username, similar to brute force attack) if authentication bypassed when setting user details
   134372 amukha: MNT-17247: Disabled user can log into Alfresco Share using external authentication
      - Updated core and data model (contain new logging)
   134390 amukha: MNT-17247: Disabled user can log into Alfresco Share using external authentication
      - isEnabled flag for users is returned correctly
      - Added tests


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@134396 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alex Mukha
2017-01-17 13:57:05 +00:00
parent 868f0029d3
commit 0d8bfe6aa7
3 changed files with 246 additions and 6 deletions

View File

@@ -27,6 +27,7 @@ package org.alfresco.repo.web.scripts.servlet;
import javax.servlet.http.HttpSession;
import org.alfresco.error.ExceptionStackUtil;
import org.alfresco.repo.SessionUser;
import org.alfresco.repo.management.subsystems.ActivateableBean;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
@@ -43,6 +44,8 @@ import org.springframework.extensions.webscripts.Description.RequiredAuthenticat
import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest;
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
import net.sf.acegisecurity.DisabledException;
/**
* Authenticator to provide Remote User based Header authentication dropping back to Basic Auth otherwise.
* Statelessly authenticating via a secure header now does not require a Session so can be used with
@@ -98,11 +101,27 @@ public class RemoteUserAuthenticatorFactory extends BasicHttpAuthenticatorFactor
// retrieve the remote user if configured and available - authenticate that user directly
final String userId = getRemoteUser();
if (userId != null)
{
try
{
authenticationComponent.setCurrentUser(userId);
listener.userAuthenticated(new TicketCredentials(authenticationService.getCurrentTicket()));
authenticated = true;
}
catch (AuthenticationException authErr)
{
// don't propagate if the user is disabled
Throwable disabledCause = ExceptionStackUtil.getCause(authErr, DisabledException.class);
if(disabledCause != null)
{
listener.authenticationFailed(new WebCredentials() {});
}
else
{
throw authErr;
}
}
}
else
{
// is there a Session which might contain a valid user ticket?

View File

@@ -61,6 +61,7 @@ public class RemoteApi01TestSuite extends TestSuite
suite.addTestSuite(org.alfresco.repo.management.subsystems.test.SubsystemsTest.class);
suite.addTestSuite(org.alfresco.repo.remoteticket.RemoteAlfrescoTicketServiceTest.class);
suite.addTest(new JUnit4TestAdapter(org.alfresco.rest.api.tests.TestCustomModelExport.class));
suite.addTest(new JUnit4TestAdapter(org.alfresco.repo.web.scripts.servlet.RemoteAuthenticatorFactoryTest.class));
}
static void tests2(TestSuite suite) //

View File

@@ -0,0 +1,220 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.web.scripts.servlet;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.management.subsystems.DefaultChildApplicationContextManager;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.GUID;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.extensions.webscripts.Authenticator;
import org.springframework.extensions.webscripts.Description.RequiredAuthentication;
import org.springframework.extensions.webscripts.servlet.WebScriptServletRequest;
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
/**
*
* @author sglover
*
*/
public class RemoteAuthenticatorFactoryTest
{
private static final String[] contextLocations = new String[] {
"classpath:alfresco/application-context.xml",
"classpath:alfresco/web-scripts-application-context.xml",
"classpath:alfresco/web-scripts-application-context-test.xml"
};
private static RemoteUserAuthenticatorFactory remoteUserAuthenticatorFactory;
private static PersonService personService;
private static TransactionService transactionService;
private static MutableAuthenticationDao authenticationDAO;
@BeforeClass
public static void beforeClass() throws Exception
{
ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(contextLocations);
DefaultChildApplicationContextManager childApplicationContextManager = (DefaultChildApplicationContextManager) ctx.getBean("Authentication");
remoteUserAuthenticatorFactory = (RemoteUserAuthenticatorFactory) ctx.getBean("webscripts.authenticator.remoteuser");
personService = (PersonService)ctx.getBean("PersonService");
transactionService = (TransactionService)ctx.getBean("TransactionService");
authenticationDAO = (MutableAuthenticationDao)ctx.getBean("authenticationDao");
childApplicationContextManager.stop();
childApplicationContextManager.setProperty("chain", "external1:external");
ChildApplicationContextFactory childApplicationContextFactory = childApplicationContextManager.getChildApplicationContextFactory("external1");
childApplicationContextFactory.stop();
childApplicationContextFactory.setProperty("external.authentication.proxyUserName", "");
}
private String createPerson(boolean enabled)
{
Map<QName, Serializable> properties = new HashMap<>();
String username = "user" + GUID.generate();
properties.put(ContentModel.PROP_USERNAME, username);
properties.put(ContentModel.PROP_FIRSTNAME, username);
properties.put(ContentModel.PROP_LASTNAME, username);
if(!enabled)
{
properties.put(ContentModel.PROP_ENABLED, enabled);
}
personService.createPerson(properties);
authenticationDAO.createUser(username, "password".toCharArray());
authenticationDAO.setEnabled(username, enabled);
return username;
}
@Test
public void testDisabledUser() throws Exception
{
final String username = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<String>()
{
@Override
public String execute() throws Throwable
{
return AuthenticationUtil.runAs(new RunAsWork<String>()
{
@Override
public String doWork() throws Exception
{
return createPerson(false);
}
}, AuthenticationUtil.SYSTEM_USER_NAME);
}
}, false, true);
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>()
{
@Override
public Void execute() throws Throwable
{
return AuthenticationUtil.runAs(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
// Mock a request with a username in the header
HttpServletRequest mockHttpRequest = mock(HttpServletRequest.class);
when(mockHttpRequest.getHeader("X-Alfresco-Remote-User")).thenReturn(username);
when(mockHttpRequest.getScheme()).thenReturn("http");
WebScriptServletRequest mockRequest = mock(WebScriptServletRequest.class);
when(mockRequest.getHttpServletRequest()).thenReturn(mockHttpRequest);
HttpServletResponse mockHttpResponse = mock(HttpServletResponse.class);
WebScriptServletResponse mockResponse = mock(WebScriptServletResponse.class);
when(mockResponse.getHttpServletResponse()).thenReturn(mockHttpResponse);
Authenticator authenticator = remoteUserAuthenticatorFactory.create(mockRequest, mockResponse);
assertFalse(authenticator.authenticate(RequiredAuthentication.user, false));
return null;
}
}, AuthenticationUtil.SYSTEM_USER_NAME);
}
}, false, true);
}
@Test
public void testEnabledUser() throws Exception
{
final String username = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<String>()
{
@Override
public String execute() throws Throwable
{
return AuthenticationUtil.runAs(new RunAsWork<String>()
{
@Override
public String doWork() throws Exception
{
return createPerson(true);
}
}, AuthenticationUtil.SYSTEM_USER_NAME);
}
}, false, true);
// Mock a request with a username in the header
HttpServletRequest mockHttpRequest = mock(HttpServletRequest.class);
when(mockHttpRequest.getHeader("X-Alfresco-Remote-User")).thenReturn(username);
when(mockHttpRequest.getScheme()).thenReturn("http");
WebScriptServletRequest mockRequest = mock(WebScriptServletRequest.class);
when(mockRequest.getHttpServletRequest()).thenReturn(mockHttpRequest);
HttpServletResponse mockHttpResponse = mock(HttpServletResponse.class);
WebScriptServletResponse mockResponse = mock(WebScriptServletResponse.class);
when(mockResponse.getHttpServletResponse()).thenReturn(mockHttpResponse);
Authenticator authenticator = remoteUserAuthenticatorFactory.create(mockRequest, mockResponse);
assertTrue(authenticator.authenticate(RequiredAuthentication.user, false));
}
@Test
public void testLogInWithNonExistingPerson()
{
// Random non existing person
final String username = GUID.generate();
// Mock a request with a username in the header
HttpServletRequest mockHttpRequest = mock(HttpServletRequest.class);
when(mockHttpRequest.getHeader("X-Alfresco-Remote-User")).thenReturn(username);
when(mockHttpRequest.getScheme()).thenReturn("http");
WebScriptServletRequest mockRequest = mock(WebScriptServletRequest.class);
when(mockRequest.getHttpServletRequest()).thenReturn(mockHttpRequest);
HttpServletResponse mockHttpResponse = mock(HttpServletResponse.class);
WebScriptServletResponse mockResponse = mock(WebScriptServletResponse.class);
when(mockResponse.getHttpServletResponse()).thenReturn(mockHttpResponse);
Authenticator authenticator = remoteUserAuthenticatorFactory.create(mockRequest, mockResponse);
assertTrue("The non existing user should be authenticated.", authenticator.authenticate(RequiredAuthentication.user, false));
assertTrue("The user should be auto created.", personService.personExists(username));
}
}