null
if it is
+ * not an authority node.
+ * @param nodeRef
+ * the node to find authorities below
+ * @return the number of processed authorities
+ */
+ private int migrateAuthorities(String parentAuthority, NodeRef nodeRef)
+ {
+ int processedCount = 0;
+ Listnull
if not found
+ */
+ private NodeRef getAuthorityContainer()
+ {
+ NodeRef rootNodeRef = this.nodeService.getRootNode(this.userBootstrap.getStoreRef());
+ QName qnameAssocSystem = QName.createQName("sys", "system", this.namespaceService);
+ ListNode ID = (Long) row[0]; diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java index 470322f919..d4e260f18b 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneIndexerImpl.java @@ -1105,8 +1105,7 @@ public class ADMLuceneIndexerImpl extends AbstractLuceneIndexerImplimp { // Temporary special case for uids and gids if (propertyName.equals(ContentModel.PROP_USER_USERNAME) - || propertyName.equals(ContentModel.PROP_USERNAME) || propertyName.equals(ContentModel.PROP_AUTHORITY_NAME) - || propertyName.equals(ContentModel.PROP_MEMBERS)) + || propertyName.equals(ContentModel.PROP_USERNAME) || propertyName.equals(ContentModel.PROP_AUTHORITY_NAME)) { doc.add(new Field(attributeName, strValue, fieldStore, fieldIndex, Field.TermVector.NO)); } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java index 9836545b62..256de0275a 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AVMLuceneIndexerImpl.java @@ -1001,8 +1001,7 @@ public class AVMLuceneIndexerImpl extends AbstractLuceneIndexerImpl impl { // Temporary special case for uids and gids if (propertyName.equals(ContentModel.PROP_USER_USERNAME) - || propertyName.equals(ContentModel.PROP_USERNAME) || propertyName.equals(ContentModel.PROP_AUTHORITY_NAME) - || propertyName.equals(ContentModel.PROP_MEMBERS)) + || propertyName.equals(ContentModel.PROP_USERNAME) || propertyName.equals(ContentModel.PROP_AUTHORITY_NAME)) { doc.add(new Field(attributeName, strValue, fieldStore, fieldIndex, Field.TermVector.NO)); } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java index 3e26f99a4e..63de25c943 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAnalyser.java @@ -174,8 +174,7 @@ public class LuceneAnalyser extends Analyzer // Temporary fix for person and user uids if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME) - || propertyQName.equals(ContentModel.PROP_MEMBERS)) + || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) { analyser = new VerbatimAnalyser(true); } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java index 9c5dfa0525..90717991a9 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneQueryParser.java @@ -3082,8 +3082,7 @@ public class LuceneQueryParser extends QueryParser else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) { if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME) - || propertyQName.equals(ContentModel.PROP_MEMBERS)) + || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) { return subQueryBuilder.getQuery(expandedFieldName, queryText, analysisMode, luceneFunction); } @@ -3276,8 +3275,7 @@ public class LuceneQueryParser extends QueryParser else if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.TEXT))) { if (propertyQName.equals(ContentModel.PROP_USER_USERNAME) - || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME) - || propertyQName.equals(ContentModel.PROP_MEMBERS)) + || propertyQName.equals(ContentModel.PROP_USERNAME) || propertyQName.equals(ContentModel.PROP_AUTHORITY_NAME)) { throw new UnsupportedOperationException("Functions are not supported agaisnt special text fields"); } diff --git a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java index f4609760fb..c880f59ad7 100644 --- a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java +++ b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java @@ -35,9 +35,9 @@ import net.sf.acegisecurity.GrantedAuthorityImpl; import net.sf.acegisecurity.UserDetails; import net.sf.acegisecurity.providers.dao.User; -import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.sync.UserRegistrySynchronizer; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -63,6 +63,8 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC private Set defaultAdministratorUserNames = Collections.emptySet(); + private boolean syncWhenMissingPeopleLogIn = true; + private boolean autoCreatePeopleOnLogin = true; private AuthenticationContext authenticationContext; @@ -72,6 +74,8 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC private NodeService nodeService; private TransactionService transactionService; + + private UserRegistrySynchronizer userRegistrySynchronizer; public AbstractAuthenticationComponent() { @@ -107,6 +111,11 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC { this.transactionService = transactionService; } + + public void setUserRegistrySynchronizer(UserRegistrySynchronizer userRegistrySynchronizer) + { + this.userRegistrySynchronizer = userRegistrySynchronizer; + } public TransactionService getTransactionService() { @@ -138,6 +147,11 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC this.autoCreatePeopleOnLogin = autoCreatePeopleOnLogin; } + public void setSyncWhenMissingPeopleLogIn(boolean syncWhenMissingPeopleLogIn) + { + this.syncWhenMissingPeopleLogIn = syncWhenMissingPeopleLogIn; + } + public void authenticate(String userName, char[] password) throws AuthenticationException { // Support guest login from the login screen @@ -434,7 +448,30 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC { public String doWork() throws Exception { - if (personService.personExists(userName)) + boolean personExists = personService.personExists(userName); + + // If the person is missing, synchronize or auto-create the missing person if we are allowed + if (!personExists) + { + if ((userName != null) && !userName.equals(AuthenticationUtil.getSystemUserName())) + { + if (syncWhenMissingPeopleLogIn) + { + userRegistrySynchronizer.synchronize(false); + personExists = personService.personExists(userName); + } + if (!personExists && autoCreatePeopleOnLogin && personService.createMissingPeople()) + { + AuthorityType authorityType = AuthorityType.getAuthorityType(userName); + if (authorityType == AuthorityType.USER) + { + personService.getPerson(userName); + } + } + } + } + + if (personExists) { NodeRef userNode = personService.getPerson(userName); if (userNode != null) @@ -443,28 +480,8 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC // checks return (String) nodeService.getProperty(userNode, ContentModel.PROP_USERNAME); } - else - { - // Get user name - return userName; - } - } - else - { - if (autoCreatePeopleOnLogin && (userName != null) && !userName.equals(AuthenticationUtil.getSystemUserName())) - { - if (personService.createMissingPeople()) - { - AuthorityType authorityType = AuthorityType.getAuthorityType(userName); - if (authorityType == AuthorityType.USER) - { - personService.getPerson(userName); - } - } - } - // Get user name - return userName; } + return userName; } }, getSystemUserName(getUserDomain(userName))); diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java index 0d22e96aaf..76fc077ffd 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,7 @@ * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing + * FLOSS exception. You should have received a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ @@ -27,10 +27,11 @@ package org.alfresco.repo.security.authentication; import java.util.Collections; import java.util.Set; +import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.security.authentication.AuthenticationComponent.UserNameValidationMode; import org.alfresco.service.cmr.security.PermissionService; -public class AuthenticationServiceImpl extends AbstractAuthenticationService +public class AuthenticationServiceImpl extends AbstractAuthenticationService implements ActivateableBean { MutableAuthenticationDao authenticationDao; @@ -65,6 +66,16 @@ public class AuthenticationServiceImpl extends AbstractAuthenticationService { this.authenticationComponent = authenticationComponent; } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.ActivateableBean#isActive() + */ + public boolean isActive() + { + return !(this.authenticationComponent instanceof ActivateableBean) + || ((ActivateableBean) this.authenticationComponent).isActive(); + } public void createAuthentication(String userName, char[] password) throws AuthenticationException { diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java index 3a82fb5052..0b052c07ef 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,7 @@ * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing + * FLOSS exception. You should have received a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ @@ -27,6 +27,7 @@ package org.alfresco.repo.security.authentication.ldap; import javax.naming.NamingException; import javax.naming.directory.InitialDirContext; +import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationException; @@ -35,11 +36,13 @@ import org.alfresco.repo.security.authentication.AuthenticationException; * * @author Andy Hind */ -public class LDAPAuthenticationComponentImpl extends AbstractAuthenticationComponent +public class LDAPAuthenticationComponentImpl extends AbstractAuthenticationComponent implements ActivateableBean { private boolean escapeCommasInBind = false; private boolean escapeCommasInUid = false; + + private boolean active = true; private String userNameFormat; @@ -69,6 +72,20 @@ public class LDAPAuthenticationComponentImpl extends AbstractAuthenticationCompo { this.escapeCommasInUid = escapeCommasInUid; } + + public void setActive(boolean active) + { + this.active = active; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.management.subsystems.ActivateableBean#isActive() + */ + public boolean isActive() + { + return this.active; + } /** * Implement the authentication method diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java deleted file mode 100644 index b9424eecb8..0000000000 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPGroupExportSource.java +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.security.authentication.ldap; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.Writer; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; - -import javax.naming.NamingEnumeration; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.InitialDirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import javax.transaction.UserTransaction; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.importer.ExportSource; -import org.alfresco.repo.importer.ExportSourceImporterException; -import org.alfresco.repo.security.authority.AuthorityDAO; -import org.alfresco.service.cmr.repository.NodeRef; -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.alfresco.util.EqualsHelper; -import org.alfresco.util.GUID; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.dom4j.io.OutputFormat; -import org.dom4j.io.XMLWriter; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.context.ApplicationContext; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.AttributesImpl; - -public class LDAPGroupExportSource implements ExportSource, InitializingBean -{ - private static Log s_logger = LogFactory.getLog(LDAPGroupExportSource.class); - - private String groupQuery = "(objectclass=groupOfNames)"; - - private String searchBase; - - private String groupIdAttributeName = "cn"; - - private String userIdAttributeName = "uid"; - - private String groupType = "groupOfNames"; - - private String personType = "inetOrgPerson"; - - private LDAPInitialDirContextFactory ldapInitialContextFactory; - - private NamespaceService namespaceService; - - private String memberAttribute = "member"; - - private boolean errorOnMissingMembers = false; - - private boolean errorOnDuplicateGID = false; - - private QName viewRef; - - private QName viewId; - - private QName viewAssociations; - - private QName childQName; - - private QName viewValueQName; - - private QName viewIdRef; - - private AuthorityDAO authorityDAO; - - private boolean errorOnMissingGID; - - private boolean errorOnMissingUID; - - public LDAPGroupExportSource() - { - super(); - } - - public void setGroupIdAttributeName(String groupIdAttributeName) - { - this.groupIdAttributeName = groupIdAttributeName; - } - - public void setGroupQuery(String groupQuery) - { - this.groupQuery = groupQuery; - } - - public void setGroupType(String groupType) - { - this.groupType = groupType; - } - - public void setLDAPInitialDirContextFactory(LDAPInitialDirContextFactory ldapInitialDirContextFactory) - { - this.ldapInitialContextFactory = ldapInitialDirContextFactory; - } - - public void setMemberAttribute(String memberAttribute) - { - this.memberAttribute = memberAttribute; - } - - public void setNamespaceService(NamespaceService namespaceService) - { - this.namespaceService = namespaceService; - } - - public void setPersonType(String personType) - { - this.personType = personType; - } - - public void setSearchBase(String searchBase) - { - this.searchBase = searchBase; - } - - public void setUserIdAttributeName(String userIdAttributeName) - { - this.userIdAttributeName = userIdAttributeName; - } - - public void setErrorOnMissingMembers(boolean errorOnMissingMembers) - { - this.errorOnMissingMembers = errorOnMissingMembers; - } - - public void setErrorOnMissingGID(boolean errorOnMissingGID) - { - this.errorOnMissingGID = errorOnMissingGID; - } - - public void setErrorOnMissingUID(boolean errorOnMissingUID) - { - this.errorOnMissingUID = errorOnMissingUID; - } - - public void setErrorOnDuplicateGID(boolean errorOnDuplicateGID) - { - this.errorOnDuplicateGID = errorOnDuplicateGID; - } - - public void setAuthorityDAO(AuthorityDAO authorityDAO) - { - this.authorityDAO = authorityDAO; - } - - public void generateExport(XMLWriter writer) - { - HashSet rootGroups = new HashSet (); - HashMap lookup = new HashMap (); - HashSet secondaryLinks = new HashSet (); - - buildGroupsAndRoots(rootGroups, lookup, secondaryLinks); - - buildXML(rootGroups, lookup, secondaryLinks, writer); - - } - - private void buildXML(HashSet rootGroups, HashMap lookup, HashSet secondaryLinks, XMLWriter writer) - { - - Collection prefixes = namespaceService.getPrefixes(); - QName childQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "childName", namespaceService); - - try - { - AttributesImpl attrs = new AttributesImpl(); - attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, childQName.getLocalName(), childQName.toPrefixString(), null, ContentModel.TYPE_PERSON - .toPrefixString(namespaceService)); - - writer.startDocument(); - - for (String prefix : prefixes) - { - if (!prefix.equals("xml")) - { - String uri = namespaceService.getNamespaceURI(prefix); - writer.startPrefixMapping(prefix, uri); - } - } - - writer.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view", NamespaceService.REPOSITORY_VIEW_PREFIX + ":" + "view", new AttributesImpl()); - - // Create group structure - - for (Group group : rootGroups) - { - addRootGroup(lookup, group, writer); - } - - // Create secondary links. - - for (SecondaryLink sl : secondaryLinks) - { - addSecondarylink(lookup, sl, writer); - } - - for (String prefix : prefixes) - { - if (!prefix.equals("xml")) - { - writer.endPrefixMapping(prefix); - } - } - - writer.endElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view", NamespaceService.REPOSITORY_VIEW_PREFIX + ":" + "view"); - - writer.endDocument(); - } - catch (SAXException e) - { - throw new ExportSourceImporterException("Failed to create file for import.", e); - } - - } - - private void addSecondarylink(HashMap lookup, SecondaryLink sl, XMLWriter writer) throws SAXException - { - - String fromId = lookup.get(sl.from).guid; - String toId = lookup.get(sl.to).guid; - - AttributesImpl attrs = new AttributesImpl(); - attrs.addAttribute(viewIdRef.getNamespaceURI(), viewIdRef.getLocalName(), viewIdRef.toPrefixString(), null, fromId); - - writer.startElement(viewRef.getNamespaceURI(), viewRef.getLocalName(), viewRef.toPrefixString(namespaceService), attrs); - - writer.startElement(viewAssociations.getNamespaceURI(), viewAssociations.getLocalName(), viewAssociations.toPrefixString(namespaceService), new AttributesImpl()); - - writer.startElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(), ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService), - new AttributesImpl()); - - AttributesImpl attrsRef = new AttributesImpl(); - attrsRef.addAttribute(viewIdRef.getNamespaceURI(), viewIdRef.getLocalName(), viewIdRef.toPrefixString(), null, toId); - attrsRef.addAttribute(childQName.getNamespaceURI(), childQName.getLocalName(), childQName.toPrefixString(), null, QName.createQName(ContentModel.USER_MODEL_URI, sl.to) - .toPrefixString(namespaceService)); - - writer.startElement(viewRef.getNamespaceURI(), viewRef.getLocalName(), viewRef.toPrefixString(namespaceService), attrsRef); - - writer.endElement(viewRef.getNamespaceURI(), viewRef.getLocalName(), viewRef.toPrefixString(namespaceService)); - - writer.endElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(), ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService)); - - writer.endElement(viewAssociations.getNamespaceURI(), viewAssociations.getLocalName(), viewAssociations.toPrefixString(namespaceService)); - - writer.endElement(viewRef.getNamespaceURI(), viewRef.getLocalName(), viewRef.toPrefixString(namespaceService)); - - } - - private void addRootGroup(HashMap lookup, Group group, XMLWriter writer) throws SAXException - { - QName nodeUUID = QName.createQName("sys:node-uuid", namespaceService); - - AttributesImpl attrs = new AttributesImpl(); - attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, childQName.getLocalName(), childQName.toPrefixString(), null, QName.createQName(ContentModel.USER_MODEL_URI, - group.gid).toPrefixString(namespaceService)); - attrs.addAttribute(viewId.getNamespaceURI(), viewId.getLocalName(), viewId.toPrefixString(), null, group.guid); - - writer.startElement(ContentModel.TYPE_AUTHORITY_CONTAINER.getNamespaceURI(), ContentModel.TYPE_AUTHORITY_CONTAINER.getLocalName(), ContentModel.TYPE_AUTHORITY_CONTAINER - .toPrefixString(namespaceService), attrs); - - if ((authorityDAO != null) && authorityDAO.authorityExists(group.gid)) - { - NodeRef authNodeRef = authorityDAO.getAuthorityNodeRefOrNull(group.gid); - if (authNodeRef != null) - { - String uguid = authorityDAO.getAuthorityNodeRefOrNull(group.gid).getId(); - - writer.startElement(nodeUUID.getNamespaceURI(), nodeUUID.getLocalName(), nodeUUID.toPrefixString(namespaceService), new AttributesImpl()); - - writer.characters(uguid.toCharArray(), 0, uguid.length()); - - writer.endElement(nodeUUID.getNamespaceURI(), nodeUUID.getLocalName(), nodeUUID.toPrefixString(namespaceService)); - } - } - - writer.startElement(ContentModel.PROP_AUTHORITY_NAME.getNamespaceURI(), ContentModel.PROP_AUTHORITY_NAME.getLocalName(), ContentModel.PROP_AUTHORITY_NAME - .toPrefixString(namespaceService), new AttributesImpl()); - - writer.characters(group.gid.toCharArray(), 0, group.gid.length()); - - writer.endElement(ContentModel.PROP_AUTHORITY_NAME.getNamespaceURI(), ContentModel.PROP_AUTHORITY_NAME.getLocalName(), ContentModel.PROP_AUTHORITY_NAME - .toPrefixString(namespaceService)); - - if (group.members.size() > 0) - { - writer.startElement(ContentModel.PROP_MEMBERS.getNamespaceURI(), ContentModel.PROP_MEMBERS.getLocalName(), ContentModel.PROP_MEMBERS.toPrefixString(namespaceService), - new AttributesImpl()); - - for (String member : group.members) - { - writer.startElement(viewValueQName.getNamespaceURI(), viewValueQName.getLocalName(), viewValueQName.toPrefixString(namespaceService), new AttributesImpl()); - - writer.characters(member.toCharArray(), 0, member.length()); - - writer.endElement(viewValueQName.getNamespaceURI(), viewValueQName.getLocalName(), viewValueQName.toPrefixString(namespaceService)); - } - - writer.endElement(ContentModel.PROP_MEMBERS.getNamespaceURI(), ContentModel.PROP_MEMBERS.getLocalName(), ContentModel.PROP_MEMBERS.toPrefixString(namespaceService)); - } - - for (Group child : group.children) - { - addgroup(lookup, child, writer); - } - - writer.endElement(ContentModel.TYPE_AUTHORITY_CONTAINER.getNamespaceURI(), ContentModel.TYPE_AUTHORITY_CONTAINER.getLocalName(), ContentModel.TYPE_AUTHORITY_CONTAINER - .toPrefixString(namespaceService)); - - } - - private void addgroup(HashMap lookup, Group group, XMLWriter writer) throws SAXException - { - AttributesImpl attrs = new AttributesImpl(); - - writer.startElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(), ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService), - attrs); - - addRootGroup(lookup, group, writer); - - writer.endElement(ContentModel.ASSOC_MEMBER.getNamespaceURI(), ContentModel.ASSOC_MEMBER.getLocalName(), ContentModel.ASSOC_MEMBER.toPrefixString(namespaceService)); - } - - private void buildGroupsAndRoots(HashSet rootGroups, HashMap lookup, HashSet secondaryLinks) - { - InitialDirContext ctx = null; - try - { - ctx = ldapInitialContextFactory.getDefaultIntialDirContext(); - - SearchControls userSearchCtls = new SearchControls(); - userSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); - - NamingEnumeration searchResults = ctx.search(searchBase, groupQuery, userSearchCtls); - while (searchResults.hasMoreElements()) - { - SearchResult result = (SearchResult) searchResults.next(); - Attributes attributes = result.getAttributes(); - Attribute gidAttribute = attributes.get(groupIdAttributeName); - if (gidAttribute == null) - { - if (errorOnMissingGID) - { - throw new ExportSourceImporterException("Group returned by group search does not have mandatory group id attribute " + attributes); - } - else - { - s_logger.warn("Missing GID on " + attributes); - continue; - } - } - String gid = (String) gidAttribute.get(0); - - Group group = lookup.get("GROUP_" + gid); - if (group == null) - { - group = new Group(gid); - lookup.put(group.gid, group); - rootGroups.add(group); - } - else - { - if (errorOnDuplicateGID) - { - throw new ExportSourceImporterException("Duplicate group id found for " + gid); - } - else - { - s_logger.warn("Duplicate gid found for " + gid + " -> merging definitions"); - // Currently we will merge the two definitions together we do not support duplciate GIDs - } - } - Attribute memAttribute = attributes.get(memberAttribute); - // check for null - if (memAttribute != null) - { - for (int i = 0; i < memAttribute.size(); i++) - { - String attribute = (String) memAttribute.get(i); - if (attribute != null) - { - group.distinguishedNames.add(attribute); - } - } - } - } - - if (s_logger.isDebugEnabled()) - { - s_logger.debug("Found " + lookup.size()); - } - - for (Group group : lookup.values()) - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("Linking " + group.gid); - } - for (String dn : group.distinguishedNames) - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("... " + dn); - } - String id; - Boolean isGroup = null; - - SearchControls memberSearchCtls = new SearchControls(); - memberSearchCtls.setSearchScope(SearchControls.OBJECT_SCOPE); - NamingEnumeration memberSearchResults; - try - { - memberSearchResults = ctx.search(dn, "(objectClass=*)", memberSearchCtls); - } - catch (NamingException e) - { - if (errorOnMissingMembers) - { - throw e; - } - s_logger.warn("Failed to resolve distinguished name: " + dn); - continue; - } - while (memberSearchResults.hasMoreElements()) - { - id = null; - - SearchResult result; - try - { - result = (SearchResult) memberSearchResults.next(); - } - catch (NamingException e) - { - if (errorOnMissingMembers) - { - throw e; - } - s_logger.warn("Failed to resolve distinguished name: " + dn); - continue; - } - Attributes attributes = result.getAttributes(); - Attribute objectclass = attributes.get("objectclass"); - if (objectclass == null) - { - if (errorOnMissingMembers) - { - throw new ExportSourceImporterException("Failed to find attribute objectclass for DN " + dn); - } - else - { - continue; - } - } - for (int i = 0; i < objectclass.size(); i++) - { - String testType; - try - { - testType = (String) objectclass.get(i); - } - catch (NamingException e) - { - if (errorOnMissingMembers) - { - throw e; - } - s_logger.warn("Failed to resolve object class attribute for distinguished name: " + dn); - continue; - } - if (testType.equals(groupType)) - { - isGroup = true; - try - { - Attribute groupIdAttribute = attributes.get(groupIdAttributeName); - if (groupIdAttribute == null) - { - if (errorOnMissingGID) - { - throw new ExportSourceImporterException("Group missing group id attribute DN =" + dn + " att = " + groupIdAttributeName); - } - else - { - s_logger.warn("Group missing group id attribute DN =" + dn + " att = " + groupIdAttributeName); - continue; - } - } - id = (String) groupIdAttribute.get(0); - } - catch (NamingException e) - { - if (errorOnMissingMembers) - { - throw e; - } - s_logger.warn("Failed to resolve group identifier " + groupIdAttributeName + " for distinguished name: " + dn); - id = "Unknown sub group"; - } - break; - } - else if (testType.equals(personType)) - { - isGroup = false; - try - { - Attribute userIdAttribute = attributes.get(userIdAttributeName); - if (userIdAttribute == null) - { - if (errorOnMissingUID) - { - throw new ExportSourceImporterException("User missing user id attribute DN =" + dn + " att = " + userIdAttributeName); - } - else - { - s_logger.warn("User missing user id attribute DN =" + dn + " att = " + userIdAttributeName); - continue; - } - } - id = (String) userIdAttribute.get(0); - } - catch (NamingException e) - { - if (errorOnMissingMembers) - { - throw e; - } - s_logger.warn("Failed to resolve group identifier " + userIdAttributeName + " for distinguished name: " + dn); - id = "Unknown member"; - } - break; - } - } - - if (id != null) - { - if (isGroup == null) - { - if (errorOnMissingMembers) - { - throw new ExportSourceImporterException("Type not recognised for DN" + dn); - } - else - { - continue; - } - } - else if (isGroup) - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("... is sub group"); - } - Group child = lookup.get("GROUP_" + id); - if (child == null) - { - if (errorOnMissingMembers) - { - throw new ExportSourceImporterException("Failed to find child group " + id); - } - else - { - continue; - } - } - if (rootGroups.contains(child)) - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("... Primary created from " + group.gid + " to " + child.gid); - } - group.children.add(child); - rootGroups.remove(child); - } - else - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("... Secondary created from " + group.gid + " to " + child.gid); - } - secondaryLinks.add(new SecondaryLink(group.gid, child.gid)); - } - } - else - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("... is member"); - } - group.members.add(id); - } - } - } - } - } - if (s_logger.isDebugEnabled()) - { - s_logger.debug("Top " + rootGroups.size()); - s_logger.debug("Secondary " + secondaryLinks.size()); - } - } - catch (NamingException e) - { - throw new ExportSourceImporterException("Failed to import people.", e); - } - finally - { - if (ctx != null) - { - try - { - ctx.close(); - } - catch (NamingException e) - { - throw new ExportSourceImporterException("Failed to import people.", e); - } - } - } - } - - private static class Group - { - String gid; - - String guid = GUID.generate(); - - HashSet children = new HashSet (); - - HashSet members = new HashSet (); - - HashSet distinguishedNames = new HashSet (); - - private Group(String gid) - { - this.gid = "GROUP_" + gid; - } - - @Override - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - if (!(o instanceof Group)) - { - return false; - } - Group g = (Group) o; - return this.gid.equals(g.gid); - } - - @Override - public int hashCode() - { - return gid.hashCode(); - } - } - - private static class SecondaryLink - { - String from; - - String to; - - private SecondaryLink(String from, String to) - { - this.from = from; - this.to = to; - } - - @Override - public boolean equals(Object o) - { - if (this == o) - { - return true; - } - if (!(o instanceof Group)) - { - return false; - } - SecondaryLink l = (SecondaryLink) o; - return EqualsHelper.nullSafeEquals(this.from, l.from) && EqualsHelper.nullSafeEquals(this.to, l.to); - } - - @Override - public int hashCode() - { - int hashCode = 0; - if (from != null) - { - hashCode = hashCode * 37 + from.hashCode(); - } - if (to != null) - { - hashCode = hashCode * 37 + to.hashCode(); - } - return hashCode; - } - } - - public static void main(String[] args) throws Exception - { - ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); - ExportSource source = (ExportSource) ctx.getBean("ldapGroupExportSource"); - - 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); - source.generateExport(xmlWriter); - xmlWriter.close(); - - tx.commit(); - } - - private static XMLWriter createXMLExporter(Writer writer) - { - // Define output format - OutputFormat format = OutputFormat.createPrettyPrint(); - format.setNewLineAfterDeclaration(false); - format.setIndentSize(3); - format.setEncoding("UTF-8"); - - // Construct an XML Exporter - - XMLWriter xmlWriter = new XMLWriter(writer, format); - return xmlWriter; - } - - public void afterPropertiesSet() throws Exception - { - viewRef = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "reference", namespaceService); - viewId = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "id", namespaceService); - viewIdRef = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "idref", namespaceService); - viewAssociations = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "associations", namespaceService); - childQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "childName", namespaceService); - viewValueQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "value", namespaceService); - - } -} diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java deleted file mode 100644 index c94c6df89a..0000000000 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPPersonExportSource.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program 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 General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.security.authentication.ldap; - -import java.io.BufferedWriter; -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; -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; -import javax.naming.directory.InitialDirContext; -import javax.naming.directory.SearchControls; -import javax.naming.directory.SearchResult; -import javax.transaction.UserTransaction; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.importer.ExportSource; -import org.alfresco.repo.importer.ExportSourceImporterException; -import org.alfresco.service.cmr.security.PersonService; -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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.dom4j.io.OutputFormat; -import org.dom4j.io.XMLWriter; -import org.springframework.context.ApplicationContext; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.AttributesImpl; - -public class LDAPPersonExportSource implements ExportSource -{ - private static Log s_logger = LogFactory.getLog(LDAPPersonExportSource.class); - - private String personQuery = "(objectclass=inetOrgPerson)"; - - private String searchBase; - - private String userIdAttributeName; - - private LDAPInitialDirContextFactory ldapInitialContextFactory; - - private PersonService personService; - - private Map attributeMapping; - - private NamespaceService namespaceService; - - private Map attributeDefaults; - - private boolean errorOnMissingUID; - - public LDAPPersonExportSource() - { - super(); - } - - public void setPersonQuery(String personQuery) - { - this.personQuery = personQuery; - } - - public void setSearchBase(String searchBase) - { - this.searchBase = searchBase; - } - - public void setUserIdAttributeName(String userIdAttributeName) - { - this.userIdAttributeName = userIdAttributeName; - } - - public void setLDAPInitialDirContextFactory(LDAPInitialDirContextFactory ldapInitialDirContextFactory) - { - this.ldapInitialContextFactory = ldapInitialDirContextFactory; - } - - public void setPersonService(PersonService personService) - { - this.personService = personService; - } - - public void setAttributeDefaults(Map attributeDefaults) - { - this.attributeDefaults = attributeDefaults; - } - - public void setNamespaceService(NamespaceService namespaceService) - { - this.namespaceService = namespaceService; - } - - public void setAttributeMapping(Map attributeMapping) - { - this.attributeMapping = attributeMapping; - } - - public void setErrorOnMissingUID(boolean errorOnMissingUID) - { - this.errorOnMissingUID = errorOnMissingUID; - } - - public void generateExport(XMLWriter writer) - { - QName nodeUUID = QName.createQName("sys:node-uuid", namespaceService); - - Collection prefixes = namespaceService.getPrefixes(); - QName childQName = QName.createQName(NamespaceService.REPOSITORY_VIEW_PREFIX, "childName", namespaceService); - - try - { - writer.startDocument(); - - for (String prefix : prefixes) - { - if (!prefix.equals("xml")) - { - String uri = namespaceService.getNamespaceURI(prefix); - writer.startPrefixMapping(prefix, uri); - } - } - - writer.startElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view", - NamespaceService.REPOSITORY_VIEW_PREFIX + ":" + "view", new AttributesImpl()); - - HashSet uids = new HashSet (); - - InitialDirContext ctx = null; - try - { - ctx = ldapInitialContextFactory.getDefaultIntialDirContext(); - - // Authentication has been successful. - // Set the current user, they are now authenticated. - - SearchControls userSearchCtls = new SearchControls(); - userSearchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); - - userSearchCtls.setCountLimit(Integer.MAX_VALUE); - - NamingEnumeration searchResults = ctx.search(searchBase, personQuery, userSearchCtls); - RESULT_LOOP: while (searchResults.hasMoreElements()) - { - SearchResult result = (SearchResult) searchResults.next(); - Attributes attributes = result.getAttributes(); - Attribute uidAttribute = attributes.get(userIdAttributeName); - if (uidAttribute == null) - { - if (errorOnMissingUID) - { - 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 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); - } - - AttributesImpl attrs = new AttributesImpl(); - attrs.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, childQName.getLocalName(), childQName - .toPrefixString(), null, QName.createQName("cm", uid, namespaceService).toPrefixString(namespaceService)); - - writer.startElement(ContentModel.TYPE_PERSON.getNamespaceURI(), ContentModel.TYPE_PERSON - .getLocalName(), ContentModel.TYPE_PERSON.toPrefixString(namespaceService), attrs); - - for (String key : attributeMapping.keySet()) - { - QName keyQName = QName.createQName(key, namespaceService); - - writer.startElement(keyQName.getNamespaceURI(), keyQName.getLocalName(), keyQName - .toPrefixString(namespaceService), new AttributesImpl()); - - // cater for null - String attributeName = attributeMapping.get(key); - if (attributeName != null) - { - Attribute attribute = attributes.get(attributeName); - if (attribute != null) - { - String value = (String) attribute.get(0); - if (value != null) - { - writer.characters(value.toCharArray(), 0, value.length()); - } - } - else - { - String defaultValue = attributeDefaults.get(key); - if (defaultValue != null) - { - writer.characters(defaultValue.toCharArray(), 0, defaultValue.length()); - } - } - } - else - { - String defaultValue = attributeDefaults.get(key); - if (defaultValue != null) - { - writer.characters(defaultValue.toCharArray(), 0, defaultValue.length()); - } - } - - writer.endElement(keyQName.getNamespaceURI(), keyQName.getLocalName(), keyQName - .toPrefixString(namespaceService)); - } - - if (personService.personExists(uid)) - { - String uguid = personService.getPerson(uid).getId(); - - writer.startElement(nodeUUID.getNamespaceURI(), nodeUUID.getLocalName(), nodeUUID - .toPrefixString(namespaceService), new AttributesImpl()); - - writer.characters(uguid.toCharArray(), 0, uguid.length()); - - writer.endElement(nodeUUID.getNamespaceURI(), nodeUUID.getLocalName(), nodeUUID - .toPrefixString(namespaceService)); - } - writer.endElement(ContentModel.TYPE_PERSON.getNamespaceURI(), ContentModel.TYPE_PERSON - .getLocalName(), ContentModel.TYPE_PERSON.toPrefixString(namespaceService)); - - } - - } - catch (NamingException e) - { - throw new ExportSourceImporterException("Failed to import people.", e); - } - finally - { - if (ctx != null) - { - try - { - ctx.close(); - } - catch (NamingException e) - { - throw new ExportSourceImporterException("Failed to import people.", e); - } - } - } - - for (String prefix : prefixes) - { - if (!prefix.equals("xml")) - { - writer.endPrefixMapping(prefix); - } - } - - writer.endElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "view", NamespaceService.REPOSITORY_VIEW_PREFIX - + ":" + "view"); - - writer.endDocument(); - } - catch (SAXException e) - { - throw new ExportSourceImporterException("Failed to create file for import.", e); - } - } - - public static void main(String[] args) throws Exception - { - ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); - ExportSource source = (ExportSource) ctx.getBean("ldapPeopleExportSource"); - 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); - source.generateExport(xmlWriter); - xmlWriter.close(); - - tx.commit(); - } - - private static XMLWriter createXMLExporter(Writer writer) - { - // Define output format - OutputFormat format = OutputFormat.createPrettyPrint(); - format.setNewLineAfterDeclaration(false); - format.setIndentSize(3); - format.setEncoding("UTF-8"); - - // Construct an XML Exporter - - XMLWriter xmlWriter = new XMLWriter(writer, format); - return xmlWriter; - } -} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationComponent.java b/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationComponent.java index a9a2284757..23c0c4c34e 100644 --- a/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationComponent.java +++ b/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationComponent.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.LinkedList; import java.util.List; +import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.management.subsystems.ChildApplicationContextManager; import org.alfresco.repo.security.authentication.AbstractChainingAuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent; @@ -80,7 +81,15 @@ public class SubsystemChainingAuthenticationComponent extends AbstractChainingAu ApplicationContext context = this.applicationContextManager.getApplicationContext(instance); try { - result.add((AuthenticationComponent) context.getBean(sourceBeanName)); + AuthenticationComponent authenticationComponent = (AuthenticationComponent) context + .getBean(sourceBeanName); + // Only add active authentication components. E.g. we might have an ldap context that is only used for + // synchronizing + if (!(authenticationComponent instanceof ActivateableBean) + || ((ActivateableBean) authenticationComponent).isActive()) + { + result.add(authenticationComponent); + } } catch (NoSuchBeanDefinitionException e) { diff --git a/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationService.java b/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationService.java index cbf5b05949..e808d1a22a 100644 --- a/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationService.java +++ b/source/java/org/alfresco/repo/security/authentication/subsystems/SubsystemChainingAuthenticationService.java @@ -27,6 +27,7 @@ package org.alfresco.repo.security.authentication.subsystems; import java.util.LinkedList; import java.util.List; +import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.management.subsystems.ChildApplicationContextManager; import org.alfresco.repo.security.authentication.AbstractChainingAuthenticationService; import org.alfresco.service.cmr.security.AuthenticationService; @@ -83,7 +84,15 @@ public class SubsystemChainingAuthenticationService extends AbstractChainingAuth ApplicationContext context = this.applicationContextManager.getApplicationContext(instance); try { - return (AuthenticationService) context.getBean(sourceBeanName); + AuthenticationService authenticationService = (AuthenticationService) context.getBean(sourceBeanName); + // Only add active authentication services. E.g. we might have an ldap context that is only used for + // synchronizing + if (!(authenticationService instanceof ActivateableBean) + || ((ActivateableBean) authenticationService).isActive()) + { + + return authenticationService; + } } catch (NoSuchBeanDefinitionException e) { @@ -107,7 +116,15 @@ public class SubsystemChainingAuthenticationService extends AbstractChainingAuth ApplicationContext context = this.applicationContextManager.getApplicationContext(instance); try { - result.add((AuthenticationService) context.getBean(sourceBeanName)); + AuthenticationService authenticationService = (AuthenticationService) context.getBean(sourceBeanName); + // Only add active authentication components. E.g. we might have an ldap context that is only used for + // synchronizing + if (!(authenticationService instanceof ActivateableBean) + || ((ActivateableBean) authenticationService).isActive()) + { + + result.add(authenticationService); + } } catch (NoSuchBeanDefinitionException e) { diff --git a/source/java/org/alfresco/repo/security/authentication/userModel.xml b/source/java/org/alfresco/repo/security/authentication/userModel.xml index a80e02439c..12b622be3a 100644 --- a/source/java/org/alfresco/repo/security/authentication/userModel.xml +++ b/source/java/org/alfresco/repo/security/authentication/userModel.xml @@ -2,8 +2,8 @@ Alfresco User Model Alfresco -2005-08-16 -0.1 +2009-06-04 +0.2 @@ -16,7 +16,6 @@ - @@ -75,42 +74,6 @@
null
if the authority does not exist.
+ */
+ public String getAuthorityZone(String name);
+
+ /**
+ * Gets the names of all authorities in a zone, optionally filtered by type.
+ *
+ * @param zoneName
+ * the zone name
+ * @param type
+ * the authority type to filter by or null
for all authority types
+ * @return the names of all authorities in a zone, optionally filtered by type
+ */
+ public SetChainingUserRegistrySynchronizer
is responsible for synchronizing Alfresco's local user (person) and
+ * group (authority) information with the external subsystems in the authentication chain (most typically LDAP
+ * directories). When the {@link #synchronize(boolean)} method is called, it visits each {@link UserRegistry} bean in
+ * the 'chain' of application contexts, managed by a {@link ChildApplicationContextManager}, and compares its
+ * timestamped user and group information with the local users and groups last retrieved from the same source. Any
+ * updates and additions made to those users and groups are applied to the local copies. The ordering of each
+ * {@link UserRegistry} in the chain determines its precedence when it comes to user and group name collisions.
+ *
+ * The force
argument determines whether a complete or partial set of information is queried from the
+ * {@link UserRegistry}. When true
then all users and groups are queried. With this complete set of
+ * information, the synchronizer is able to identify which users and groups have been deleted, so it will delete users
+ * and groups as well as update and create them. Since processing all users and groups may be fairly time consuming, it
+ * is recommended this mode is only used by a background scheduled synchronization job. When the argument is
+ * false
then only those users and groups modified since the most recent modification date of all the
+ * objects last queried from the same {@link UserRegistry} are retrieved. In this mode, local users and groups are
+ * created and updated, but not deleted (except where a name collision with a lower priority {@link UserRegistry} is
+ * detected). This 'differential' mode is much faster, and by default is triggered when a user is successfully
+ * authenticated who doesn't yet have a local person object in Alfresco. This should mean that new users and their group
+ * information are pulled over from LDAP servers as and when required.
+ *
+ * @author dward
+ */
+public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronizer
+{
+ /** The logger. */
+ private static final Log logger = LogFactory.getLog(ChainingUserRegistrySynchronizer.class);
+
+ /** The path in the attribute service below which we persist attributes. */
+ private static final String ROOT_ATTRIBUTE_PATH = ".ChainingUserRegistrySynchronizer";
+
+ /** The label under which the last group modification timestamp is stored for each zone. */
+ private static final String GROUP_LAST_MODIFIED_ATTRIBUTE = "GROUP";
+
+ /** The label under which the last user modification timestamp is stored for each zone. */
+ private static final String PERSON_LAST_MODIFIED_ATTRIBUTE = "PERSON";
+
+ /** The manager for the autentication chain to be traversed. */
+ private ChildApplicationContextManager applicationContextManager;
+
+ /** The name used to look up a {@link UserRegistry} bean in each child application context. */
+ private String sourceBeanName;
+
+ /** The authority service. */
+ private AuthorityService authorityService;
+
+ /** The person service. */
+ private PersonService personService;
+
+ /** The attribute service. */
+ private AttributeService attributeService;
+
+ /**
+ * Sets the application context manager.
+ *
+ * @param applicationContextManager
+ * the applicationContextManager to set
+ */
+ public void setApplicationContextManager(ChildApplicationContextManager applicationContextManager)
+ {
+ this.applicationContextManager = applicationContextManager;
+ }
+
+ /**
+ * Sets the name used to look up a {@link UserRegistry} bean in each child application context.
+ *
+ * @param sourceBeanName
+ * the bean name
+ */
+ public void setSourceBeanName(String sourceBeanName)
+ {
+ this.sourceBeanName = sourceBeanName;
+ }
+
+ /**
+ * Sets the authority service.
+ *
+ * @param authorityService
+ * the new authority service
+ */
+ public void setAuthorityService(AuthorityService authorityService)
+ {
+ this.authorityService = authorityService;
+ }
+
+ /**
+ * Sets the person service.
+ *
+ * @param personService
+ * the new person service
+ */
+ public void setPersonService(PersonService personService)
+ {
+ this.personService = personService;
+ }
+
+ /**
+ * Sets the attribute service.
+ *
+ * @param attributeService
+ * the new attribute service
+ */
+ public void setAttributeService(AttributeService attributeService)
+ {
+ this.attributeService = attributeService;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#synchronize(boolean)
+ */
+ public void synchronize(boolean force)
+ {
+ Settrue
if all persons are to be queried. false
if only those changed since the
+ * most recent queried user should be queried.
+ * @param visitedZoneIds
+ * the set of zone ids already processed. These zones have precedence over the current zone when it comes
+ * to user name 'collisions'. If a user is queried that already exists locally but is tagged with one of
+ * the zones in this set, then it will be ignored as this zone has lower priority.
+ * @return the number of users processed
+ */
+ private int syncPersonsWithPlugin(String zoneId, UserRegistry userRegistry, boolean force,
+ Settrue
if all groups are to be queried. false
if only those changed since the
+ * most recent queried group should be queried.
+ * @param visitedZoneIds
+ * the set of zone ids already processed. These zones have precedence over the current zone when it comes
+ * to group name 'collisions'. If a group is queried that already exists locally but is tagged with one
+ * of the zones in this set, then it will be ignored as this zone has lower priority.
+ * @return the number of groups processed
+ */
+ private int syncGroupsWithPlugin(String zoneId, UserRegistry plugin, boolean force, Set
+ * Z1
+ * G1
+ * G2 - U1, G3 - U2, G4, G5
+ *
+ * Z2
+ * G2 - U1, U3, U4
+ * G6 - U3, U4, G7 - U5
+ *
+ *
+ * @throws Exception
+ * the exception
+ */
+ public void setUpTestUsersAndGroups() throws Exception
+ {
+ this.applicationContextManager.setUserRegistries(new MockUserRegistry("Z1", new NodeDescription[]
+ {
+ newPerson("U1"), newPerson("U2")
+ }, new NodeDescription[]
+ {
+ newGroup("G1"), 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);
+ assertExists("Z1", "U1");
+ assertExists("Z1", "U2");
+ assertExists("Z1", "G1");
+ 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");
+ }
+
+ /**
+ * 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.setUserRegistries(new MockUserRegistry("Z1", new NodeDescription[]
+ {
+ newPerson("U1", "changeofemail@alfresco.com"), newPerson("U6")
+ }, new NodeDescription[]
+ {
+ newGroup("G1", "U1", "U6"), newGroup("G2", "U1"), newGroup("G5", "U6")
+ }), new MockUserRegistry("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.synchronizer.synchronize(false);
+ assertExists("Z1", "U1");
+ assertEmailEquals("U1", "changeofemail@alfresco.com");
+ assertExists("Z1", "U2");
+ assertExists("Z1", "U6");
+ assertExists("Z1", "G1", "U1", "U6");
+ assertExists("Z1", "G2", "U1");
+ assertExists("Z1", "G3", "U2", "G4", "G5");
+ assertExists("Z1", "G4");
+ assertExists("Z1", "G5", "U6");
+ assertExists("Z2", "U3");
+ assertExists("Z2", "U4");
+ assertExists("Z2", "U5");
+ assertEmailEquals("U5", "u5email@alfresco.com");
+ assertExists("Z2", "G6", "U3", "U4", "G7");
+ assertExists("Z2", "G7");
+ }
+
+ /**
+ * 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. 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", "U6"), newGroup("G6", "U3", "G7"), newGroup("G7")
+ }));
+ this.synchronizer.synchronize(true);
+ 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");
+ }
+
+ /**
+ * 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)
+ {
+ NodeDescription group = new NodeDescription();
+ PropertyMap properties = group.getProperties();
+ properties.put(ContentModel.PROP_AUTHORITY_NAME, longName(name));
+ if (members.length > 0)
+ {
+ SetUserRegistry
is an encapsulation of an external registry from which user and group information can be
+ * queried (typically an LDAP directory). Implementations may optional support the ability to query only those users and
+ * groups modified since a certain time.
+ *
+ * @author dward
+ */
+public interface UserRegistry
+{
+ /**
+ * Gets descriptions of all the persons (users) in the user registry or all those changed since a certain date.
+ *
+ * @param modifiedSince
+ * if non-null, then only descriptions of users modified since this date should be returned; if
+ * null
then descriptions of all users should be returned.
+ * @return a {@link Iterator} over {@link NodeDescription}s of all the persons (users) in the user registry or all
+ * those changed since a certain date. The description properties should correspond to those of an Alfresco
+ * person node.
+ */
+ public Iteratornull
then descriptions of all groups should be returned.
+ * @return a {@link Iterator} over {@link NodeDescription}s of all the groups in the user registry or all those
+ * changed since a certain date. The description properties should correspond to those of an Alfresco
+ * authority node.
+ */
+ public IteratorUserRegistrySynchronizer
is responsible for synchronizing Alfresco's local user (person) and group
+ * (authority) information with one or more external sources (most typically LDAP directories).
+ *
+ * @author dward
+ */
+public interface UserRegistrySynchronizer
+{
+ /**
+ * Retrieves timestamped user and group information from configured external sources and compares it with the local
+ * users and groups last retrieved from the same sources. Any updates and additions made to those users and groups
+ * are applied to the local Alfresco copies.
+ *
+ * @param force
+ * Should a complete or partial set of information be queried from the external sources? When
+ * true
then all users and groups are queried. With this complete set of information,
+ * the implementation is able to identify which users and groups have been deleted, so it will delete
+ * users and groups as well as update and create them. When false
then each source is only
+ * queried for those users and groups modified since the most recent modification date of all the objects
+ * last queried from that same source. In this mode, local users and groups are created and updated, but
+ * not deleted.
+ */
+ public void synchronize(boolean force);
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/security/sync/UserRegistrySynchronizerJob.java b/source/java/org/alfresco/repo/security/sync/UserRegistrySynchronizerJob.java
new file mode 100644
index 0000000000..60ad687133
--- /dev/null
+++ b/source/java/org/alfresco/repo/security/sync/UserRegistrySynchronizerJob.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2005-2009 Alfresco Software Limited.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program 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 General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+ * As a special exception to the terms and conditions of version 2.0 of
+ * the GPL, you may redistribute this Program in connection with Free/Libre
+ * and Open Source Software ("FLOSS") applications as described in Alfresco's
+ * FLOSS exception. You should have received a copy of the text describing
+ * the FLOSS exception, and it is also available here:
+ * http://www.alfresco.com/legal/licensing"
+ */
+package org.alfresco.repo.security.sync;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+
+/**
+ * A scheduled job that regularly invokes a {@link UserRegistrySynchronizer}. Supports a
+ * synchronizeChangesOnly
string parameter. When "true"
means that the
+ * {@link UserRegistrySynchronizer#synchronize(boolean)} method will be called with a false
argument rather
+ * than the default true
.
+ *
+ * @author dward
+ */
+public class UserRegistrySynchronizerJob implements Job
+{
+ /*
+ * (non-Javadoc)
+ * @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
+ */
+ public void execute(JobExecutionContext executionContext) throws JobExecutionException
+ {
+ final UserRegistrySynchronizer userRegistrySynchronizer = (UserRegistrySynchronizer) executionContext
+ .getJobDetail().getJobDataMap().get("userRegistrySynchronizer");
+ final String synchronizeChangesOnly = (String) executionContext.getJobDetail().getJobDataMap().get(
+ "synchronizeChangesOnly");
+ AuthenticationUtil.runAs(new RunAsWork