diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index 8d85b92220..c038bb6e38 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -90,11 +90,11 @@ - - - - - ${user.name.caseSensitive} + + + + + @@ -231,6 +231,20 @@ + + + + + ${user.name.caseSensitive} + + + ${domain.name.caseSensitive} + + + ${domain.separator} + + + @@ -288,8 +302,8 @@ ${server.transaction.allow-writes} - - ${user.name.caseSensitive} + + @@ -304,6 +318,9 @@ false + + + @@ -316,6 +333,10 @@ + + + + ${home.folder.creation.eager} diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 3b886c144c..7c0af4d308 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -38,10 +38,11 @@ index.recovery.maximumPoolSize=5 # http://wiki.alfresco.com/wiki/High_Availability_Configuration_V1.4_to_V2.1#Version_1.4.5.2C_2.1.1_and_later # By default, this is effectively never, but can be modified as required. # Examples: +# Never: * * * * * ? 2099 # Once every five seconds: 0/5 * * * * ? # Once every two seconds : 0/2 * * * * ? # See http://quartz.sourceforge.net/javadoc/org/quartz/CronTrigger.html -index.tracking.cronExpression=* * * * * ? 2099 +index.tracking.cronExpression=0/5 * * * * ? index.tracking.adm.cronExpression=${index.tracking.cronExpression} index.tracking.avm.cronExpression=${index.tracking.cronExpression} # Other properties. @@ -231,6 +232,8 @@ system.workflow_container.childname=sys:workflow # Are user names case sensitive? user.name.caseSensitive=false +domain.name.caseSensitive=false +domain.separator= # AVM Specific properties. avm.remote.idlestream.timeout=30000 @@ -259,6 +262,9 @@ system.usages.enabled=true # Repository endpoint - used by Activity Service repo.remote.endpoint.url=http://localhost:8080/alfresco/service +# Create home folders as people are created (true) or create them lazily (false) +home.folder.creation.eager=true + # The well known RMI registry port is defined in the alfresco-shared.properties file # alfresco.rmi.services.port=50500 # diff --git a/source/java/org/alfresco/repo/avm/AVMInterpreter.java b/source/java/org/alfresco/repo/avm/AVMInterpreter.java index 0290072327..6a41c70a9c 100644 --- a/source/java/org/alfresco/repo/avm/AVMInterpreter.java +++ b/source/java/org/alfresco/repo/avm/AVMInterpreter.java @@ -357,11 +357,15 @@ public class AVMInterpreter } else if (command[0].equals("snap")) { - if (command.length != 2) + if ((command.length < 2) || (command.length > 4)) { return "Syntax Error."; } - fService.createSnapshot(command[1], null, null); + + String tag = (command.length > 2) ? command[2] : null; + String description = (command.length > 3) ? command[3] : null; + + fService.createSnapshot(command[1], tag, description); } else if (command[0].equals("cat")) { diff --git a/source/java/org/alfresco/repo/avm/ChildKey.java b/source/java/org/alfresco/repo/avm/ChildKey.java index d6258fd101..39b72c16d8 100644 --- a/source/java/org/alfresco/repo/avm/ChildKey.java +++ b/source/java/org/alfresco/repo/avm/ChildKey.java @@ -109,7 +109,7 @@ public class ChildKey implements Serializable } ChildKey o = (ChildKey)other; return fParent.equals(o.getParent()) && - fName.equals(o.getName()); + fName.equalsIgnoreCase(o.getName()); } /** @@ -117,6 +117,6 @@ public class ChildKey implements Serializable */ public int hashCode() { - return fParent.hashCode() + fName.hashCode(); + return fParent.hashCode() + fName.toLowerCase().hashCode(); } } diff --git a/source/java/org/alfresco/repo/avm/locking/AVMLockingBootstrap.java b/source/java/org/alfresco/repo/avm/locking/AVMLockingBootstrap.java index f2cde86fb5..a446b839f3 100644 --- a/source/java/org/alfresco/repo/avm/locking/AVMLockingBootstrap.java +++ b/source/java/org/alfresco/repo/avm/locking/AVMLockingBootstrap.java @@ -25,32 +25,41 @@ package org.alfresco.repo.avm.locking; +import org.alfresco.service.cmr.avm.locking.AVMLockingService; import org.alfresco.util.AbstractLifecycleBean; import org.springframework.context.ApplicationEvent; /** * Bootstrap for AVM Locking Service. + * * @author britt */ public class AVMLockingBootstrap extends AbstractLifecycleBean { - private AVMLockingServiceImpl fLockingService; - - public void setAvmLockingService(AVMLockingServiceImpl service) + private AVMLockingService fLockingService; + + public void setAvmLockingService(AVMLockingService service) { fLockingService = service; } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.alfresco.util.AbstractLifecycleBean#onBootstrap(org.springframework.context.ApplicationEvent) */ @Override protected void onBootstrap(ApplicationEvent event) { - fLockingService.init(); + if (fLockingService instanceof AVMLockingServiceImpl) + { + ((AVMLockingServiceImpl) fLockingService).init(); + } } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.alfresco.util.AbstractLifecycleBean#onShutdown(org.springframework.context.ApplicationEvent) */ @Override diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java index 543ced0f61..0082156b1b 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java @@ -51,6 +51,7 @@ import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.ExpiryMode; import org.alfresco.repo.security.authentication.InMemoryTicketComponentImpl.Ticket; +import org.alfresco.repo.security.person.UserNameMatcher; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; @@ -118,6 +119,8 @@ public class AuthenticationTest extends TestCase private PersonService personService; + private UserNameMatcher userNameMatcher; + public AuthenticationTest() { super(); @@ -143,6 +146,7 @@ public class AuthenticationTest extends TestCase authenticationComponentImpl = (AuthenticationComponent) ctx.getBean("authenticationComponent"); pubPersonService = (PersonService) ctx.getBean("PersonService"); personService = (PersonService) ctx.getBean("personService"); + userNameMatcher = (UserNameMatcher) ctx.getBean("userNameMatcher"); // permissionServiceSPI = (PermissionServiceSPI) // ctx.getBean("permissionService"); ticketsCache = (SimpleCache) ctx.getBean("ticketsCache"); @@ -182,6 +186,8 @@ public class AuthenticationTest extends TestCase dao.setDictionaryService(dictionaryService); dao.setNamespaceService(getNamespacePrefixReolsver("")); dao.setPasswordEncoder(passwordEncoder); + dao.setUserNameMatcher(userNameMatcher); + dao.setRetryingTransactionHelper(transactionService.getRetryingTransactionHelper()); if (dao.getUserOrNull("andy") != null) { @@ -388,6 +394,8 @@ public class AuthenticationTest extends TestCase dao.setDictionaryService(dictionaryService); dao.setNamespaceService(getNamespacePrefixReolsver("")); dao.setPasswordEncoder(passwordEncoder); + dao.setUserNameMatcher(userNameMatcher); + dao.setRetryingTransactionHelper(transactionService.getRetryingTransactionHelper()); dao.createUser("Andy", "cabbage".toCharArray()); assertNotNull(dao.getUserOrNull("Andy")); diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java index 18b4082c8d..ee5b0edd6a 100644 --- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java @@ -39,9 +39,9 @@ import net.sf.acegisecurity.providers.encoding.PasswordEncoder; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.person.UserNameMatcher; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.service.Managed; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.InvalidNodeRefException; @@ -63,6 +63,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao private static final StoreRef STOREREF_USERS = new StoreRef("user", "alfrescoUserStore"); private NodeService nodeService; + private TenantService tenantService; private NamespacePrefixResolver namespacePrefixResolver; @@ -71,22 +72,26 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao private DictionaryService dictionaryService; private SearchService searchService; - + private RetryingTransactionHelper retryingTransactionHelper; private PasswordEncoder passwordEncoder; - private boolean userNamesAreCaseSensitive; + private UserNameMatcher userNameMatcher; + public RepositoryAuthenticationDao() + { + super(); + } + public boolean getUserNamesAreCaseSensitive() { - return userNamesAreCaseSensitive; + return userNameMatcher.getUserNamesAreCaseSensitive(); } - @Managed(category="Security") - public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) + public void setUserNameMatcher(UserNameMatcher userNameMatcher) { - this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; + this.userNameMatcher = userNameMatcher; } public void setDictionaryService(DictionaryService dictionaryService) @@ -103,7 +108,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { this.nodeService = nodeService; } - + public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper) { this.retryingTransactionHelper = retryingTransactionHelper; @@ -124,8 +129,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao this.searchService = searchService; } - public UserDetails loadUserByUsername(String incomingUserName) throws UsernameNotFoundException, - DataAccessException + public UserDetails loadUserByUsername(String incomingUserName) throws UsernameNotFoundException, DataAccessException { NodeRef userRef = getUserOrNull(incomingUserName); if (userRef == null) @@ -134,28 +138,25 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao } Map properties = nodeService.getProperties(userRef); - String password = DefaultTypeConverter.INSTANCE.convert(String.class, properties - .get(ContentModel.PROP_PASSWORD)); + String password = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_PASSWORD)); // Report back the user name as stored on the user - String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties - .get(ContentModel.PROP_USER_USERNAME)); + String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_USER_USERNAME)); GrantedAuthority[] gas = new GrantedAuthority[1]; gas[0] = new GrantedAuthorityImpl("ROLE_AUTHENTICATED"); - UserDetails ud = new User(userName, password, getEnabled(userRef), !getAccountHasExpired(userRef), - !getCredentialsHaveExpired(userRef), !getAccountlocked(userRef), gas); + UserDetails ud = new User(userName, password, getEnabled(userRef), !getAccountHasExpired(userRef), !getCredentialsHaveExpired(userRef), !getAccountlocked(userRef), gas); return ud; } public NodeRef getUserOrNull(String searchUserName) { - if(searchUserName == null) + if (searchUserName == null) { return null; } - if(searchUserName.length() == 0) + if (searchUserName.length() == 0) { return null; } @@ -189,75 +190,37 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao final NodeRef nodeRef = row.getNodeRef(); if (nodeService.exists(nodeRef)) { - String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty( - nodeRef, ContentModel.PROP_USER_USERNAME)); + String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_USER_USERNAME)); - if (userNamesAreCaseSensitive) + if(userNameMatcher.matches(realUserName, searchUserName)) { - if (realUserName.equals(searchUserName)) + if (returnRef == null) { - if(returnRef == null) + returnRef = nodeRef; + } + else + { + try { - returnRef = nodeRef; + this.retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + // Delete the extra user node references + RepositoryAuthenticationDao.this.nodeService.deleteNode(nodeRef); + + return null; + } + + }, false, true); } - else + catch (InvalidNodeRefException exception) { - try - { - this.retryingTransactionHelper.doInTransaction( - new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() - throws Throwable - { - // Delete the extra user node references - RepositoryAuthenticationDao.this.nodeService.deleteNode(nodeRef); - - return null; - } - - }, false, true); - } - catch (InvalidNodeRefException exception) - { - // Ignore this exception as the node has already been deleted - } - } - } - } - else - { - if (realUserName.equalsIgnoreCase(searchUserName)) - { - if(returnRef == null) - { - returnRef = nodeRef; - } - else - { - try - { - this.retryingTransactionHelper.doInTransaction( - new RetryingTransactionHelper.RetryingTransactionCallback() - { - public Object execute() - throws Throwable - { - // Delete the extra user node references - RepositoryAuthenticationDao.this.nodeService.deleteNode(nodeRef); - - return null; - } - - }, false, true); - } - catch (InvalidNodeRefException exception) - { - // Ignore this exception as the node has already been deleted - } + // Ignore this exception as the node has already been deleted } } } + } } @@ -291,8 +254,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao properties.put(ContentModel.PROP_CREDENTIALS_EXPIRE, Boolean.valueOf(false)); properties.put(ContentModel.PROP_ENABLED, Boolean.valueOf(true)); properties.put(ContentModel.PROP_ACCOUNT_LOCKED, Boolean.valueOf(false)); - nodeService.createNode(typesNode, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_USER, ContentModel.TYPE_USER, - properties); + nodeService.createNode(typesNode, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_USER, ContentModel.TYPE_USER, properties); } private NodeRef getUserFolderLocation(String caseSensitiveUserName) @@ -304,8 +266,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao // AR-527 NodeRef rootNode = nodeService.getRootNode(userStoreRef); - List results = nodeService.getChildAssocs(rootNode, RegexQNamePattern.MATCH_ALL, - qnameAssocSystem); + List results = nodeService.getChildAssocs(rootNode, RegexQNamePattern.MATCH_ALL, qnameAssocSystem); NodeRef sysNodeRef = null; if (results.size() == 0) { @@ -404,11 +365,9 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { return null; } - if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, - ContentModel.PROP_ACCOUNT_EXPIRES))) + if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, ContentModel.PROP_ACCOUNT_EXPIRES))) { - return DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, - ContentModel.PROP_ACCOUNT_EXPIRY_DATE)); + return DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, ContentModel.PROP_ACCOUNT_EXPIRY_DATE)); } else { @@ -427,11 +386,9 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { return false; } - if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, - ContentModel.PROP_ACCOUNT_EXPIRES))) + if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, ContentModel.PROP_ACCOUNT_EXPIRES))) { - Date date = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, - ContentModel.PROP_ACCOUNT_EXPIRY_DATE)); + Date date = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, ContentModel.PROP_ACCOUNT_EXPIRY_DATE)); if (date == null) { return false; @@ -498,11 +455,9 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { return null; } - if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, - ContentModel.PROP_CREDENTIALS_EXPIRE))) + if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, ContentModel.PROP_CREDENTIALS_EXPIRE))) { - return DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, - ContentModel.PROP_CREDENTIALS_EXPIRY_DATE)); + return DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, ContentModel.PROP_CREDENTIALS_EXPIRY_DATE)); } else { @@ -521,11 +476,9 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao { return false; } - if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, - ContentModel.PROP_CREDENTIALS_EXPIRE))) + if (DefaultTypeConverter.INSTANCE.booleanValue(nodeService.getProperty(userNode, ContentModel.PROP_CREDENTIALS_EXPIRE))) { - Date date = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, - ContentModel.PROP_CREDENTIALS_EXPIRY_DATE)); + Date date = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(userNode, ContentModel.PROP_CREDENTIALS_EXPIRY_DATE)); if (date == null) { return false; @@ -634,8 +587,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao } else { - String password = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(userNode, - ContentModel.PROP_PASSWORD)); + String password = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(userNode, ContentModel.PROP_PASSWORD)); return password; } } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java index 3e0c1a6692..1c8efd8865 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java @@ -590,6 +590,26 @@ public class PermissionServiceTest extends AbstractPermissionTest assertEquals(permissionService.hasPermission(rootNodeRef, (PermissionService.CONSUMER)), AccessStatus.DENIED); } + public void testEqualBarCaseAuthorities() + { + + runAs("admin"); + + NodeRef n1 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}one"), ContentModel.TYPE_FOLDER).getChildRef(); + + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "andy", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "Andy", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "ANDY", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "woof/adobe", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "woof/Adobe", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "woof/ADOBE", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "Woof/Adobe", AccessStatus.ALLOWED)); + permissionService.setPermission(new SimplePermissionEntry(n1, getPermission(PermissionService.READ), "WOOF/ADOBE", AccessStatus.ALLOWED)); + + assertEquals(8, permissionService.getAllSetPermissions(n1).size()); + } + + public void testGetAllSetPermissions() { runAs("andy"); diff --git a/source/java/org/alfresco/repo/security/person/HomeFolderManager.java b/source/java/org/alfresco/repo/security/person/HomeFolderManager.java index 723bcbd9b0..f9bef16786 100644 --- a/source/java/org/alfresco/repo/security/person/HomeFolderManager.java +++ b/source/java/org/alfresco/repo/security/person/HomeFolderManager.java @@ -49,6 +49,8 @@ public class HomeFolderManager implements InitializingBean, NodeServicePolicies. private PolicyComponent policyComponent; private NodeService nodeService; + + private boolean enableHomeFolderCreationAsPeopleAreCreated = false; /** * A default provider @@ -61,12 +63,19 @@ public class HomeFolderManager implements InitializingBean, NodeServicePolicies. private Map providers = new HashMap(); /** - * Bind the calss behaviour to this implementation + * Bind the class behaviour to this implementation */ public void afterPropertiesSet() throws Exception { - policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), - ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); + if (enableHomeFolderCreationAsPeopleAreCreated) + { + policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); + } + } + + public void setEnableHomeFolderCreationAsPeopleAreCreated(boolean enableHomeFolderCreationAsPeopleAreCreated) + { + this.enableHomeFolderCreationAsPeopleAreCreated = enableHomeFolderCreationAsPeopleAreCreated; } /** diff --git a/source/java/org/alfresco/repo/security/person/PersonDao.java b/source/java/org/alfresco/repo/security/person/PersonDao.java index c29b76bcc9..53b9ae6828 100644 --- a/source/java/org/alfresco/repo/security/person/PersonDao.java +++ b/source/java/org/alfresco/repo/security/person/PersonDao.java @@ -31,7 +31,7 @@ import org.alfresco.service.cmr.repository.NodeRef; public interface PersonDao { - public List getPersonOrNull(String searchUserName, boolean userNamesAreCaseSensitive); + public List getPersonOrNull(final String searchUserName, UserNameMatcher matcher); public Set getAllPeople(); } diff --git a/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java b/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java index ceb4795dbc..445aeb559d 100644 --- a/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java @@ -51,8 +51,9 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport; public class PersonDaoImpl extends HibernateDaoSupport implements PersonDao { - private static final String QUERY_PERSON_GET_PERSON = "person.getPerson"; - private static final String QUERY_PERSON_GET_ALL_PEOPLE = "person.getAllPeople"; + private static final String QUERY_PERSON_GET_PERSON = "person.getPerson"; + + private static final String QUERY_PERSON_GET_ALL_PEOPLE = "person.getAllPeople"; private QNameDAO qnameDAO; @@ -61,33 +62,33 @@ public class PersonDaoImpl extends HibernateDaoSupport implements PersonDao private LocaleDAO localeDAO; private DictionaryService dictionaryService; - + private StoreRef storeRef; - + private TenantService tenantService; - + public void setStoreUrl(String storeUrl) { this.storeRef = new StoreRef(storeUrl); } - + public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; } @SuppressWarnings("unchecked") - public List getPersonOrNull(final String searchUserName, boolean userNamesAreCaseSensitive) + public List getPersonOrNull(final String searchUserName, UserNameMatcher matcher) { final StoreRef personStoreRef = tenantService.getName(storeRef); - + List answer = new ArrayList(); HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) { - SQLQuery query = (SQLQuery) session.getNamedQuery(QUERY_PERSON_GET_PERSON); + SQLQuery query = (SQLQuery) session.getNamedQuery(QUERY_PERSON_GET_PERSON); query.setParameter("qnameId", qNameId); query.setParameter("userName1", searchUserName); query.setParameter("userName2", searchUserName); @@ -111,19 +112,9 @@ public class PersonDaoImpl extends HibernateDaoSupport implements PersonDao Serializable value = converted.get(ContentModel.PROP_USERNAME); String realUserName = DefaultTypeConverter.INSTANCE.convert(String.class, value); - if (userNamesAreCaseSensitive) + if (matcher.matches(searchUserName, realUserName)) { - if (realUserName.equals(searchUserName)) - { - answer.add(nodeRef); - } - } - else - { - if (realUserName.equalsIgnoreCase(searchUserName)) - { - answer.add(nodeRef); - } + answer.add(nodeRef); } } @@ -133,21 +124,21 @@ public class PersonDaoImpl extends HibernateDaoSupport implements PersonDao public void init() { - qNameId = qnameDAO.getOrCreateQName(ContentModel.PROP_USERNAME).getFirst(); + qNameId = qnameDAO.getOrCreateQName(ContentModel.PROP_USERNAME).getFirst(); } @SuppressWarnings("unchecked") public Set getAllPeople() { final StoreRef personStoreRef = tenantService.getName(storeRef); - + Set answer = new HashSet(); HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) { - SQLQuery query = (SQLQuery) session.getNamedQuery(QUERY_PERSON_GET_ALL_PEOPLE); + SQLQuery query = (SQLQuery) session.getNamedQuery(QUERY_PERSON_GET_ALL_PEOPLE); query.setParameter("qnameId", qNameId); query.setParameter("False", Boolean.FALSE); query.setParameter("storeProtocol", personStoreRef.getProtocol()); diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index d72d99cab5..8772e3942b 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -67,6 +67,7 @@ import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.GUID; +import org.alfresco.util.Pair; import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -92,27 +93,27 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per private TransactionService transactionService; private NodeService nodeService; - + private TenantService tenantService; private SearchService searchService; private AuthorityService authorityService; - + private DictionaryService dictionaryService; private PermissionServiceSPI permissionServiceSPI; private NamespacePrefixResolver namespacePrefixResolver; + + private HomeFolderManager homeFolderManager; private PolicyComponent policyComponent; private boolean createMissingPeople; private static Set mutableProperties; - - private boolean userNamesAreCaseSensitive = false; - + private String defaultHomeFolderProvider; private boolean processDuplicates = true; @@ -128,6 +129,8 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per /** a transactionally-safe cache to be injected */ private SimpleCache personCache; + private UserNameMatcher userNameMatcher; + static { Set props = new HashSet(); @@ -168,20 +171,21 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per PropertyCheck.mandatory(this, "personCache", personCache); PropertyCheck.mandatory(this, "personDao", personDao); - this.policyComponent - .bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); + // Avoid clash with home folder registration + //this.policyComponent + // .bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), ContentModel.TYPE_PERSON, new JavaBehaviour(this, "beforeDeleteNode")); } - public boolean getUserNamesAreCaseSensitive() + public UserNameMatcher getUserNameMatcher() { - return userNamesAreCaseSensitive; + return userNameMatcher; } - public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) + public void setUserNameMatcher(UserNameMatcher userNameMatcher) { - this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; + this.userNameMatcher = userNameMatcher; } void setDefaultHomeFolderProvider(String defaultHomeFolderProvider) @@ -208,6 +212,11 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { this.processDuplicates = processDuplicates; } + + public void setHomeFolderManager(HomeFolderManager homeFolderManager) + { + this.homeFolderManager = homeFolderManager; + } public void setPersonDao(PersonDao personDao) { @@ -287,7 +296,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per NodeRef returnRef = this.personCache.get(searchUserName); if (returnRef == null) { - List refs = personDao.getPersonOrNull(searchUserName, userNamesAreCaseSensitive); + List refs = personDao.getPersonOrNull(searchUserName, userNameMatcher); if (refs.size() > 1) { returnRef = handleDuplicates(refs, searchUserName); @@ -300,6 +309,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per // add to cache this.personCache.put(searchUserName, returnRef); } + makeHomeFolderIfRequired(returnRef); return returnRef; } @@ -316,14 +326,14 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per } else { - if (userNamesAreCaseSensitive) + String userNameSensitivity = " (user name is case-" + (userNameMatcher.getUserNamesAreCaseSensitive() ? "sensitive" : "insensitive") + ")"; + String domainNameSensitivity = ""; + if (! userNameMatcher.getDomainSeparator().equals("")) { - 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)"); + domainNameSensitivity = " (domain name is case-" + (userNameMatcher.getDomainNamesAreCaseSensitive() ? "sensitive" : "insensitive") + ")"; } + + throw new AlfrescoRuntimeException("Found more than one user for " + searchUserName + userNameSensitivity + domainNameSensitivity); } } @@ -510,7 +520,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per } Map update = nodeService.getProperties(personNode); update.putAll(properties); - + nodeService.setProperties(personNode, update); } @@ -522,9 +532,31 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per private NodeRef createMissingPerson(String userName) { HashMap properties = getDefaultProperties(userName); - return createPerson(properties); + NodeRef person = createPerson(properties); + makeHomeFolderIfRequired(person); + return person; } + private void makeHomeFolderIfRequired(NodeRef person) + { + if (person != null) + { + NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER)); + if (homeFolder == null) + { + final ChildAssociationRef ref = nodeService.getPrimaryParent(person); + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + homeFolderManager.onCreateNode(ref); + return null; + } + }, transactionService.isReadOnly(), false); + } + } + } + private HashMap getDefaultProperties(String userName) { HashMap properties = new HashMap(); @@ -534,48 +566,46 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per properties.put(ContentModel.PROP_EMAIL, ""); properties.put(ContentModel.PROP_ORGID, ""); properties.put(ContentModel.PROP_HOME_FOLDER_PROVIDER, defaultHomeFolderProvider); - + properties.put(ContentModel.PROP_SIZE_CURRENT, 0L); properties.put(ContentModel.PROP_SIZE_QUOTA, -1L); // no quota - + return properties; } public NodeRef createPerson(Map properties) { String userName = DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_USERNAME)); - + tenantService.checkDomainUser(userName); - + properties.put(ContentModel.PROP_USERNAME, userName); properties.put(ContentModel.PROP_SIZE_CURRENT, 0L); - - return nodeService.createNode( - getPeopleContainer(), - ContentModel.ASSOC_CHILDREN, - QName.createQName("cm", userName, namespacePrefixResolver), - ContentModel.TYPE_PERSON, properties).getChildRef(); + + return nodeService.createNode(getPeopleContainer(), ContentModel.ASSOC_CHILDREN, QName.createQName("cm", userName, namespacePrefixResolver), ContentModel.TYPE_PERSON, + properties).getChildRef(); } public NodeRef getPeopleContainer() { NodeRef rootNodeRef = nodeService.getRootNode(tenantService.getName(storeRef)); - List children = nodeService.getChildAssocs(rootNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(SYSTEM_FOLDER_SHORT_QNAME, namespacePrefixResolver)); - + List children = nodeService.getChildAssocs(rootNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(SYSTEM_FOLDER_SHORT_QNAME, + namespacePrefixResolver)); + if (children.size() == 0) { throw new AlfrescoRuntimeException("Required people system path not found: " + SYSTEM_FOLDER_SHORT_QNAME); } - + NodeRef systemNodeRef = children.get(0).getChildRef(); - + children = nodeService.getChildAssocs(systemNodeRef, RegexQNamePattern.MATCH_ALL, QName.createQName(PEOPLE_FOLDER_SHORT_QNAME, namespacePrefixResolver)); - - if (children.size() == 0) + + if (children.size() == 0) { throw new AlfrescoRuntimeException("Required people system path not found: " + PEOPLE_FOLDER_SHORT_QNAME); } - + NodeRef peopleNodeRef = children.get(0).getChildRef(); return peopleNodeRef; } @@ -612,16 +642,15 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per // and throw exception if it isn't if (this.dictionaryService.getProperty(ContentModel.TYPE_PERSON, propertyKey) == null) { - throw new AlfrescoRuntimeException("Property '" + propertyKey + "' is not defined " - + "for content model type cm:person"); + throw new AlfrescoRuntimeException("Property '" + propertyKey + "' is not defined " + "for content model type cm:person"); } - + LinkedHashSet people = new LinkedHashSet(); - + // // Search for people using the given property // - + SearchParameters sp = new SearchParameters(); sp.setLanguage(SearchService.LANGUAGE_LUCENE); sp.setQuery("@cm\\:" + propertyKey.getLocalName() + ":\"" + propertyValue + "\""); @@ -650,10 +679,10 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per rs.close(); } } - + return people; } - + // Policies /* @@ -695,7 +724,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { this.authorityService = authorityService; } - + public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; @@ -715,17 +744,17 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { this.nodeService = nodeService; } - + public void setTenantService(TenantService tenantService) { this.tenantService = tenantService; - } + } public void setSearchService(SearchService searchService) { this.searchService = searchService; } - + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; @@ -789,4 +818,11 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per } } + + public boolean getUserNamesAreCaseSensitive() + { + return userNameMatcher.getUserNamesAreCaseSensitive(); + } + + } diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceLoader.java b/source/java/org/alfresco/repo/security/person/PersonServiceLoader.java index f8adc41aae..318c750923 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceLoader.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceLoader.java @@ -28,6 +28,10 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; @@ -106,6 +110,51 @@ public class PersonServiceLoader PersonServiceLoader loader = new PersonServiceLoader(ctx, batchSize, batchCount); loader.run(user, pwd, threads); + + // check the lazy creation + + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + + final ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + final AuthenticationService authenticationService = serviceRegistry.getAuthenticationService(); + final PersonService personService = serviceRegistry.getPersonService(); + final TransactionService transactionService = serviceRegistry.getTransactionService(); + final NodeService nodeService = serviceRegistry.getNodeService(); + + String firstName = "" + System.currentTimeMillis(); + String lastName = String.format("%05d", -1); + String username = GUID.generate(); + String emailAddress = String.format("%s.%s@xyz.com", firstName, lastName); + PropertyMap properties = new PropertyMap(7); + properties.put(ContentModel.PROP_USERNAME, username); + properties.put(ContentModel.PROP_FIRSTNAME, firstName); + properties.put(ContentModel.PROP_LASTNAME, lastName); + properties.put(ContentModel.PROP_EMAIL, emailAddress); + NodeRef madePerson = personService.createPerson(properties); + + NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(madePerson, ContentModel.PROP_HOMEFOLDER)); + if(homeFolder != null) + { + throw new IllegalStateException("Home folder created eagerly"); + } + + NodeRef person = personService.getPerson(username); + homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER)); + if(homeFolder == null) + { + throw new IllegalStateException("Home folder not created lazily"); + } + + + NodeRef autoPerson = personService.getPerson(GUID.generate()); + NodeRef autoHomeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(autoPerson, ContentModel.PROP_HOMEFOLDER)); + if(autoHomeFolder == null) + { + throw new IllegalStateException("Home folder not created lazily for auto created users"); + } + + + // All done ApplicationContextHelper.closeApplicationContext(); System.exit(0); diff --git a/source/java/org/alfresco/repo/security/person/UserNameMatcher.java b/source/java/org/alfresco/repo/security/person/UserNameMatcher.java new file mode 100644 index 0000000000..63652c68fa --- /dev/null +++ b/source/java/org/alfresco/repo/security/person/UserNameMatcher.java @@ -0,0 +1,48 @@ +/* + * 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.person; + +/** + * Check if userNames match + * @author andyh + * + */ +public interface UserNameMatcher +{ + /** + * Do the two user names match? + * + * @param userName1 + * @param userName2 + * @return + */ + public boolean matches(String userName1, String userName2); + + public boolean getUserNamesAreCaseSensitive(); + + public boolean getDomainNamesAreCaseSensitive(); + + public String getDomainSeparator(); +} diff --git a/source/java/org/alfresco/repo/security/person/UserNameMatcherImpl.java b/source/java/org/alfresco/repo/security/person/UserNameMatcherImpl.java new file mode 100644 index 0000000000..aafa07b294 --- /dev/null +++ b/source/java/org/alfresco/repo/security/person/UserNameMatcherImpl.java @@ -0,0 +1,74 @@ +package org.alfresco.repo.security.person; + +import org.alfresco.util.Pair; + +public class UserNameMatcherImpl implements UserNameMatcher +{ + private boolean userNamesAreCaseSensitive = false; + + private boolean domainNamesAreCaseSensitive = false; + + private String domainSeparator = ""; + + public boolean getUserNamesAreCaseSensitive() + { + return userNamesAreCaseSensitive; + } + + public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) + { + this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; + } + + public boolean getDomainNamesAreCaseSensitive() + { + return domainNamesAreCaseSensitive; + } + + public void setDomainNamesAreCaseSensitive(boolean domainNamesAreCaseSensitive) + { + this.domainNamesAreCaseSensitive = domainNamesAreCaseSensitive; + } + + public String getDomainSeparator() + { + return domainSeparator; + } + + public void setDomainSeparator(String domainSeparator) + { + this.domainSeparator = domainSeparator; + } + + public boolean matches(String realUserName, String searchUserName) + { + // note: domain string may be empty + Pair real = splitByDomain(realUserName, domainSeparator); + Pair search = splitByDomain(searchUserName, domainSeparator); + + return (((userNamesAreCaseSensitive && (real.getFirst().equals(search.getFirst()))) || (!userNamesAreCaseSensitive && (real.getFirst().equalsIgnoreCase(search + .getFirst())))) && + + ((domainNamesAreCaseSensitive && (real.getSecond().equals(search.getSecond()))) || (!domainNamesAreCaseSensitive && (real.getSecond().equalsIgnoreCase(search + .getSecond()))))); + } + + // Trailing domain only + private Pair splitByDomain(String name, String domainSeparator) + { + int idx = name.lastIndexOf(domainSeparator); + if (idx != -1) + { + if ((idx + 1) > name.length()) + { + return new Pair(name.substring(0, idx), ""); + } + else + { + return new Pair(name.substring(0, idx), name.substring(idx + 1)); + } + } + + return new Pair(name, ""); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java index 40dc766c52..a08a179671 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java @@ -82,7 +82,7 @@ public class UserUsageTrackingComponentTest extends TestCase nodeService = (NodeService)applicationContext.getBean("NodeService"); authenticationService = (AuthenticationService)applicationContext.getBean("authenticationService"); transactionService = (TransactionService)applicationContext.getBean("transactionComponent"); - personService = (PersonService)applicationContext.getBean("personService"); + personService = (PersonService)applicationContext.getBean("PersonService"); contentService = (ContentService)applicationContext.getBean("ContentService"); contentUsageService = (ContentUsageService)applicationContext.getBean("ContentUsageService");