Thursday, February 8, 2018
Best Practices for Managing User Import into Alfresco from Active Directory
Alfresco has a built-in user authentication and directory service but it can also make use of Active Directory to not only handle authentication but also handle user and group memberships. In order for this to work though, you will need to add LDAP-AD as an authentication instance in the authentication.chain setting and enable the synchronization subsystem. There are a number of settings that need to be modified in order to do this but before doing so, one needs to think out how they are going to use Active Directory to manage this.
Often when I help an Alfresco administrator set up synchronization the admin has settings that are very generic like these:
ldap.synchronization.groupSearchBase=dc\=someco,dc\=com
ldap.synchronization.userSearchBase=dc\=someco,dc\=com
ldap.synchronization.groupQuery=objectclass\=group
ldap.synchronization.personQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512))
ldap.synchronization.personDifferentialQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512)(!(modifyTimestamp<\={0})))
ldap.synchronization.groupDifferentialQuery=(&(objectclass\=group)(!modifyTimestamp<\={0}))
Technically speaking, these queries are valid but the end result is that setting it like this will import users and groups from a very plain perspective and could make user management more difficult than it has to be. What these queries and settings will do is this:
The search for groups and users will take place in the someco.com domain which is good.
ldap.synchronization.groupSearchBase=dc\=someco,dc\=com
ldap.synchronization.userSearchBase=dc\=someco,dc\=com
Based on these two queries however, Alfresco will import ALL users and ALL groups inside of someco.com.
ldap.synchronization.groupQuery=objectclass\=group
ldap.synchronization.personQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512))
It's very likely that this is not what you want.
Of course, a differential sync will import the same set of users (but only those that have changed since the last import):
ldap.synchronization.personDifferentialQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512)(!(modifyTimestamp<\={0})))
ldap.synchronization.groupDifferentialQuery=(&(objectclass\=group)(!modifyTimestamp<\={0}))
Now, you may have a use-case that requires all of your users in Active Directory. It's possible that you have a small company but be aware that this method does not encourage scalability and allow for filtering out those users in your company who will not need to use Alfresco at all.
Here is what I recommend instead. The best practice is to create an Alfresco organizational unit and then create a few Alfresco groups in this OU. For example:
Create an Alfresco organizational unit with a distinguished name:
OU=Alfresco,DC=someco,DC=com
Then you can create any Alfresco specific groups within the Alfresco OU:
CN=AlfrescoAdmins,OU=Alfresco,DC=someco,DC=com
CN=AlfrescoUsers,OU=Alfresco,DC=someco,DC=com
Once these groups are set up in Active Directory, you can assign your users to them. For my testing purposes I created 3 AD users (aduser1, aduser2, aduser3) and then made aduser1 a member of AlfrescoAdmins and then all three, members of the AlfrescoUsers group. Now we can change our queries so that only these users in these two groups are imported into Alfresco:
ldap.synchronization.userSearchBase=ou\=alfresco,dc\=someco,dc\=com
ldap.synchronization.groupSearchBase=ou\=alfresco,dc\=someco,dc\=com
Also, because we have now set our groupSearchBase to only look in the Alfresco OU, our groupQuery will be very simple:
ldap.synchronization.groupQuery=objectclass\=group
If we use the personQuery setting below, it will only import users who are members of AlfrescoAdmins and AlfrescoUsers and not every single user in Active Directory:
ldap.synchronization.personQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512)(|(memberOf=cn\=AlfrescoAdmins,ou=alfresco,dc=someco,dc=com)(memberOf=cn\=AlfrescoUsers,ou=alfresco,dc=someco,dc=com)))
Next of course, for differential imports, we’ll use the same queries except add the modifiedTimestamp directive at the end to ensure we only pick up changes to our users since our last import:
ldap.synchronization.personDifferentialQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512)(|(memberOf=cn\=AlfrescoAdmins,ou=alfresco,dc=someco,dc=com)(memberOf=cn\=AlfrescoUsers,ou=alfresco,dc=someco,dc=com))(!(modifyTimestamp<\={0})))
ldap.synchronization.groupDifferentialQuery=(&(objectclass\=group)(!modifyTimestamp<\={0}))
To tie it all together, at a glance, here are the settings I use for testing Active Directory:
### AD authentication only ###
authentication.chain=alfrescoNtlm1:alfrescoNtlm,ldap-ad1:ldap-ad
ldap.authentication.active=true
ldap.authentication.allowGuestLogin=true
ldap.authentication.userNameFormat=%s@someco.com
ldap.authentication.java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
ldap.authentication.java.naming.provider.url=ldap://someco.com:389 # This points to your Active Directory server - IP Address is ok to use here as well!
ldap.authentication.java.naming.security.authentication=simple
ldap.authentication.escapeCommasInBind=false
ldap.authentication.escapeCommasInUid=false
ldap.authentication.defaultAdministratorUserNames=Administrator
ldap.synchronization.active=true
ldap.synchronization.java.naming.security.principal=administrator@someco.com
ldap.synchronization.java.naming.security.credentials=Alfr3sc0
ldap.synchronization.queryBatchSize=1000
ldap.synchronization.attributeBatchSize=1000
synchronization.synchronizeChangesOnly=false
synchronization.allowDeletions=true
synchronization.syncWhenMissingPeopleLogIn=true
ldap.synchronization.groupQuery=objectclass\=group
ldap.synchronization.groupDifferentialQuery=(&(objectclass\=group)(!(modifyTimestamp<\={0})))
ldap.synchronization.personQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512)(|(memberOf=cn\=AlfrescoAdmins,ou=alfresco,dc=someco,dc=com)(memberOf=cn\=AlfrescoUsers,ou=alfresco,dc=someco,dc=com)))
ldap.synchronization.personDifferentialQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512)(|(memberOf=cn\=AlfrescoAdmins,ou=alfresco,dc=someco,dc=com)(memberOf=cn\=AlfrescoUsers,ou=alfresco,dc=someco,dc=com))(!(modifyTimestamp<\={0})))
ldap.synchronization.groupSearchBase=ou\=alfresco,dc\=someco,dc\=com
ldap.synchronization.userSearchBase=dc\=someco,dc\=com
ldap.synchronization.modifyTimestampAttributeName=modifyTimestamp
ldap.synchronization.timestampFormat=yyyyMMddHHmmss'.0Z'
ldap.synchronization.userIdAttributeName=sAMAccountName
ldap.synchronization.userFirstNameAttributeName=givenName
ldap.synchronization.userLastNameAttributeName=sn
ldap.synchronization.userEmailAttributeName=mail
ldap.synchronization.userOrganizationalIdAttributeName=company
ldap.synchronization.defaultHomeFolderProvider=largeHomeFolderProvider
ldap.synchronization.groupIdAttributeName=cn
ldap.synchronization.groupDisplayNameAttributeName=displayName
ldap.synchronization.groupType=group
ldap.synchronization.personType=user
ldap.synchronization.groupMemberAttributeName=member
ldap.synchronization.enableProgressEstimation=true
Now, when you run the user synchronization, all groups will be imported and all users will show up in their expected groups. Most importantly, you will now only have those users imported from Active Directory who should have access to your Alfresco environment. This will make user management in Alfresco much simpler.
Subscribe to:
Posts (Atom)