/*
* Copyright (C) 2005-2010 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.security.authentication;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import net.sf.acegisecurity.AccountExpiredException;
import net.sf.acegisecurity.Authentication;
import net.sf.acegisecurity.AuthenticationManager;
import net.sf.acegisecurity.BadCredentialsException;
import net.sf.acegisecurity.CredentialsExpiredException;
import net.sf.acegisecurity.DisabledException;
import net.sf.acegisecurity.LockedException;
import net.sf.acegisecurity.UserDetails;
import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.management.subsystems.ChildApplicationContextManager;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.ExpiryMode;
import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.Ticket;
import org.alfresco.repo.security.person.UserNameMatcher;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.DynamicNamespacePrefixResolver;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.springframework.context.ApplicationContext;
@SuppressWarnings("unchecked")
public class AuthenticationTest extends TestCase
{
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private NodeService nodeService;
private TenantService tenantService;
private SearchService searchService;
private NodeRef rootNodeRef;
private NodeRef systemNodeRef;
private NodeRef typesNodeRef;
private NodeRef personAndyNodeRef;
private DictionaryService dictionaryService;
private MD4PasswordEncoder passwordEncoder;
private MutableAuthenticationDao dao;
private AuthenticationManager authenticationManager;
private TicketComponent ticketComponent;
private SimpleCache ticketsCache;
private MutableAuthenticationService authenticationService;
private MutableAuthenticationService pubAuthenticationService;
private AuthenticationComponent authenticationComponent;
private UserTransaction userTransaction;
private AuthenticationComponent authenticationComponentImpl;
private TransactionService transactionService;
private PersonService pubPersonService;
private PersonService personService;
private UserNameMatcher userNameMatcher;
public AuthenticationTest()
{
super();
}
public AuthenticationTest(String arg0)
{
super(arg0);
}
public void setUp() throws Exception
{
nodeService = (NodeService) ctx.getBean("nodeService");
tenantService = (TenantService) ctx.getBean("tenantService");
searchService = (SearchService) ctx.getBean("searchService");
dictionaryService = (DictionaryService) ctx.getBean("dictionaryService");
passwordEncoder = (MD4PasswordEncoder) ctx.getBean("passwordEncoder");
ticketComponent = (TicketComponent) ctx.getBean("ticketComponent");
authenticationService = (MutableAuthenticationService) ctx.getBean("authenticationService");
pubAuthenticationService = (MutableAuthenticationService) ctx.getBean("AuthenticationService");
authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent");
authenticationComponentImpl = (AuthenticationComponent) ctx.getBean("authenticationComponent");
pubPersonService = (PersonService) ctx.getBean("PersonService");
personService = (PersonService) ctx.getBean("personService");
userNameMatcher = (UserNameMatcher) ctx.getBean("userNameMatcher");
// permissionServiceSPI = (PermissionServiceSPI)
// ctx.getBean("permissionService");
ticketsCache = (SimpleCache) ctx.getBean("ticketsCache");
dao = (MutableAuthenticationDao) ctx.getBean("authenticationDao");
// Let's look inside the alfresco authentication subsystem to get the DAO-wired authentication manager
ChildApplicationContextManager authenticationChain = (ChildApplicationContextManager) ctx.getBean("Authentication");
ApplicationContext subsystem = authenticationChain.getApplicationContext(authenticationChain.getInstanceIds().iterator().next());
authenticationManager = (AuthenticationManager) subsystem.getBean("authenticationManager");
transactionService = (TransactionService) ctx.getBean(ServiceRegistry.TRANSACTION_SERVICE.getLocalName());
userTransaction = transactionService.getUserTransaction();
userTransaction.begin();
StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
rootNodeRef = nodeService.getRootNode(storeRef);
QName children = ContentModel.ASSOC_CHILDREN;
QName system = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "system");
QName container = ContentModel.TYPE_CONTAINER;
QName types = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "people");
systemNodeRef = nodeService.createNode(rootNodeRef, children, system, container).getChildRef();
typesNodeRef = nodeService.createNode(systemNodeRef, children, types, container).getChildRef();
Map props = createPersonProperties("Andy");
personAndyNodeRef = nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props).getChildRef();
assertNotNull(personAndyNodeRef);
deleteAndy();
authenticationComponent.clearCurrentSecurityContext();
}
private void deleteAndy()
{
RepositoryAuthenticationDao dao = new RepositoryAuthenticationDao();
dao.setTenantService(tenantService);
dao.setNodeService(nodeService);
dao.setSearchService(searchService);
dao.setDictionaryService(dictionaryService);
dao.setNamespaceService(getNamespacePrefixReolsver(""));
dao.setPasswordEncoder(passwordEncoder);
dao.setUserNameMatcher(userNameMatcher);
dao.setRetryingTransactionHelper(transactionService.getRetryingTransactionHelper());
if (dao.getUserOrNull("andy") != null)
{
dao.deleteUser("andy");
}
if(personService.personExists("andy"))
{
personService.deletePerson("andy");
}
}
@Override
protected void tearDown() throws Exception
{
authenticationComponentImpl.clearCurrentSecurityContext();
userTransaction.rollback();
super.tearDown();
}
private Map createPersonProperties(String userName)
{
HashMap properties = new HashMap();
properties.put(ContentModel.PROP_USERNAME, "Andy");
return properties;
}
public void testSystemTicket() throws Exception
{
assertNull(AuthenticationUtil.getFullAuthentication());
assertNull(AuthenticationUtil.getRunAsAuthentication());
authenticationComponent.setSystemUserAsCurrentUser();
pubAuthenticationService.createAuthentication("andy", "andy".toCharArray());
pubAuthenticationService.clearCurrentSecurityContext();
assertNull(AuthenticationUtil.getFullAuthentication());
assertNull(AuthenticationUtil.getRunAsAuthentication());
// Authenticate
pubAuthenticationService.authenticate("andy", "andy".toCharArray());
// Get current user name
String userName = pubAuthenticationService.getCurrentUserName();
assertEquals("andy", userName);
// Get ticket
String ticket = pubAuthenticationService.getCurrentTicket();
assertEquals("andy", ticketComponent.getAuthorityForTicket(ticket));
// Get logged in user ...
// Get userName
userName = pubAuthenticationService.getCurrentUserName();
assertEquals("andy", userName);
// get Person
assertTrue(pubPersonService.personExists(userName));
AuthenticationUtil.runAs(new RunAsWork() {
public Object doWork() throws Exception
{
// TODO Auto-generated method stub
assertEquals("andy", ticketComponent.getAuthorityForTicket(pubAuthenticationService.getCurrentTicket()));
return null;
}}, AuthenticationUtil.getSystemUserName());
pubPersonService.getPerson(userName);
assertTrue(pubPersonService.personExists(userName));
// re-getTicket
String newticket = pubAuthenticationService.getCurrentTicket();
assertEquals(ticket, newticket);
assertEquals("andy", ticketComponent.getAuthorityForTicket(newticket));
userName = pubAuthenticationService.getCurrentUserName();
assertEquals("andy", userName);
// new TX
//userTransaction.commit();
//userTransaction = transactionService.getUserTransaction();
//userTransaction.begin();
pubAuthenticationService.validate(ticket, null);
userName = pubAuthenticationService.getCurrentUserName();
assertEquals("andy", userName);
pubAuthenticationService.validate(newticket, null);
userName = pubAuthenticationService.getCurrentUserName();
assertEquals("andy", userName);
}
public void xtestScalability()
{
long create = 0;
long start;
long end;
authenticationComponent.authenticate(AuthenticationUtil.getAdminUserName(), "admin".toCharArray());
for (int i = 0; i < 10000; i++)
{
String id = "TestUser-" + i;
start = System.nanoTime();
authenticationService.createAuthentication(id, id.toCharArray());
end = System.nanoTime();
create += (end - start);
if ((i > 0) && (i % 100 == 0))
{
System.out.println("Count = " + i);
System.out.println("Average create : " + (create / i / 1000000.0f));
start = System.nanoTime();
dao.userExists(id);
end = System.nanoTime();
System.out.println("Exists : " + ((end - start) / 1000000.0f));
}
}
authenticationComponent.clearCurrentSecurityContext();
}
public void c()
{
try
{
authenticationService.authenticate("", "".toCharArray());
}
catch (AuthenticationException e)
{
// Expected
}
}
public void testNewTicketOnLogin()
{
authenticationComponent.setSystemUserAsCurrentUser();
pubAuthenticationService.createAuthentication("Andy", "auth1".toCharArray());
authenticationComponent.clearCurrentSecurityContext();
// authenticate with this user details
pubAuthenticationService.authenticate("Andy", "auth1".toCharArray());
String ticket1 = pubAuthenticationService.getCurrentTicket();
pubAuthenticationService.authenticate("Andy", "auth1".toCharArray());
assertFalse(ticket1.equals(pubAuthenticationService.getCurrentTicket()));
}
public void testGuest()
{
authenticationService.authenticate(AuthenticationUtil.getGuestUserName(), "".toCharArray());
}
public void testCreateUsers()
{
authenticationService.createAuthentication(AuthenticationUtil.getGuestUserName(), "".toCharArray());
authenticationService.authenticate(AuthenticationUtil.getGuestUserName(), "".toCharArray());
// Guest is treated like any other user
assertEquals(AuthenticationUtil.getGuestUserName(), authenticationService.getCurrentUserName());
authenticationService.createAuthentication("Andy", "".toCharArray());
authenticationService.authenticate("Andy", "".toCharArray());
assertEquals("Andy", authenticationService.getCurrentUserName());
if (! tenantService.isEnabled())
{
authenticationService.createAuthentication("Mr.Woof.Banana@chocolate.chip.cookie.com", "".toCharArray());
authenticationService.authenticate("Mr.Woof.Banana@chocolate.chip.cookie.com", "".toCharArray());
assertEquals("Mr.Woof.Banana@chocolate.chip.cookie.com", authenticationService.getCurrentUserName());
}
else
{
// TODO - could create tenant domain 'chocolate.chip.cookie.com'
}
authenticationService.createAuthentication("Andy_Woof/Domain", "".toCharArray());
authenticationService.authenticate("Andy_Woof/Domain", "".toCharArray());
assertEquals("Andy_Woof/Domain", authenticationService.getCurrentUserName());
authenticationService.createAuthentication("Andy_ Woof/Domain", "".toCharArray());
authenticationService.authenticate("Andy_ Woof/Domain", "".toCharArray());
assertEquals("Andy_ Woof/Domain", authenticationService.getCurrentUserName());
if (! tenantService.isEnabled())
{
authenticationService.createAuthentication("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?|", "".toCharArray());
authenticationService.authenticate("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?|", "".toCharArray());
assertEquals("Andy `\u00ac\u00a6!\u00a3$%^&*()-_=+\t\n\u0000[]{};'#:@~,./<>?|", authenticationService.getCurrentUserName());
}
else
{
// tenant domain ~,./<>?\\| is not valid format"
}
}
public void testCreateAndyUserAndOtherCRUD() throws NoSuchAlgorithmException, UnsupportedEncodingException
{
RepositoryAuthenticationDao dao = new RepositoryAuthenticationDao();
dao.setTenantService(tenantService);
dao.setNodeService(nodeService);
dao.setSearchService(searchService);
dao.setDictionaryService(dictionaryService);
dao.setNamespaceService(getNamespacePrefixReolsver(""));
dao.setPasswordEncoder(passwordEncoder);
dao.setUserNameMatcher(userNameMatcher);
dao.setRetryingTransactionHelper(transactionService.getRetryingTransactionHelper());
dao.createUser("Andy", "cabbage".toCharArray());
assertNotNull(dao.getUserOrNull("Andy"));
byte[] decodedHash = passwordEncoder.decodeHash(dao.getMD4HashedPassword("Andy"));
byte[] testHash = MessageDigest.getInstance("MD4").digest("cabbage".getBytes("UnicodeLittleUnmarked"));
assertEquals(new String(decodedHash), new String(testHash));
UserDetails AndyDetails = (UserDetails) dao.loadUserByUsername("Andy");
assertNotNull(AndyDetails);
assertEquals("Andy", AndyDetails.getUsername());
// assertNotNull(dao.getSalt(AndyDetails));
assertTrue(AndyDetails.isAccountNonExpired());
assertTrue(AndyDetails.isAccountNonLocked());
assertTrue(AndyDetails.isCredentialsNonExpired());
assertTrue(AndyDetails.isEnabled());
assertNotSame("cabbage", AndyDetails.getPassword());
assertEquals(AndyDetails.getPassword(), passwordEncoder.encodePassword("cabbage", dao.getSalt(AndyDetails)));
assertEquals(1, AndyDetails.getAuthorities().length);
// Object oldSalt = dao.getSalt(AndyDetails);
dao.updateUser("Andy", "carrot".toCharArray());
UserDetails newDetails = (UserDetails) dao.loadUserByUsername("Andy");
assertNotNull(newDetails);
assertEquals("Andy", newDetails.getUsername());
// assertNotNull(dao.getSalt(newDetails));
assertTrue(newDetails.isAccountNonExpired());
assertTrue(newDetails.isAccountNonLocked());
assertTrue(newDetails.isCredentialsNonExpired());
assertTrue(newDetails.isEnabled());
assertNotSame("carrot", newDetails.getPassword());
assertEquals(1, newDetails.getAuthorities().length);
assertNotSame(AndyDetails.getPassword(), newDetails.getPassword());
// assertNotSame(oldSalt, dao.getSalt(newDetails));
dao.deleteUser("Andy");
assertNull(dao.getUserOrNull("Andy"));
MessageDigest digester;
try
{
digester = MessageDigest.getInstance("MD4");
System.out.println("Digester from " + digester.getProvider());
}
catch (NoSuchAlgorithmException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("No digester");
}
}
public void testAuthentication()
{
dao.createUser("GUEST", "".toCharArray());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("GUEST", "");
token.setAuthenticated(false);
Authentication result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.createUser("Andy", "squash".toCharArray());
token = new UsernamePasswordAuthenticationToken("Andy", "squash");
token.setAuthenticated(false);
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.setEnabled("Andy", false);
try
{
result = authenticationManager.authenticate(token);
assertNotNull(result);
assertNotNull(null);
}
catch (DisabledException e)
{
// Expected
}
dao.setEnabled("Andy", true);
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.setLocked("Andy", true);
try
{
result = authenticationManager.authenticate(token);
assertNotNull(result);
assertNotNull(null);
}
catch (LockedException e)
{
// Expected
}
dao.setLocked("Andy", false);
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.setAccountExpires("Andy", true);
dao.setCredentialsExpire("Andy", true);
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.setAccountExpiryDate("Andy", null);
dao.setCredentialsExpiryDate("Andy", null);
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.setAccountExpiryDate("Andy", new Date(new Date().getTime() + 10000));
dao.setCredentialsExpiryDate("Andy", new Date(new Date().getTime() + 10000));
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.setAccountExpiryDate("Andy", new Date(new Date().getTime() - 10000));
try
{
result = authenticationManager.authenticate(token);
assertNotNull(result);
assertNotNull(null);
}
catch (AccountExpiredException e)
{
// Expected
}
dao.setAccountExpiryDate("Andy", new Date(new Date().getTime() + 10000));
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.setCredentialsExpiryDate("Andy", new Date(new Date().getTime() - 10000));
try
{
result = authenticationManager.authenticate(token);
assertNotNull(result);
assertNotNull(null);
}
catch (CredentialsExpiredException e)
{
// Expected
}
dao.setCredentialsExpiryDate("Andy", new Date(new Date().getTime() + 10000));
result = authenticationManager.authenticate(token);
assertNotNull(result);
dao.deleteUser("Andy");
// assertNull(dao.getUserOrNull("Andy"));
}
public void testCreateAuthenticationWhileRunningAsSystem() throws Exception
{
RunAsWork