Merged 1.4 to HEAD

svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4380 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4386 .


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4659 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2006-12-19 15:04:47 +00:00
parent 31d1fa1fb1
commit 4443f42279
14 changed files with 601 additions and 46 deletions

View File

@@ -39,6 +39,8 @@ import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import net.sf.acegisecurity.providers.dao.SaltSource;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.Ticket;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -54,6 +56,7 @@ 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();
@@ -81,6 +84,8 @@ public class AuthenticationTest extends TestCase
private SaltSource saltSource;
private TicketComponent ticketComponent;
private SimpleCache<String, Ticket> ticketsCache;
private AuthenticationService authenticationService;
@@ -116,6 +121,7 @@ public class AuthenticationTest extends TestCase
authenticationComponentImpl = (AuthenticationComponent) ctx.getBean("authenticationComponentImpl");
// permissionServiceSPI = (PermissionServiceSPI)
// ctx.getBean("permissionService");
ticketsCache = (SimpleCache<String, Ticket>) ctx.getBean("ticketsCache");
dao = (MutableAuthenticationDao) ctx.getBean("alfDaoImpl");
authenticationManager = (AuthenticationManager) ctx.getBean("authenticationManager");
@@ -143,7 +149,6 @@ public class AuthenticationTest extends TestCase
deleteAndy();
authenticationComponent.clearCurrentSecurityContext();
}
private void deleteAndy()
@@ -476,6 +481,7 @@ public class AuthenticationTest extends TestCase
tc.setOneOff(false);
tc.setTicketsExpire(false);
tc.setValidDuration("P0D");
tc.setTicketsCache(ticketsCache);
dao.createUser("Andy", "ticket".toCharArray());
@@ -499,6 +505,7 @@ public class AuthenticationTest extends TestCase
tc.setOneOff(true);
tc.setTicketsExpire(false);
tc.setValidDuration("P0D");
tc.setTicketsCache(ticketsCache);
dao.createUser("Andy", "ticket".toCharArray());
@@ -530,6 +537,7 @@ public class AuthenticationTest extends TestCase
tc.setOneOff(false);
tc.setTicketsExpire(true);
tc.setValidDuration("P5S");
tc.setTicketsCache(ticketsCache);
dao.createUser("Andy", "ticket".toCharArray());
@@ -619,6 +627,7 @@ public class AuthenticationTest extends TestCase
tc.setOneOff(false);
tc.setTicketsExpire(true);
tc.setValidDuration("P1D");
tc.setTicketsCache(ticketsCache);
dao.createUser("Andy", "ticket".toCharArray());

View File

