diff --git a/config/alfresco/subsystems/Authentication/common-ldap-context.xml b/config/alfresco/subsystems/Authentication/common-ldap-context.xml
new file mode 100644
index 0000000000..0075fd01b0
--- /dev/null
+++ b/config/alfresco/subsystems/Authentication/common-ldap-context.xml
@@ -0,0 +1,354 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${ldap.authentication.active}
+
+
+
+
+
+
+ ${ldap.authentication.userNameFormat}
+
+
+
+
+
+
+
+
+
+
+
+ ${ldap.authentication.escapeCommasInBind}
+
+
+ ${ldap.authentication.escapeCommasInUid}
+
+
+ ${ldap.authentication.allowGuestLogin}
+
+
+ ${ldap.authentication.defaultAdministratorUserNames}
+
+
+
+
+
+
+ org.alfresco.repo.security.authentication.AuthenticationComponent
+
+
+
+
+
+
+
+
+
+ ${server.transaction.mode.default}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${ldap.synchronization.active}
+
+
+
+
+ ${ldap.synchronization.queryBatchSize}
+
+
+
+
+ ${ldap.synchronization.groupQuery}
+
+
+
+
+ ${ldap.synchronization.groupDifferentialQuery}
+
+
+
+
+ ${ldap.synchronization.personQuery}
+
+
+
+
+ ${ldap.synchronization.personDifferentialQuery}
+
+
+
+
+ ${ldap.synchronization.groupSearchBase}
+
+
+
+
+ ${ldap.synchronization.userSearchBase}
+
+
+
+
+ ${ldap.synchronization.userIdAttributeName}
+
+
+
+
+ ${ldap.synchronization.modifyTimestampAttributeName}
+
+
+
+
+ ${ldap.synchronization.timestampFormat}
+
+
+
+
+ ${ldap.synchronization.groupIdAttributeName}
+
+
+
+
+ ${ldap.synchronization.groupType}
+
+
+
+
+ ${ldap.synchronization.personType}
+
+
+
+
+ ${ldap.synchronization.groupMemberAttributeName}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication-context.xml b/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication-context.xml
new file mode 100644
index 0000000000..05442f1f66
--- /dev/null
+++ b/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication-context.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties b/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties
new file mode 100644
index 0000000000..c6b59b0201
--- /dev/null
+++ b/config/alfresco/subsystems/Authentication/ldap-ad/ldap-ad-authentication.properties
@@ -0,0 +1,109 @@
+# This flag enables use of this LDAP subsystem for authentication. It may be
+# that this subsytem should only be used for synchronization, in which case
+# this flag should be set to false.
+ldap.authentication.active=true
+
+#
+# This properties file brings together the common options for LDAP authentication rather than editing the bean definitions
+#
+ldap.authentication.allowGuestLogin=true
+# How to map the user id entered by the user to taht passed through to LDAP
+# - simple
+# - this must be a DN and would be something like
+# uid=%s,ou=People,dc=company,dc=com
+# - digest
+# - usually pass through what is entered
+# %s
+ldap.authentication.userNameFormat=%s
+
+# The LDAP context factory to use
+ldap.authentication.java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
+
+# The URL to connect to the LDAP server
+ldap.authentication.java.naming.provider.url=ldap://domaincontroller.company.com:389
+
+# The authentication mechanism to use
+ldap.authentication.java.naming.security.authentication=DIGEST-MD5
+
+# Escape commas entered by the user at bind time
+# Useful when using simple authentication and the CN is part of the DN and contains commas
+ldap.authentication.escapeCommasInBind=false
+
+# Escape commas entered by the user when setting the authenticated user
+# Useful when using simple authentication and the CN is part of the DN and contains commas, and the escaped \, is
+# pulled in as part of an LDAP sync
+# If this option is set to true it will break the default home folder provider as space names can not contain \
+ldap.authentication.escapeCommasInUid=false
+
+# Comma separated list of user names who should be considered administrators by default
+ldap.authentication.defaultAdministratorUserNames=Administrator
+
+# This flag enables use of this LDAP subsystem for user and group
+# synchronization. It may be that this subsytem should only be used for
+# authentication, in which case this flag should be set to false.
+ldap.synchronization.active=true
+
+# The default principal to use (only used for LDAP sync)
+ldap.synchronization.java.naming.security.principal=alfresco
+
+# The password for the default principal (only used for LDAP sync)
+ldap.synchronization.java.naming.security.credentials=secret
+
+# If positive, this property indicates that RFC 2696 paged results should be
+# used to split query results into batches of the specified size. This
+# overcomes any size limits imposed by the LDAP server.
+ldap.synchronization.queryBatchSize=1000
+
+# The query to select all objects that represent the groups to import.
+ldap.synchronization.groupQuery=(objectclass\=group)
+
+# The query to select objects that represent the groups to import that have changed since a certain time.
+ldap.synchronization.groupDifferentialQuery=(&(objectclass\=group)(!(modifyTimestamp<\={0})))
+
+# The query to select all objects that represent the users to import.
+ldap.synchronization.personQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512))
+
+# The query to select objects that represent the users to import that have changed since a certain time.
+ldap.synchronization.personDifferentialQuery=(&(objectclass\=user)(userAccountControl\:1.2.840.113556.1.4.803\:\=512)(!(modifyTimestamp<\={0})))
+
+# The group search base restricts the LDAP group query to a sub section of tree on the LDAP server.
+ldap.synchronization.groupSearchBase=ou\=Security Groups,ou\=Alfresco,dc=domain
+
+# The user search base restricts the LDAP user query to a sub section of tree on the LDAP server.
+ldap.synchronization.userSearchBase=ou\=User Accounts,ou=\Alfresco,dc=domain
+
+# The name of the operational attribute recording the last update time for a group or user.
+ldap.synchronization.modifyTimestampAttributeName=modifyTimestamp
+
+# The timestamp format. Unfortunately, this varies between directory servers.
+ldap.synchronization.timestampFormat=yyyyMMddHHmmss'.0Z'
+
+# The attribute name on people objects found in LDAP to use as the uid in Alfresco
+ldap.synchronization.userIdAttributeName=sAMAccountName
+
+# The attribute on person objects in LDAP to map to the first name property in Alfresco
+ldap.synchronization.userFirstNameAttributeName=givenName
+
+# The attribute on person objects in LDAP to map to the last name property in Alfresco
+ldap.synchronization.userLastNameAttributeName=sn
+
+# The attribute on person objects in LDAP to map to the email property in Alfresco
+ldap.synchronization.userEmailAttributeName=mail
+
+# The attribute on person objects in LDAP to map to the organizational id property in Alfresco
+ldap.synchronization.userOrganizationalIdAttributeName=company
+
+# The default home folder provider to use for people created via LDAP import
+ldap.synchronization.defaultHomeFolderProvider=personalHomeFolderProvider
+
+# The attribute on LDAP group objects to map to the gid property in Alfrecso
+ldap.synchronization.groupIdAttributeName=cn
+
+# The group type in LDAP
+ldap.synchronization.groupType=group
+
+# The person type in LDAP
+ldap.synchronization.personType=user
+
+# The attribute in LDAP on group objects that defines the DN for its members
+ldap.synchronization.groupMemberAttributeName=member
diff --git a/config/alfresco/subsystems/Authentication/ldap/ldap-authentication-context.xml b/config/alfresco/subsystems/Authentication/ldap/ldap-authentication-context.xml
index 74f82bb910..05442f1f66 100644
--- a/config/alfresco/subsystems/Authentication/ldap/ldap-authentication-context.xml
+++ b/config/alfresco/subsystems/Authentication/ldap/ldap-authentication-context.xml
@@ -3,342 +3,8 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${ldap.authentication.active}
-
-
-
-
-
-
- ${ldap.authentication.userNameFormat}
-
-
-
-
-
-
-
-
-
-
-
- ${ldap.authentication.escapeCommasInBind}
-
-
- ${ldap.authentication.escapeCommasInUid}
-
-
- ${ldap.authentication.allowGuestLogin}
-
-
- ${ldap.authentication.defaultAdministratorUserNames}
-
-
-
-
-
-
- org.alfresco.repo.security.authentication.AuthenticationComponent
-
-
-
-
-
-
-
-
-
- ${server.transaction.mode.default}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${ldap.synchronization.active}
-
-
-
-
- ${ldap.synchronization.queryBatchSize}
-
-
-
-
- ${ldap.synchronization.groupQuery}
-
-
-
-
- ${ldap.synchronization.groupDifferentialQuery}
-
-
-
-
- ${ldap.synchronization.personQuery}
-
-
-
-
- ${ldap.synchronization.personDifferentialQuery}
-
-
-
-
- ${ldap.synchronization.groupSearchBase}
-
-
-
-
- ${ldap.synchronization.userSearchBase}
-
-
-
-
- ${ldap.synchronization.userIdAttributeName}
-
-
-
-
- ${ldap.synchronization.modifyTimestampAttributeName}
-
-
-
-
- ${ldap.synchronization.groupIdAttributeName}
-
-
-
-
- ${ldap.synchronization.groupType}
-
-
-
-
- ${ldap.synchronization.personType}
-
-
-
-
- ${ldap.synchronization.groupMemberAttributeName}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties b/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties
index 8d1a2429ed..c17dfa1574 100644
--- a/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties
+++ b/config/alfresco/subsystems/Authentication/ldap/ldap-authentication.properties
@@ -25,12 +25,6 @@ ldap.authentication.java.naming.provider.url=ldap://openldap.domain.com:389
# The authentication mechanism to use
ldap.authentication.java.naming.security.authentication=simple
-# The default principal to use (only used for LDAP sync)
-ldap.authentication.java.naming.security.principal=cn\=Manager,dc\=company,dc\=com
-
-# The password for the default principal (only used for LDAP sync)
-ldap.authentication.java.naming.security.credentials=secret
-
# Escape commas entered by the user at bind time
# Useful when using simple authentication and the CN is part of the DN and contains commas
ldap.authentication.escapeCommasInBind=false
@@ -49,6 +43,12 @@ ldap.authentication.defaultAdministratorUserNames=
# authentication, in which case this flag should be set to false.
ldap.synchronization.active=true
+# The default principal to use (only used for LDAP sync)
+ldap.synchronization.java.naming.security.principal=cn\=Manager,dc\=company,dc\=com
+
+# The password for the default principal (only used for LDAP sync)
+ldap.synchronization.java.naming.security.credentials=secret
+
# If positive, this property indicates that RFC 2696 paged results should be
# used to split query results into batches of the specified size. This
# overcomes any size limits imposed by the LDAP server.
@@ -75,6 +75,9 @@ ldap.synchronization.userSearchBase=ou\=People,dc\=company,dc\=com
# The name of the operational attribute recording the last update time for a group or user.
ldap.synchronization.modifyTimestampAttributeName=modifyTimestamp
+# The timestamp format. Unfortunately, this varies between directory servers.
+ldap.synchronization.timestampFormat=yyyyMMddHHmmss'Z'
+
# The attribute name on people objects found in LDAP to use as the uid in Alfresco
ldap.synchronization.userIdAttributeName=uid
diff --git a/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java b/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java
index cc680320a7..fbe14419b1 100644
--- a/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java
+++ b/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java
@@ -145,11 +145,12 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
private String[] groupAttributeNames;
/** The LDAP generalized time format. */
- private static DateFormat LDAP_GENERALIZED_TIME_FORMAT;
- static
+ private DateFormat timestampFormat;
+
+ public LDAPUserRegistry()
{
- LDAPUserRegistry.LDAP_GENERALIZED_TIME_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
- LDAPUserRegistry.LDAP_GENERALIZED_TIME_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
+ // Default to official LDAP generalized time format (unfortunately not used by Active Directory)
+ setTimestampFormat("yyyyMMddHHmmss'Z'");
}
/**
@@ -295,6 +296,22 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
this.modifyTimestampAttributeName = modifyTimestampAttributeName;
}
+ /**
+ * Sets the timestamp format. Unfortunately, this varies between directory servers.
+ *
+ * @param timestampFormat
+ * the timestamp format
+ *
+ * - OpenLDAP: "yyyyMMddHHmmss'Z'"
+ *
- Active Directory: "yyyyMMddHHmmss'.0Z'"
+ *
+ */
+ public void setTimestampFormat(String timestampFormat)
+ {
+ this.timestampFormat = new SimpleDateFormat(timestampFormat);
+ this.timestampFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+ }
+
/**
* Decides whether to error on missing group members.
*
@@ -464,12 +481,17 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
{
searchResults = ctx.search(this.groupSearchBase, this.groupDifferentialQuery, new Object[]
{
- LDAPUserRegistry.LDAP_GENERALIZED_TIME_FORMAT.format(modifiedSince)
+ this.timestampFormat.format(modifiedSince)
}, userSearchCtls);
}
LdapName groupDistinguishedNamePrefix = new LdapName(this.groupSearchBase);
LdapName userDistinguishedNamePrefix = new LdapName(this.userSearchBase);
+
+ // Work out whether the user and group trees are disjoint. This may allow us to optimize reverse DN
+ // resolution.
+ boolean disjoint = !groupDistinguishedNamePrefix.startsWith(userDistinguishedNamePrefix)
+ && !userDistinguishedNamePrefix.startsWith(groupDistinguishedNamePrefix);
while (searchResults.hasMoreElements())
{
@@ -511,8 +533,7 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
Attribute modifyTimestamp = attributes.get(this.modifyTimestampAttributeName);
if (modifyTimestamp != null)
{
- group.setLastModified(LDAPUserRegistry.LDAP_GENERALIZED_TIME_FORMAT.parse(modifyTimestamp.get()
- .toString()));
+ group.setLastModified(this.timestampFormat.parse(modifyTimestamp.get().toString()));
}
Set childAssocs = group.getChildAssociations();
@@ -530,7 +551,7 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
// If the user and group search bases are different we may be able to recognise user and
// group DNs without a secondary lookup
- if (!this.userSearchBase.equalsIgnoreCase(this.groupSearchBase))
+ if (disjoint)
{
Attributes nameAttributes = distinguishedName.getRdn(distinguishedName.size() - 1)
.toAttributes();
@@ -563,8 +584,8 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
{
"objectclass", this.groupIdAttributeName, this.userIdAttributeName
});
- String objectclass = (String) childAttributes.get("objectclass").get();
- if (objectclass.equalsIgnoreCase(this.personType))
+ Attribute objectClass = childAttributes.get("objectclass");
+ if (hasAttributeValue(objectClass, this.personType))
{
nameAttribute = childAttributes.get(this.userIdAttributeName);
if (nameAttribute == null)
@@ -586,9 +607,8 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
childAssocs.add((String) nameAttribute.get());
continue;
}
- else if (objectclass.equalsIgnoreCase(this.groupType))
+ else if (hasAttributeValue(objectClass, this.groupType))
{
-
nameAttribute = childAttributes.get(this.groupIdAttributeName);
if (nameAttribute == null)
{
@@ -656,6 +676,37 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
}
}
+ /**
+ * Does a case-insensitive search for the given value in an attribute
+ *
+ * @param attribute
+ * the attribute
+ * @param value
+ * the value to search for
+ * @return true
, if the value was found
+ * @throws NamingException
+ * if there is a problem accessing the attribute values
+ */
+ private boolean hasAttributeValue(Attribute attribute, String value) throws NamingException
+ {
+ NamingEnumeration> values = attribute.getAll();
+ while (values.hasMore())
+ {
+ try
+ {
+ if (value.equalsIgnoreCase((String) values.next()))
+ {
+ return true;
+ }
+ }
+ catch (ClassCastException e)
+ {
+ // Not a string value. ignore and continue
+ }
+ }
+ return false;
+ }
+
/**
* Wraps the LDAP user query as an {@link Iterator}.
*/
@@ -811,8 +862,8 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
{
try
{
- person.setLastModified(LDAPUserRegistry.LDAP_GENERALIZED_TIME_FORMAT.parse(modifyTimestamp
- .get().toString()));
+ person.setLastModified(LDAPUserRegistry.this.timestampFormat.parse(modifyTimestamp.get()
+ .toString()));
}
catch (ParseException e)
{
@@ -879,7 +930,7 @@ public class LDAPUserRegistry implements UserRegistry, InitializingBean, Activat
this.searchResults = this.ctx.search(LDAPUserRegistry.this.userSearchBase,
LDAPUserRegistry.this.personDifferentialQuery, new Object[]
{
- LDAPUserRegistry.LDAP_GENERALIZED_TIME_FORMAT.format(this.modifiedSince)
+ LDAPUserRegistry.this.timestampFormat.format(this.modifiedSince)
}, this.userSearchCtls);
}
}