/*
* 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.sync;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.management.subsystems.ChildApplicationContextManager;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
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.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.alfresco.util.PropertyMap;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
/**
* Tests the {@link ChainingUserRegistrySynchronizer} using a simulated {@link UserRegistry}.
*
* @author dward
*/
public class ChainingUserRegistrySynchronizerTest extends TestCase
{
/** The context locations, in reverse priority order. */
private static final String[] CONFIG_LOCATIONS =
{
"classpath:alfresco/application-context.xml", "classpath:sync-test-context.xml"
};
/** The Spring application context. */
private static ApplicationContext context = new ClassPathXmlApplicationContext(
ChainingUserRegistrySynchronizerTest.CONFIG_LOCATIONS);
/** The synchronizer we are testing. */
private UserRegistrySynchronizer synchronizer;
/** The application context manager. */
private MockApplicationContextManager applicationContextManager;
/** The person service. */
private PersonService personService;
/** The authority service. */
private AuthorityService authorityService;
/** The node service. */
private NodeService nodeService;
/** The authentication context. */
private AuthenticationContext authenticationContext;
/** The retrying transaction helper. */
private RetryingTransactionHelper retryingTransactionHelper;
/*
* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception
{
this.synchronizer = (UserRegistrySynchronizer) ChainingUserRegistrySynchronizerTest.context
.getBean("testUserRegistrySynchronizer");
this.applicationContextManager = (MockApplicationContextManager) ChainingUserRegistrySynchronizerTest.context
.getBean("testApplicationContextManager");
this.personService = (PersonService) ChainingUserRegistrySynchronizerTest.context.getBean("personService");
this.authorityService = (AuthorityService) ChainingUserRegistrySynchronizerTest.context
.getBean("authorityService");
this.nodeService = (NodeService) ChainingUserRegistrySynchronizerTest.context.getBean("nodeService");
this.authenticationContext = (AuthenticationContext) ChainingUserRegistrySynchronizerTest.context
.getBean("authenticationContext");
this.authenticationContext.setSystemUserAsCurrentUser();
this.retryingTransactionHelper = (RetryingTransactionHelper) ChainingUserRegistrySynchronizerTest.context
.getBean("retryingTransactionHelper");
}
/*
* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception
{
this.authenticationContext.clearCurrentSecurityContext();
}
/**
* Sets up the test users and groups in three zones, "Z0", "Z1" and "Z2", by doing a forced synchronize with a Mock
* user registry. Note that the zones have some overlapping entries. "Z0" is not used in subsequent synchronizations
* and is used to test that users and groups in zones that aren't in the authentication chain get 're-zoned'
* appropriately. The layout is as follows
*
*
* Z0
* G1
* U6
*
* Z1
* G2 - U1, G3 - U2, G4, G5
*
* Z2
* G2 - U1, U3, U4
* G6 - U3, U4, G7 - U5
*
*
* @throws Exception
* the exception
*/
private void setUpTestUsersAndGroups() throws Exception
{
this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z0", new NodeDescription[]
{
newPerson("U6")
}, new NodeDescription[]
{
newGroup("G1")
}), new MockUserRegistry("Z1", new NodeDescription[]
{
newPerson("U1"), newPerson("U2"), newPerson("U7")
}, new NodeDescription[]
{
newGroup("G2", "U1", "G3"), newGroup("G3", "U2", "G4", "G5"), newGroup("G4"), newGroup("G5")
}), new MockUserRegistry("Z2", new NodeDescription[]
{
newPerson("U1"), newPerson("U3"), newPerson("U4"), newPerson("U5")
}, new NodeDescription[]
{
newGroup("G2", "U1", "U3", "U4"), newGroup("G6", "U3", "U4", "G7"), newGroup("G7", "U5")
}));
this.synchronizer.synchronize(true, true, true);
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback()
{
public Object execute() throws Throwable
{
assertExists("Z0", "U6");
assertExists("Z0", "G1");
assertExists("Z1", "U1");
assertExists("Z1", "U2");
assertExists("Z1", "G2", "U1", "G3");
assertExists("Z1", "G3", "U2", "G4", "G5");
assertExists("Z1", "G4");
assertExists("Z1", "G5");
assertExists("Z2", "U3");
assertExists("Z2", "U4");
assertExists("Z2", "U5");
assertExists("Z2", "G6", "U3", "U4", "G7");
assertExists("Z2", "G7", "U5");
return null;
}
}, false, true);
}
/**
* Tear down test users and groups.
*
* @throws Exception
* the exception
*/
public void tearDownTestUsersAndGroups() throws Exception
{
// Wipe out everything that was in Z1 and Z2
this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z0", new NodeDescription[] {},
new NodeDescription[] {}), new MockUserRegistry("Z1", new NodeDescription[] {},
new NodeDescription[] {}), new MockUserRegistry("Z2", new NodeDescription[] {},
new NodeDescription[] {}));
this.synchronizer.synchronize(true, true, true);
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback()
{
public Object execute() throws Throwable
{
assertNotExists("U1");
assertNotExists("U2");
assertNotExists("U3");
assertNotExists("U4");
assertNotExists("U5");
assertNotExists("U6");
assertNotExists("U7");
assertNotExists("G1");
assertNotExists("G2");
assertNotExists("G3");
assertNotExists("G4");
assertNotExists("G5");
assertNotExists("G6");
assertNotExists("G7");
return null;
}
}, false, true);
}
/**
* Tests a differential update of the test users and groups. The layout is as follows
*
*
* Z1
* G1 - U1, U6
* G2 - U1
* G3 - U2, G4, G5 - U6
*
* Z2
* G2 - U1, U3, U4, U6
* G6 - U3, U4, G7
*
*
* @throws Exception
* the exception
*/
public void testDifferentialUpdate() throws Exception
{
setUpTestUsersAndGroups();
this.applicationContextManager.removeZone("Z0");
this.applicationContextManager.updateZone("Z1", new NodeDescription[]
{
newPerson("U1", "changeofemail@alfresco.com"), newPerson("U6"), newPerson("U7")
}, new NodeDescription[]
{
newGroup("G1", "U1", "U6", "UDangling"), newGroup("G2", "U1", "GDangling"),
newGroupWithDisplayName("G5", "Amazing Group", "U6", "U7", "G4")
});
this.applicationContextManager.updateZone("Z2", new NodeDescription[]
{
newPerson("U1", "shouldbeignored@alfresco.com"), newPerson("U5", "u5email@alfresco.com"), newPerson("U6")
}, new NodeDescription[]
{
newGroup("G2", "U1", "U3", "U4", "U6"), newGroup("G7")
});
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback()
{
public Object execute() throws Throwable
{
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false, false);
// Stay in the same transaction
assertExists("Z1", "U1");
assertEmailEquals("U1", "changeofemail@alfresco.com");
assertExists("Z1", "U2");
assertExists("Z1", "U6");
assertExists("Z1", "U7");
assertExists("Z1", "G1", "U1", "U6");
assertExists("Z1", "G2", "U1");
assertExists("Z1", "G3", "U2", "G4", "G5");
assertExists("Z1", "G4");
assertExists("Z1", "G5", "U6", "U7", "G4");
assertGroupDisplayNameEquals("G5", "Amazing Group");
assertExists("Z2", "U3");
assertExists("Z2", "U4");
assertExists("Z2", "U5");
assertEmailEquals("U5", "u5email@alfresco.com");
assertExists("Z2", "G6", "U3", "U4", "G7");
assertExists("Z2", "G7");
return null;
}
});
tearDownTestUsersAndGroups();
}
/**
* Tests a forced update of the test users and groups. Also tests that groups and users that previously existed in
* Z2 get moved when they appear in Z1. Also tests that 'dangling references' to removed users (U4, U5) do not cause
* any problems. Also tests that case-sensitivity is not a problem when an occluded user is recreated with different
* case. The layout is as follows
*
*
* Z1
* G1 - U6
* G2 -
* G3 - U2, G5 - U6
* G6 - u3
*
* Z2
* G2 - U1, U3, U6
* G6 - U3, G7
*
*
* @throws Exception
* the exception
*/
public void testForcedUpdate() throws Exception
{
setUpTestUsersAndGroups();
this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z1", new NodeDescription[]
{
newPerson("U2"), newPerson("u3"), newPerson("U6")
}, new NodeDescription[]
{
newGroup("G1", "U6"), newGroup("G2"), newGroup("G3", "U2", "G5"), newGroup("G5", "U6"),
newGroup("G6", "u3")
}), new MockUserRegistry("Z2", new NodeDescription[]
{
newPerson("U1", "somenewemail@alfresco.com"), newPerson("U3"), newPerson("U6")
}, new NodeDescription[]
{
newGroup("G2", "U1", "U3", "U4", "U6"), newGroup("G6", "U3", "U4", "G7"),
newGroupWithDisplayName("G7", "Late Arrival", "U4", "U5")
}));
this.synchronizer.synchronize(true, true, true);
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback()
{
public Object execute() throws Throwable
{
assertExists("Z1", "U2");
assertExists("Z1", "u3");
assertExists("Z1", "U6");
assertExists("Z1", "G1", "U6");
assertExists("Z1", "G2");
assertExists("Z1", "G3", "U2", "G5");
assertNotExists("G4");
assertExists("Z1", "G5", "U6");
assertExists("Z1", "G6", "u3");
assertExists("Z2", "U1");
assertEmailEquals("U1", "somenewemail@alfresco.com");
assertNotExists("U4");
assertNotExists("U5");
assertExists("Z2", "G7");
assertGroupDisplayNameEquals("G7", "Late Arrival");
return null;
}
}, false, true);
tearDownTestUsersAndGroups();
}
/**
* Tests a forced update of the test users and groups where some of the users change their case and some groups
* appear with different case.
*/
public void testCaseChange() throws Exception
{
setUpTestUsersAndGroups();
final Map personNodes = new TreeMap();
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback()
{
public Object execute() throws Throwable
{
// Get hold of the original person nodes so we can compare them later
personNodes.put("u1", ChainingUserRegistrySynchronizerTest.this.personService.getPerson("U1", false));
personNodes.put("u2", ChainingUserRegistrySynchronizerTest.this.personService.getPerson("U2", false));
personNodes.put("u6", ChainingUserRegistrySynchronizerTest.this.personService.getPerson("U6", false));
return null;
}
}, false, true);
this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z1", new NodeDescription[]
{
newPerson("u1"), newPerson("u2"), newPerson("u6"), newPerson("U7")
}, new NodeDescription[]
{
newGroup("g1", "u6"), newGroup("g2", "u1", "G3"), newGroup("G3", "u2", "g4", "g5"), newGroup("g4"),
newGroup("g5")
}), new MockUserRegistry("Z2", new NodeDescription[]
{
newPerson("U1"), newPerson("U3"), newPerson("U4"), newPerson("U5")
}, new NodeDescription[]
{
newGroup("G2", "U1", "U3", "U4"), newGroup("G6", "U3", "U4", "G7"), newGroup("G7", "U5")
}));
this.synchronizer.synchronize(true, true, true);
this.retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback()
{
public Object execute() throws Throwable
{
assertExists("Z1", "u1");
assertExists("Z1", "u2");
assertExists("Z1", "u6");
assertExists("Z1", "g1", "u6");
assertExists("Z1", "g2", "u1", "G3");
assertExists("Z1", "G3", "u2", "g4", "g5");
assertExists("Z1", "g4");
assertExists("Z1", "g5");
assertExists("Z2", "U3");
assertExists("Z2", "U4");
assertExists("Z2", "U5");
assertExists("Z2", "G2", "U3", "U4");
assertExists("Z2", "G6", "U3", "U4", "G7");
assertExists("Z2", "G7", "U5");
// Make sure the original people have been preserved
assertEquals(personNodes.get("u1"), ChainingUserRegistrySynchronizerTest.this.personService.getPerson(
"U1", false));
assertEquals(personNodes.get("u2"), ChainingUserRegistrySynchronizerTest.this.personService.getPerson(
"U2", false));
assertEquals(personNodes.get("u6"), ChainingUserRegistrySynchronizerTest.this.personService.getPerson(
"U6", false));
return null;
}
}, false, true);
tearDownTestUsersAndGroups();
}
/**
* Tests synchronization with a zone with a larger volume of authorities.
*
* @throws Exception
* the exception
*/
public void testVolume() throws Exception
{
List persons = new ArrayList(new RandomPersonCollection(100));
List groups = new ArrayList(new RandomGroupCollection(50, persons));
this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z0", persons, groups));
this.synchronizer.synchronize(true, true, true);
tearDownTestUsersAndGroups();
}
/**
* Tests synchronization of group associations in a zone with a larger volume of authorities.
*
* @throws Exception
* the exception
*/
public void dontTestAssocs() throws Exception
{
List groups = this.retryingTransactionHelper.doInTransaction(
new RetryingTransactionCallback>()
{
public List execute() throws Throwable
{
return new ArrayList(new RandomGroupCollection(1000,
ChainingUserRegistrySynchronizerTest.this.authorityService.getAllAuthoritiesInZone(
AuthorityService.ZONE_AUTH_EXT_PREFIX + "Z0", null)));
}
}, true, true);
ChainingUserRegistrySynchronizerTest.this.applicationContextManager.setUserRegistries(new MockUserRegistry(
"Z0", Collections. emptyList(), groups));
ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(true, true, true);
tearDownTestUsersAndGroups();
}
/**
* Constructs a description of a test group.
*
* @param name
* the name
* @param members
* the members
* @return the node description
*/
private NodeDescription newGroup(String name, String... members)
{
return newGroupWithDisplayName(name, name, members);
}
/**
* Constructs a description of a test group with a display name.
*
* @param name
* the name
* @param displayName
* the display name
* @param members
* the members
* @return the node description
*/
private NodeDescription newGroupWithDisplayName(String name, String displayName, String... members)
{
String longName = longName(name);
NodeDescription group = new NodeDescription(longName);
PropertyMap properties = group.getProperties();
properties.put(ContentModel.PROP_AUTHORITY_NAME, longName);
properties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, displayName);
if (members.length > 0)
{
Set assocs = group.getChildAssociations();
for (String member : members)
{
assocs.add(longName(member));
}
}
group.setLastModified(new Date());
return group;
}
/**
* Constructs a description of a test person with default email (userName@alfresco.com)
*
* @param userName
* the user name
* @return the node description
*/
private NodeDescription newPerson(String userName)
{
return newPerson(userName, userName + "@alfresco.com");
}
/**
* Constructs a description of a test person with a given email.
*
* @param userName
* the user name
* @param email
* the email
* @return the node description
*/
private NodeDescription newPerson(String userName, String email)
{
NodeDescription person = new NodeDescription(userName);
PropertyMap properties = person.getProperties();
properties.put(ContentModel.PROP_USERNAME, userName);
properties.put(ContentModel.PROP_FIRSTNAME, userName + "F");
properties.put(ContentModel.PROP_LASTNAME, userName + "L");
properties.put(ContentModel.PROP_EMAIL, email);
person.setLastModified(new Date());
return person;
}
/**
* Perform all the necessary assertions to ensure that an authority and its members exist in the correct zone.
*
* @param zone
* the zone
* @param name
* the name
* @param members
* the members
*/
private void assertExists(String zone, String name, String... members)
{
String longName = longName(name);
// Check authority exists
assertTrue(this.authorityService.authorityExists(longName));
// Check in correct zone
assertTrue(this.authorityService.getAuthorityZones(longName).contains(
AuthorityService.ZONE_AUTH_EXT_PREFIX + zone));
if (AuthorityType.getAuthorityType(longName).equals(AuthorityType.GROUP))
{
// Check groups have expected members
Set memberSet = new HashSet(members.length * 2);
for (String member : members)
{
memberSet.add(longName(member));
}
assertEquals(memberSet, this.authorityService.getContainedAuthorities(null, longName, true));
}
else
{
// Check users exist as persons
assertTrue(this.personService.personExists(name));
// Check case matches
assertEquals(this.personService.getUserIdentifier(name), name);
}
}
/**
* Perform all the necessary assertions to ensure that an authority does not exist.
*
* @param name
* the name
*/
private void assertNotExists(String name)
{
String longName = longName(name);
// Check authority does not exist
assertFalse(this.authorityService.authorityExists(longName));
// Check there is no zone
assertNull(this.authorityService.getAuthorityZones(longName));
if (!AuthorityType.getAuthorityType(longName).equals(AuthorityType.GROUP))
{
// Check person does not exist
assertFalse(this.personService.personExists(name));
}
}
/**
* Asserts that a person's email has the expected value.
*
* @param personName
* the person name
* @param email
* the email
*/
private void assertEmailEquals(String personName, String email)
{
NodeRef personRef = this.personService.getPerson(personName);
assertEquals(email, this.nodeService.getProperty(personRef, ContentModel.PROP_EMAIL));
}
/**
* Asserts that a group's display name has the expected value.
*
* @param personName
* the person name
* @param email
* the email
*/
private void assertGroupDisplayNameEquals(String name, String displayName)
{
assertEquals(displayName, this.authorityService.getAuthorityDisplayName(longName(name)));
}
/**
* Converts the given short name to a full authority name, assuming that those short names beginning with 'G'
* correspond to groups and all others correspond to users.
*
* @param shortName
* the short name
* @return the full authority name
*/
private String longName(String shortName)
{
return this.authorityService.getName(shortName.toLowerCase().startsWith("g") ? AuthorityType.GROUP
: AuthorityType.USER, shortName);
}
/**
* A Mock {@link UserRegistry} that returns a fixed set of users and groups.
*/
public static class MockUserRegistry implements UserRegistry
{
/** The zone id. */
private String zoneId;
/** The persons. */
private Collection persons;
/** The groups. */
private Collection groups;
/**
* Instantiates a new mock user registry.
*
* @param zoneId
* the zone id
* @param persons
* the persons
* @param groups
* the groups
*/
public MockUserRegistry(String zoneId, Collection persons, Collection groups)
{
this.zoneId = zoneId;
this.persons = persons;
this.groups = groups;
}
/**
* Modifies the state to match the arguments. Compares new with old and records new modification dates only for
* changes.
*
* @param persons
* the persons
* @param groups
* the groups
*/
public void updateState(Collection persons, Collection groups)
{
List newPersons = new ArrayList(this.persons);
mergeNodeDescriptions(newPersons, persons, ContentModel.PROP_USERNAME, false);
this.persons = newPersons;
List newGroups = new ArrayList(this.groups);
mergeNodeDescriptions(newGroups, groups, ContentModel.PROP_AUTHORITY_NAME, true);
this.groups = newGroups;
}
/**
* Merges together an old and new list of node descriptions. Retains the old node with its old modification date
* if it is the same in the new list, otherwises uses the node from the new list.
*
* @param oldNodes
* the old node list
* @param newNodes
* the new node list
* @param idProp
* the name of the ID property
* @param caseSensitive
* are IDs case sensitive?
*/
private void mergeNodeDescriptions(List oldNodes, Collection newNodes,
QName idProp, boolean caseSensitive)
{
Map nodeMap = new LinkedHashMap(newNodes.size() * 2);
for (NodeDescription node : newNodes)
{
String id = (String) node.getProperties().get(idProp);
if (!caseSensitive)
{
id = id.toLowerCase();
}
nodeMap.put(id, node);
}
for (int i = 0; i < oldNodes.size(); i++)
{
NodeDescription oldNode = oldNodes.get(i);
String id = (String) oldNode.getProperties().get(idProp);
if (!caseSensitive)
{
id = id.toLowerCase();
}
NodeDescription newNode = nodeMap.remove(id);
if (newNode == null)
{
oldNodes.remove(i);
i--;
}
else if (!oldNode.getProperties().equals(newNode.getProperties())
|| !oldNode.getChildAssociations().equals(newNode.getChildAssociations()))
{
oldNodes.set(i, newNode);
}
}
oldNodes.addAll(nodeMap.values());
}
/**
* Instantiates a new mock user registry.
*
* @param zoneId
* the zone id
* @param persons
* the persons
* @param groups
* the groups
*/
public MockUserRegistry(String zoneId, NodeDescription[] persons, NodeDescription[] groups)
{
this(zoneId, Arrays.asList(persons), Arrays.asList(groups));
}
/**
* Gets the zone id.
*
* @return the zoneId
*/
public String getZoneId()
{
return this.zoneId;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.security.sync.UserRegistry#getGroupNames()
*/
public Collection getGroupNames()
{
List groupNames = new LinkedList();
for (NodeDescription group : this.groups)
{
groupNames.add((String) group.getProperties().get(ContentModel.PROP_AUTHORITY_NAME));
}
return groupNames;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.security.sync.UserRegistry#getPersonNames()
*/
public Collection getPersonNames()
{
List personNames = new LinkedList();
for (NodeDescription person : this.persons)
{
personNames.add((String) person.getProperties().get(ContentModel.PROP_USERNAME));
}
return personNames;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.security.sync.UserRegistry#getGroups(java.util.Date)
*/
public Collection getGroups(Date modifiedSince)
{
return filterNodeDescriptions(this.groups, modifiedSince);
}
/**
* Filters the given list of node descriptions, retaining only those with a modification date greater than the
* given date.
*
* @param nodes
* the list of nodes
* @param modifiedSince
* the modified date
* @return the filter list of nodes
*/
private Collection filterNodeDescriptions(Collection nodes, Date modifiedSince)
{
if (modifiedSince == null)
{
return nodes;
}
List filteredNodes = new LinkedList();
for (NodeDescription node : nodes)
{
Date modified = node.getLastModified();
if (modifiedSince.compareTo(modified) < 0)
{
filteredNodes.add(node);
}
}
return filteredNodes;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.security.sync.UserRegistry#getPersons(java.util.Date)
*/
public Collection getPersons(Date modifiedSince)
{
return filterNodeDescriptions(this.persons, modifiedSince);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.security.sync.UserRegistry#getPersonMappedProperties()
*/
public Set getPersonMappedProperties()
{
return new HashSet(Arrays.asList(new QName[]
{
ContentModel.PROP_USERNAME, ContentModel.PROP_FIRSTNAME, ContentModel.PROP_LASTNAME,
ContentModel.PROP_EMAIL, ContentModel.PROP_ORGID, ContentModel.PROP_ORGANIZATION,
ContentModel.PROP_HOME_FOLDER_PROVIDER
}));
}
}
/**
* An {@link ChildApplicationContextManager} for a chain of application contexts containing mock user registries.
*/
public static class MockApplicationContextManager implements ChildApplicationContextManager
{
/** The contexts. */
private Map contexts = Collections.emptyMap();
/**
* Sets the user registries.
*
* @param registries
* the new user registries
*/
public void setUserRegistries(MockUserRegistry... registries)
{
this.contexts = new LinkedHashMap(registries.length * 2);
for (MockUserRegistry registry : registries)
{
StaticApplicationContext context = new StaticApplicationContext();
context.getDefaultListableBeanFactory().registerSingleton("userRegistry", registry);
this.contexts.put(registry.getZoneId(), context);
}
}
/**
* Removes the application context for the given zone ID (simulating a change in the authentication chain).
*
* @param zoneId
* the zone id
*/
public void removeZone(String zoneId)
{
this.contexts.remove(zoneId);
}
/**
* Updates the state of the given zone ID, oopying in new modification dates only where changes have been made.
*
* @param zoneId
* the zone id
* @param persons
* the new list of persons
* @param groups
* the new list of groups
*/
public void updateZone(String zoneId, NodeDescription[] persons, NodeDescription[] groups)
{
ApplicationContext context = this.contexts.get(zoneId);
MockUserRegistry registry = (MockUserRegistry) context.getBean("userRegistry");
registry.updateState(Arrays.asList(persons), Arrays.asList(groups));
}
/*
* (non-Javadoc)
* @see
* org.alfresco.repo.management.subsystems.ChildApplicationContextManager#getApplicationContext(java.lang.String
* )
*/
public ApplicationContext getApplicationContext(String id)
{
return this.contexts.get(id);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.management.subsystems.ChildApplicationContextManager#getInstanceIds()
*/
public Collection getInstanceIds()
{
return this.contexts.keySet();
}
}
/**
* A collection whose iterator returns randomly generated persons.
*/
public class RandomPersonCollection extends AbstractCollection
{
/** The collection size. */
private final int size;
/**
* The Constructor.
*
* @param size
* the collection size
*/
public RandomPersonCollection(int size)
{
this.size = size;
}
/*
* (non-Javadoc)
* @see java.util.AbstractCollection#iterator()
*/
@Override
public Iterator iterator()
{
return new Iterator()
{
private int pos;
public boolean hasNext()
{
return this.pos < RandomPersonCollection.this.size;
}
public NodeDescription next()
{
this.pos++;
return newPerson("U" + GUID.generate());
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
/*
* (non-Javadoc)
* @see java.util.AbstractCollection#size()
*/
@Override
public int size()
{
return this.size;
}
}
/**
* A collection whose iterator returns randomly generated groups with random associations to a given list of
* persons.
*/
public class RandomGroupCollection extends AbstractCollection
{
/** Use a fixed seed to give this class deterministic behaviour */
private Random generator = new Random(1628876500L);
/** The collection size. */
private final int size;
/** The authorities. */
private final List authorities;
/**
* The Constructor.
*
* @param size
* the collection size
* @param authorities
* the authorities
*/
public RandomGroupCollection(int size, Set authorities)
{
this.size = size;
this.authorities = new ArrayList(authorities);
}
/**
* The Constructor.
*
* @param size
* the collection size
* @param authorities
* the authorities
*/
public RandomGroupCollection(int size, Collection persons)
{
this.size = size;
this.authorities = new ArrayList(persons.size());
for (NodeDescription nodeDescription : persons)
{
this.authorities.add((String) nodeDescription.getProperties().get(ContentModel.PROP_USERNAME));
}
}
/*
* (non-Javadoc)
* @see java.util.AbstractCollection#iterator()
*/
@Override
public Iterator iterator()
{
return new Iterator()
{
private int pos;
public boolean hasNext()
{
return this.pos < RandomGroupCollection.this.size;
}
public NodeDescription next()
{
this.pos++;
String[] authorityNames = new String[17];
for (int i = 0; i < authorityNames.length; i++)
{
// Choose an authority at random from the list of known authorities
int index = RandomGroupCollection.this.generator.nextInt(RandomGroupCollection.this.authorities
.size());
authorityNames[i] = ChainingUserRegistrySynchronizerTest.this.authorityService
.getShortName((String) RandomGroupCollection.this.authorities.get(index));
}
NodeDescription group = newGroup("G" + GUID.generate(), authorityNames);
// Make this group a candidate for adding to other groups
RandomGroupCollection.this.authorities.add((String) group.getProperties().get(
ContentModel.PROP_AUTHORITY_NAME));
return group;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
/*
* (non-Javadoc)
* @see java.util.AbstractCollection#size()
*/
@Override
public int size()
{
return this.size;
}
}
}