@@ -16,11 +16,12 @@
*/
package org.alfresco.repo.security.authentication;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.repository.datatype.Duration;
import org.alfresco.util.GUID;
public class InMemoryTicketComponentImpl implements TicketComponent
@@ -33,13 +34,18 @@ public class InMemoryTicketComponentImpl implements TicketComponent
private boolean oneOff;
private HashMap<String, Ticket> tickets = new HashMap<String, Ticket>();
private SimpleCache<String, Ticket> ticketsCache; // Can't use Ticket as it's private
public InMemoryTicketComponentImpl()
{
super();
}
public void setTicketsCache(SimpleCache<String, Ticket> ticketsCache)
{
this.ticketsCache = ticketsCache;
}
public String getTicket(String userName) throws AuthenticationException
{
Date expiryDate = null;
@@ -48,7 +54,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
expiryDate = Duration.add(new Date(), validDuration);
}
Ticket ticket = new Ticket(ticketsExpire, expiryDate, userName);
tickets.put(ticket.getTicketId(), ticket);
ticketsCache.put(ticket.getTicketId(), ticket);
return GRANTED_AUTHORITY_TICKET_PREFIX + ticket.getTicketId();
}
@@ -61,7 +67,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
}
String key = ticketString.substring(GRANTED_AUTHORITY_TICKET_PREFIX.length());
Ticket ticket = tickets.get(key);
Ticket ticket = ticketsCache.get(key);
if (ticket == null)
{
throw new AuthenticationException("Missing ticket for " + ticketString);
@@ -74,7 +80,7 @@ public class InMemoryTicketComponentImpl implements TicketComponent
// TODO: Strengthen ticket as GUID is predicatble
if(oneOff)
{
tickets.remove(key);
ticketsCache.remove(key);
}
return ticket.getUserName();
}
@@ -82,16 +88,16 @@ public class InMemoryTicketComponentImpl implements TicketComponent
public void invalidateTicketById(String ticketString)
{
String key = ticketString.substring(GRANTED_AUTHORITY_TICKET_PREFIX.length());
tickets.remove(key);
ticketsCache.remove(key);
}
public void invalidateTicketByUser(String userName)
{
Set<String> toRemove = new HashSet<String>();
for(String key: tickets.keySet())
for(String key: ticketsCache.getKeys())
{
Ticket ticket = tickets.get(key);
Ticket ticket = ticketsCache.get(key);
if(ticket.getUserName().equals(userName))
{
toRemove.add(ticket.getTicketId());
@@ -100,14 +106,16 @@ public class InMemoryTicketComponentImpl implements TicketComponent
for(String id: toRemove)
{
tickets.remove(id);
ticketsCache.remove(id);
}
}
private static class Ticket
public static class Ticket implements Serializable
{
private static final long serialVersionUID = -5904510560161261049L;
private boolean expires;
private Date expiryDate;

View File

@@ -21,6 +21,7 @@ import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import javax.naming.NamingEnumeration;
@@ -67,7 +68,7 @@ public class LDAPPersonExportSource implements ExportSource
private NamespaceService namespaceService;
private Map<String, String> attributeDefaults;
private boolean errorOnMissingUID;
public LDAPPersonExportSource()
@@ -119,7 +120,7 @@ public class LDAPPersonExportSource implements ExportSource
{
this.errorOnMissingUID = errorOnMissingUID;
}
public void generateExport(XMLWriter writer)
{
QName nodeUUID = QName.createQName("sys:node-uuid", namespaceService);
@@ -147,6 +148,8 @@ public class LDAPPersonExportSource implements ExportSource
writer.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view",
NamespaceService.REPOSITORY_VIEW_PREFIX + ":" + "view", new AttributesImpl());
HashSet<String> uids = new HashSet<String>();
InitialDirContext ctx = null;
try
{
@@ -157,35 +160,44 @@ public class LDAPPersonExportSource implements ExportSource
SearchControls userSearchCtls = new SearchControls();
userSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
userSearchCtls.setCountLimit(Integer.MAX_VALUE);
NamingEnumeration searchResults = ctx.search(searchBase, personQuery, userSearchCtls);
while (searchResults.hasMoreElements())
RESULT_LOOP: while (searchResults.hasMoreElements())
{
SearchResult result = (SearchResult) searchResults.next();
Attributes attributes = result.getAttributes();
Attribute uidAttribute = attributes.get(userIdAttributeName);
if (uidAttribute == null)
{
if(errorOnMissingUID)
if (errorOnMissingUID)
{
throw new ExportSourceImporterException(
"User returned by user search does not have mandatory user id attribute " + attributes);
throw new ExportSourceImporterException(
"User returned by user search does not have mandatory user id attribute "
+ attributes);
}
else
{
s_logger.warn("User returned by user search does not have mandatory user id attribute " + attributes);
continue;
s_logger.warn("User returned by user search does not have mandatory user id attribute "
+ attributes);
continue RESULT_LOOP;
}
}
String uid = (String) uidAttribute.get(0);
if (uids.contains(uid))
{
s_logger.warn("Duplicate uid found - there will be more than one person object for this user - "
+ uid);
}
uids.add(uid);
if (s_logger.isDebugEnabled())
{
s_logger.debug("Adding user for " + uid);
}
writer.startElement(ContentModel.TYPE_PERSON.getNamespaceURI(), ContentModel.TYPE_PERSON
.getLocalName(), ContentModel.TYPE_PERSON.toPrefixString(namespaceService), attrs);
@@ -234,7 +246,7 @@ public class LDAPPersonExportSource implements ExportSource
else
{
String defaultValue = attributeDefaults.get(key);
if(defaultValue != null)
if (defaultValue != null)
{
writer.characters(defaultValue.toCharArray(), 0, defaultValue.length());
}
@@ -243,7 +255,7 @@ public class LDAPPersonExportSource implements ExportSource
else
{
String defaultValue = attributeDefaults.get(key);
if(defaultValue != null)
if (defaultValue != null)
{
writer.characters(defaultValue.toCharArray(), 0, defaultValue.length());
}
@@ -316,7 +328,7 @@ public class LDAPPersonExportSource implements ExportSource
TransactionService txs = (TransactionService) ctx.getBean("transactionComponent");
UserTransaction tx = txs.getUserTransaction();
tx.begin();
File file = new File(args[0]);
Writer writer = new BufferedWriter(new FileWriter(file));
XMLWriter xmlWriter = createXMLExporter(writer);

View File

@@ -27,6 +27,7 @@ import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.ldap.LDAPPersonExportSource;
import org.alfresco.repo.security.permissions.PermissionServiceSPI;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -41,9 +42,20 @@ import org.alfresco.service.cmr.security.NoSuchPersonException;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class PersonServiceImpl implements PersonService
{
private static Log s_logger = LogFactory.getLog(PersonServiceImpl.class);
private static final String DELETE = "DELETE";
private static final String SPLIT = "SPLIT";
private static final String LEAVE = "LEAVE";
public static final String SYSTEM_FOLDER = "/sys:system";
public static final String PEOPLE_FOLDER = SYSTEM_FOLDER + "/sys:people";
@@ -67,9 +79,17 @@ public class PersonServiceImpl implements PersonService
private static Set<QName> mutableProperties;
private boolean userNamesAreCaseSensitive = false;
private String defaultHomeFolderProvider;
private boolean processDuplicates = true;
private String duplicateMode = LEAVE;
private boolean lastIsBest = true;
private boolean includeAutoCreated = false;
static
{
Set<QName> props = new HashSet<QName>();
@@ -96,12 +116,32 @@ public class PersonServiceImpl implements PersonService
{
this.userNamesAreCaseSensitive = userNamesAreCaseSensitive;
}
void setDefaultHomeFolderProvider(String defaultHomeFolderProvider)
{
this.defaultHomeFolderProvider = defaultHomeFolderProvider;
}
public void setDuplicateMode(String duplicateMode)
{
this.duplicateMode = duplicateMode;
}
public void setIncludeAutoCreated(boolean includeAutoCreated)
{
this.includeAutoCreated = includeAutoCreated;
}
public void setLastIsBest(boolean lastIsBest)
{
this.lastIsBest = lastIsBest;
}
public void setProcessDuplicates(boolean processDuplicates)
{
this.processDuplicates = processDuplicates;
}
public NodeRef getPerson(String userName)
{
NodeRef personNode = getPersonOrNull(userName);
@@ -130,21 +170,23 @@ public class PersonServiceImpl implements PersonService
public NodeRef getPersonOrNull(String searchUserName)
{
NodeRef returnRef = null;
SearchParameters sp = new SearchParameters();
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\"" + searchUserName
+ "\"");
sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\""
+ searchUserName + "\"");
sp.addStore(storeRef);
sp.excludeDataInTheCurrentTransaction(false);
ResultSet rs = null;
boolean singleton = true;
try
{
rs = searchService.query(sp);
NodeRef returnRef = null;
for (ResultSetRow row : rs)
{
@@ -164,8 +206,8 @@ public class PersonServiceImpl implements PersonService
}
else
{
throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName
+ " (case sensitive)");
singleton = false;
break;
}
}
}
@@ -179,15 +221,13 @@ public class PersonServiceImpl implements PersonService
}
else
{
throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName
+ " (case insensitive)");
singleton = false;
break;
}
}
}
}
}
return returnRef;
}
finally
{
@@ -196,6 +236,269 @@ public class PersonServiceImpl implements PersonService
rs.close();
}
}
if (singleton)
{
return returnRef;
}
else
{
return handleDuplicates(searchUserName);
}
}
private NodeRef handleDuplicates(String searchUserName)
{
if (processDuplicates)
{
NodeRef best = findBest(searchUserName);
if (duplicateMode.equalsIgnoreCase(SPLIT))
{
split(searchUserName, best);
s_logger.info("Split duplicate person objects for uid " + searchUserName);
}
else if (duplicateMode.equalsIgnoreCase(DELETE))
{
delete(searchUserName, best);
s_logger.info("Deleted duplicate person objects for uid " + searchUserName);
}
else
{
if (s_logger.isDebugEnabled())
{
s_logger.debug("Duplicate person objects exist for uid " + searchUserName);
}
}
return best;
}
else
{
if (userNamesAreCaseSensitive)
{
throw new AlfrescoRuntimeException("Found more than one user for "
+ searchUserName + " (case sensitive)");
}
else
{
throw new AlfrescoRuntimeException("Found more than one user for "
+ searchUserName + " (case insensitive)");
}
}
}
private void delete(String searchUserName, NodeRef best)
{
SearchParameters sp = new SearchParameters();
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\""
+ searchUserName + "\"");
sp.addStore(storeRef);
sp.excludeDataInTheCurrentTransaction(false);
ResultSet rs = null;
try
{
rs = searchService.query(sp);
for (ResultSetRow row : rs)
{
NodeRef nodeRef = row.getNodeRef();
// Do not delete the best
if ((!best.equals(nodeRef)) && (nodeService.exists(nodeRef)))
{
String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(
nodeRef, ContentModel.PROP_USERNAME));
if (userNamesAreCaseSensitive)
{
if (realUserName.equals(searchUserName))
{
nodeService.deleteNode(nodeRef);
}
}
else
{
if (realUserName.equalsIgnoreCase(searchUserName))
{
nodeService.deleteNode(nodeRef);
}
}
}
}
}
finally
{
if (rs != null)
{
rs.close();
}
}
}
private void split(String searchUserName, NodeRef best)
{
SearchParameters sp = new SearchParameters();
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\""
+ searchUserName + "\"");
sp.addStore(storeRef);
sp.excludeDataInTheCurrentTransaction(false);
ResultSet rs = null;
try
{
rs = searchService.query(sp);
for (ResultSetRow row : rs)
{
NodeRef nodeRef = row.getNodeRef();
// Do not delete the best
if ((!best.equals(nodeRef)) && (nodeService.exists(nodeRef)))
{
String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(
nodeRef, ContentModel.PROP_USERNAME));
if (userNamesAreCaseSensitive)
{
if (realUserName.equals(searchUserName))
{
nodeService.setProperty(nodeRef, ContentModel.PROP_USERNAME, searchUserName
+ "(" + GUID.generate() + ")");
}
}
else
{
if (realUserName.equalsIgnoreCase(searchUserName))
{
nodeService.setProperty(nodeRef, ContentModel.PROP_USERNAME, searchUserName
+ GUID.generate());
}
}
}
}
}
finally
{
if (rs != null)
{
rs.close();
}
}
}
private NodeRef findBest(String searchUserName)
{
SearchParameters sp = new SearchParameters();
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("TYPE:\\{http\\://www.alfresco.org/model/content/1.0\\}person +@cm\\:userName:\""
+ searchUserName + "\"");
sp.addStore(storeRef);
sp.excludeDataInTheCurrentTransaction(false);
if (lastIsBest)
{
sp.addSort(SearchParameters.SORT_IN_DOCUMENT_ORDER_DESCENDING);
}
else
{
sp.addSort(SearchParameters.SORT_IN_DOCUMENT_ORDER_ASCENDING);
}
ResultSet rs = null;
NodeRef fallBack = null;
try
{
rs = searchService.query(sp);
for (ResultSetRow row : rs)
{
NodeRef nodeRef = row.getNodeRef();
if (fallBack == null)
{
fallBack = nodeRef;
}
// Do not delete the best
if (nodeService.exists(nodeRef))
{
String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(
nodeRef, ContentModel.PROP_USERNAME));
if (userNamesAreCaseSensitive)
{
if (realUserName.equals(searchUserName))
{
if (includeAutoCreated || !wasAutoCreated(nodeRef, searchUserName))
{
return nodeRef;
}
}
}
else
{
if (realUserName.equalsIgnoreCase(searchUserName))
{
if (includeAutoCreated || !wasAutoCreated(nodeRef, searchUserName))
{
return nodeRef;
}
}
}
}
}
}
finally
{
if (rs != null)
{
rs.close();
}
}
return fallBack;
}
private boolean wasAutoCreated(NodeRef nodeRef, String userName)
{
String testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef,
ContentModel.PROP_FIRSTNAME));
if ((testString == null) || !testString.equals(userName))
{
return false;
}
testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef,
ContentModel.PROP_LASTNAME));
if ((testString == null) || !testString.equals(""))
{
return false;
}
testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef,
ContentModel.PROP_EMAIL));
if ((testString == null) || !testString.equals(""))
{
return false;
}
testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef,
ContentModel.PROP_ORGID));
if ((testString == null) || !testString.equals(""))
{
return false;
}
testString = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef,
ContentModel.PROP_HOME_FOLDER_PROVIDER));
if ((testString == null) || !testString.equals(defaultHomeFolderProvider))
{
return false;
}
return true;
}
public boolean createMissingPeople()
@@ -225,8 +528,8 @@ public class PersonServiceImpl implements PersonService
}
else
{
String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(personNode,
ContentModel.PROP_USERNAME));
String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(
personNode, ContentModel.PROP_USERNAME));
properties.put(ContentModel.PROP_USERNAME, realUserName);
}

View File

@@ -53,6 +53,12 @@ public class UIDBasedHomeFolderProvider extends ExistingPathBasedHomeFolderProvi
{
String uid = DefaultTypeConverter.INSTANCE.convert(String.class, getServiceRegistry().getNodeService()
.getProperty(person, ContentModel.PROP_USERNAME));
if((uid == null) || (uid.length() == 0))
{
throw new PersonException("Can not create a home space when the uid is null or empty");
}
FileInfo fileInfo;
// Test if it already exists