}
// Hack prefix to objectclass
- if (empty($this->config->objectclass)) {
- // Can't send empty filter
- $this->config->objectclass = '(objectClass=*)';
- } else if (stripos($this->config->objectclass, 'objectClass=') === 0) {
- // Value is 'objectClass=some-string-here', so just add ()
- // around the value (filter _must_ have them).
- $this->config->objectclass = '('.$this->config->objectclass.')';
- } else if (strpos($this->config->objectclass, '(') !== 0) {
- // Value is 'some-string-not-starting-with-left-parentheses',
- // which is assumed to be the objectClass matching value.
- // So build a valid filter with it.
- $this->config->objectclass = '(objectClass='.$this->config->objectclass.')';
- } else {
- // There is an additional possible value
- // '(some-string-here)', that can be used to specify any
- // valid filter string, to select subsets of users based
- // on any criteria. For example, we could select the users
- // whose objectClass is 'user' and have the
- // 'enabledMoodleUser' attribute, with something like:
- //
- // (&(objectClass=user)(enabledMoodleUser=1))
- //
- // In this particular case we don't need to do anything,
- // so leave $this->config->objectclass as is.
- }
+ $this->config->objectclass = ldap_normalise_objectclass($this->config->objectclass);
}
/**
protected $enroltype = 'enrol_ldap';
protected $errorlogtag = '[ENROL LDAP] ';
+ /**
+ * The object class to use when finding users.
+ *
+ * @var string $userobjectclass
+ */
+ protected $userobjectclass;
+
/**
* Constructor for the plugin. In addition to calling the parent
* constructor, we define and 'fix' some settings depending on the
unset($ldap_usertypes);
$default = ldap_getdefaults();
- // Remove the objectclass default, as the values specified there are for
- // users, and we are dealing with groups here.
+
+ // The objectclass in the defaults is for a user.
+ // This will be required later, but enrol_ldap uses 'objectclass' for its group objectclass.
+ // Save the normalised user objectclass for later.
+ $this->userobjectclass = ldap_normalise_objectclass($default['objectclass'][$this->get_config('user_type')]);
+
+ // Remove the objectclass default, as the values specified there are for users, and we are dealing with groups here.
unset($default['objectclass']);
// Use defaults if values not given. Dont use this->get_config()
}
}
+ // Normalise the objectclass used for groups.
if (empty($this->config->objectclass)) {
- // Can't send empty filter. Fix it for now and future occasions
- $this->set_config('objectclass', '(objectClass=*)');
- } else if (stripos($this->config->objectclass, 'objectClass=') === 0) {
- // Value is 'objectClass=some-string-here', so just add ()
- // around the value (filter _must_ have them).
- // Fix it for now and future occasions
- $this->set_config('objectclass', '('.$this->config->objectclass.')');
- } else if (stripos($this->config->objectclass, '(') !== 0) {
- // Value is 'some-string-not-starting-with-left-parentheses',
- // which is assumed to be the objectClass matching value.
- // So build a valid filter with it.
- $this->set_config('objectclass', '(objectClass='.$this->config->objectclass.')');
+ // No objectclass set yet - set a default class.
+ $this->config->objectclass = ldap_normalise_objectclass(null, '*');
+ $this->set_config('objectclass', $this->config->objectclass);
} else {
- // There is an additional possible value
- // '(some-string-here)', that can be used to specify any
- // valid filter string, to select subsets of users based
- // on any criteria. For example, we could select the users
- // whose objectClass is 'user' and have the
- // 'enabledMoodleUser' attribute, with something like:
- //
- // (&(objectClass=user)(enabledMoodleUser=1))
- //
- // In this particular case we don't need to do anything,
- // so leave $this->config->objectclass as is.
+ $objectclass = ldap_normalise_objectclass($this->config->objectclass);
+ if ($objectclass !== $this->config->objectclass) {
+ // The objectclass was changed during normalisation.
+ // Save it in config, and update the local copy of config.
+ $this->set_config('objectclass', $objectclass);
+ $this->config->objectclass = $objectclass;
+ }
}
}
// as the idnumber does not match their dn and we get dn's from membership.
$memberidnumbers = array();
foreach ($ldapmembers as $ldapmember) {
- $result = ldap_read($this->ldapconnection, $ldapmember, '(objectClass=*)',
+ $result = ldap_read($this->ldapconnection, $ldapmember, $this->userobjectclass,
array($this->config->idnumber_attribute));
$entry = ldap_first_entry($this->ldapconnection, $result);
$values = ldap_get_values($this->ldapconnection, $entry, $this->config->idnumber_attribute);
require_once($CFG->libdir.'/ldaplib.php');
$ldap_contexts = explode(';', $this->get_config('user_contexts'));
- $ldap_defaults = ldap_getdefaults();
return ldap_find_userdn($this->ldapconnection, $userid, $ldap_contexts,
- '(objectClass='.$ldap_defaults['objectclass'][$this->get_config('user_type')].')',
+ $this->userobjectclass,
$this->get_config('idnumber_attribute'), $this->get_config('user_search_sub'));
}
}
}
}
+
+ /**
+ * Test that normalisation of the use objectclass is completed successfully.
+ *
+ * @dataProvider objectclass_fetch_provider
+ * @param string $usertype The supported user type
+ * @param string $expected The expected filter value
+ */
+ public function test_objectclass_fetch($usertype, $expected) {
+ $this->resetAfterTest();
+ // Set the user type - this must be performed before the plugin is instantiated.
+ set_config('user_type', $usertype, 'enrol_ldap');
+
+ // Fetch the plugin.
+ $instance = enrol_get_plugin('ldap');
+
+ // Use reflection to sneak a look at the plugin.
+ $rc = new ReflectionClass('enrol_ldap_plugin');
+ $rcp = $rc->getProperty('userobjectclass');
+ $rcp->setAccessible(true);
+
+ // Fetch the current userobjectclass value.
+ $value = $rcp->getValue($instance);
+ $this->assertEquals($expected, $value);
+ }
+
+ /**
+ * Data provider for the test_objectclass_fetch testcase.
+ *
+ * @return array of testcases.
+ */
+ public function objectclass_fetch_provider() {
+ return array(
+ // This is the list of values from ldap_getdefaults() normalised.
+ 'edir' => array(
+ 'edir',
+ '(objectClass=user)'
+ ),
+ 'rfc2307' => array(
+ 'rfc2307',
+ '(objectClass=posixaccount)'
+ ),
+ 'rfc2307bis' => array(
+ 'rfc2307bis',
+ '(objectClass=posixaccount)'
+ ),
+ 'samba' => array(
+ 'samba',
+ '(objectClass=sambasamaccount)'
+ ),
+ 'ad' => array(
+ 'ad',
+ '(samaccounttype=805306368)'
+ ),
+ 'default' => array(
+ 'default',
+ '(objectClass=*)'
+ ),
+ );
+ }
}
return $ldap_user_dn;
}
+/**
+ * Normalise the supplied objectclass filter.
+ *
+ * This normalisation is a rudimentary attempt to format the objectclass filter correctly.
+ *
+ * @param string $objectclass The objectclass to normalise
+ * @param string $default The default objectclass value to use if no objectclass was supplied
+ * @return string The normalised objectclass.
+ */
+function ldap_normalise_objectclass($objectclass, $default = '*') {
+ if (empty($objectclass)) {
+ // Can't send empty filter.
+ $return = sprintf('(objectClass=%s)', $default);
+ } else if (stripos($objectclass, 'objectClass=') === 0) {
+ // Value is 'objectClass=some-string-here', so just add () around the value (filter _must_ have them).
+ $return = sprintf('(%s)', $objectclass);
+ } else if (stripos($objectclass, '(') !== 0) {
+ // Value is 'some-string-not-starting-with-left-parentheses', which is assumed to be the objectClass matching value.
+ // Build a valid filter using the value it.
+ $return = sprintf('(objectClass=%s)', $objectclass);
+ } else {
+ // There is an additional possible value '(some-string-here)', that can be used to specify any valid filter
+ // string, to select subsets of users based on any criteria.
+ //
+ // For example, we could select the users whose objectClass is 'user' and have the 'enabledMoodleUser'
+ // attribute, with something like:
+ //
+ // (&(objectClass=user)(enabledMoodleUser=1))
+ //
+ // In this particular case we don't need to do anything, so leave $this->config->objectclass as is.
+ $return = $objectclass;
+ }
+
+ return $return;
+}
+
/**
* Returns values like ldap_get_entries but is binary compatible and
* returns all attributes as array.
$this->assertSame($test['expected'], ldap_stripslashes($test['test']));
}
}
+
+ /**
+ * Tests for ldap_normalise_objectclass.
+ *
+ * @dataProvider ldap_normalise_objectclass_provider
+ * @param array $args Arguments passed to ldap_normalise_objectclass
+ * @param string $expected The expected objectclass filter
+ */
+ public function test_ldap_normalise_objectclass($args, $expected) {
+ $this->assertEquals($expected, call_user_func_array('ldap_normalise_objectclass', $args));
+ }
+
+ /**
+ * Data provider for the test_ldap_normalise_objectclass testcase.
+ *
+ * @return array of testcases.
+ */
+ public function ldap_normalise_objectclass_provider() {
+ return array(
+ 'Empty value' => array(
+ array(null),
+ '(objectClass=*)',
+ ),
+ 'Empty value with different default' => array(
+ array(null, 'lion'),
+ '(objectClass=lion)',
+ ),
+ 'Supplied unwrapped objectClass' => array(
+ array('objectClass=tiger'),
+ '(objectClass=tiger)',
+ ),
+ 'Supplied string value' => array(
+ array('leopard'),
+ '(objectClass=leopard)',
+ ),
+ 'Supplied complex' => array(
+ array('(&(objectClass=cheetah)(enabledMoodleUser=1))'),
+ '(&(objectClass=cheetah)(enabledMoodleUser=1))',
+ ),
+ );
+ }
}