diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index ffb1782e5e..1a305de31e 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -198,9 +198,6 @@ - - ${authentication.syncWhenMissingPeopleLogIn} - @@ -223,24 +220,17 @@ - - - - - - - - - - - - - - - - userRegistry - - + + + + + + + + org.alfresco.repo.security.sync.UserRegistrySynchronizer + + + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index efc9ddd4c0..d86f406568 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -328,7 +328,6 @@ V2.1-A.fixes.to.schema=0 # The default authentication chain authentication.chain=alfrescoNtlm1:alfrescoNtlm -authentication.syncWhenMissingPeopleLogIn=true # Default NFS user mappings nfs.user.mappings=admin diff --git a/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml b/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml index 652502fa81..19f5670947 100644 --- a/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml +++ b/config/alfresco/subsystems/Synchronization/default/default-synchronization-context.xml @@ -2,7 +2,7 @@ - + @@ -30,4 +30,30 @@ + + + + ${synchronization.syncWhenMissingPeopleLogIn} + + + ${synchronization.autoCreatePeopleOnLogin} + + + + + + + + + + + + + + + userRegistry + + + + \ No newline at end of file diff --git a/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties b/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties index b875ff5706..56ec2d23b3 100644 --- a/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties +++ b/config/alfresco/subsystems/Synchronization/default/default-synchronization.properties @@ -1,13 +1,19 @@ # -# This properties file is used to configure scheduled user registry syncronisation (e.g. LDAP) +# This properties file is used to configure user registry syncronisation (e.g. LDAP) # # Should the scheduled sync job only query users and groups changed since the # last sync? Note that when true, the sync job will not be able to detect which # users or groups have been removed from the directory (but obviously group # membership changes would still be reflected). When false, a more regular -# differential sync on login can still be enabled on the person service. +# differential sync on login can still be enabled. synchronization.synchronizeChangesOnly=false # The cron expression defining when imports should take place synchronization.import.cron=0 0 0 * * ? + +# Should we trigger a differential sync when missing people log in? +synchronization.syncWhenMissingPeopleLogIn=true + +# Should we auto create a missing person on log in? +synchronization.autoCreatePeopleOnLogin=true \ No newline at end of file diff --git a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java index c880f59ad7..c418c54dcf 100644 --- a/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java +++ b/source/java/org/alfresco/repo/security/authentication/AbstractAuthenticationComponent.java @@ -44,7 +44,6 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.transaction.TransactionService; @@ -63,10 +62,6 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC private Set defaultAdministratorUserNames = Collections.emptySet(); - private boolean syncWhenMissingPeopleLogIn = true; - - private boolean autoCreatePeopleOnLogin = true; - private AuthenticationContext authenticationContext; private PersonService personService; @@ -136,21 +131,6 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC { return personService; } - - public boolean isAutoCreatePeopleOnLogin() - { - return autoCreatePeopleOnLogin; - } - - public void setAutoCreatePeopleOnLogin(boolean autoCreatePeopleOnLogin) - { - this.autoCreatePeopleOnLogin = autoCreatePeopleOnLogin; - } - - public void setSyncWhenMissingPeopleLogIn(boolean syncWhenMissingPeopleLogIn) - { - this.syncWhenMissingPeopleLogIn = syncWhenMissingPeopleLogIn; - } public void authenticate(String userName, char[] password) throws AuthenticationException { @@ -448,30 +428,7 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC { public String doWork() throws Exception { - 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) + if (personService.personExists(userName)|| userRegistrySynchronizer.createMissingPerson(userName)) { NodeRef userNode = personService.getPerson(userName); if (userNode != null) @@ -480,8 +437,8 @@ public abstract class AbstractAuthenticationComponent implements AuthenticationC // checks return (String) nodeService.getProperty(userNode, ContentModel.PROP_USERNAME); } - } - return userName; + } + throw new AuthenticationException("Person does not exist in Alfresco"); } }, getSystemUserName(getUserDomain(userName))); diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java index d3b98b2cc4..f91e6ab78c 100644 --- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java +++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java @@ -39,6 +39,7 @@ import org.alfresco.repo.attributes.LongAttributeValue; import org.alfresco.repo.attributes.MapAttributeValue; import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.management.subsystems.ChildApplicationContextManager; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; @@ -66,14 +67,16 @@ import org.springframework.context.ApplicationContext; * 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. + * detected). This 'differential' mode is much faster, and by default is triggered by + * {@link #createMissingPerson(String)} 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); @@ -101,6 +104,12 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize /** The attribute service. */ private AttributeService attributeService; + /** Should we trigger a sync when missing people log in? */ + private boolean syncWhenMissingPeopleLogIn = true; + + /** Should we auto create a missing person on log in? */ + private boolean autoCreatePeopleOnLogin = true; + /** * Sets the application context manager. * @@ -156,6 +165,28 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize this.attributeService = attributeService; } + /** + * Controls whether we auto create a missing person on log in + * + * @param autoCreatePeopleOnLogin + * true if we should auto create a missing person on log in + */ + public void setAutoCreatePeopleOnLogin(boolean autoCreatePeopleOnLogin) + { + this.autoCreatePeopleOnLogin = autoCreatePeopleOnLogin; + } + + /** + * Controls whether we trigger a sync when missing people log in + * + * @param syncWhenMissingPeopleLogIn + * if we should trigger a sync when missing people log in + */ + public void setSyncWhenMissingPeopleLogIn(boolean syncWhenMissingPeopleLogIn) + { + this.syncWhenMissingPeopleLogIn = syncWhenMissingPeopleLogIn; + } + /* * (non-Javadoc) * @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#synchronize(boolean) @@ -188,7 +219,8 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize int groupsProcessed = syncGroupsWithPlugin(zoneId, plugin, force, visitedZoneIds); ChainingUserRegistrySynchronizer.logger .info("Finished synchronizing users and groups with user registry '" + zoneId + "'"); - logger.info(personsProcessed + " user(s) and " + groupsProcessed + " group(s) processed"); + ChainingUserRegistrySynchronizer.logger.info(personsProcessed + " user(s) and " + groupsProcessed + + " group(s) processed"); } } catch (NoSuchBeanDefinitionException e) @@ -199,6 +231,36 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize } } + /* + * (non-Javadoc) + * @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#ensureExists(java.lang.String) + */ + public boolean createMissingPerson(String userName) + { + // synchronize or auto-create the missing person if we are allowed + if (userName != null && !userName.equals(AuthenticationUtil.getSystemUserName())) + { + if (this.syncWhenMissingPeopleLogIn) + { + synchronize(false); + if (this.personService.personExists(userName)) + { + return true; + } + } + if (this.autoCreatePeopleOnLogin && this.personService.createMissingPeople()) + { + AuthorityType authorityType = AuthorityType.getAuthorityType(userName); + if (authorityType == AuthorityType.USER) + { + this.personService.getPerson(userName); + return true; + } + } + } + return false; + } + /** * Synchronizes local users (persons) with a {@link UserRegistry} for a particular zone. * @@ -300,7 +362,7 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize return processedCount; } - + /** * Synchronizes local groups (authorities) with a {@link UserRegistry} for a particular zone. * @@ -318,7 +380,7 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize * 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 visitedZoneIds) + private int syncGroupsWithPlugin(String zoneId, UserRegistry userRegistry, boolean force, Set visitedZoneIds) { int processedCount = 0; long lastModifiedMillis = force ? -1L : getMostRecentUpdateTime( @@ -334,7 +396,7 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize + DateFormat.getDateTimeInstance().format(lastModified) + " from user registry '" + zoneId + "'"); } - Iterator groups = plugin.getGroups(lastModified); + Iterator groups = userRegistry.getGroups(lastModified); Map> groupAssocsToCreate = new TreeMap>(); Set groupsToDelete = this.authorityService.getAllAuthoritiesInZone(zoneId, AuthorityType.GROUP); while (groups.hasNext()) @@ -483,7 +545,16 @@ public class ChainingUserRegistrySynchronizer implements UserRegistrySynchronize } this.attributeService.setAttribute(path, zoneId, new LongAttributeValue(lastModifiedMillis)); } - + + /** + * Gets the default set of zones to set on a person or group belonging to the user registry with the given zone ID. + * We add the default zone as well as the zone corresponding to the user registry so that the users and groups are + * visible in the UI. + * + * @param zoneId + * the zone id + * @return the zone set + */ private Set getZones(String zoneId) { HashSet zones = new HashSet(2, 1.0f); diff --git a/source/java/org/alfresco/repo/security/sync/UserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/UserRegistrySynchronizer.java index 02fe5a0796..30ab728990 100644 --- a/source/java/org/alfresco/repo/security/sync/UserRegistrySynchronizer.java +++ b/source/java/org/alfresco/repo/security/sync/UserRegistrySynchronizer.java @@ -31,7 +31,18 @@ package org.alfresco.repo.security.sync; * @author dward */ public interface UserRegistrySynchronizer -{ +{ + /** + * Creates a person object for a successfully authenticated user who does not yet have a person object, if allowed + * to by configuration. Depending on configuration, may trigger a partial synchronize and/or create a new person + * with default settings. + * + * @param username + * the user name + * @return true, if a person is created + */ + public boolean createMissingPerson(String username); + /** * 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