diff --git a/README.md b/README.md index a7fb666..74d4913 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,59 @@ here the domain is test.com All the parameters are in a separate dc in ldap These are the configuration of the connection to the ldap server. Then the login will try to use this library. + +Configuration +============================ + +By default and LDAP user will be considered a test-taker belonging to no group. + +The following attributes will be taken from LDAP and mapped to TAO properties by default: + +* 'mail' as PROPERTY_USER_MAIL +* 'givenName' as PROPERTY_USER_FIRSTNAME +* 'sn' as PROPERTY_USER_LASTNAME +* 'displayName' as RDFS_LABEL + +However there are several ways to enhance or override this default behaviour: + +------------------------------ + +To hardcode one of the user properties, you would need to add a mapping of the type 'value' to the configuration: + + array( + 'driver' => 'oat\authLdap\model\LdapAdapter', + 'config' => SEE_ABOVE + 'mapping' => array( + 'http://www.tao.lu/Ontologies/TAOGroup.rdf#member' => array( + 'type' => 'value', + 'value' => array('http://localnamespace.com/install#i123456789') + ) + ); + ), + +This example would set the group membership of all users loging in to a group identified by the id http://localnamespace.com/install#i123456789 + +------------------------------ + +Alternatively if you want to take over a value of an LDAP attribute you would add a mapping of type 'attributeValue' + + array( + 'driver' => 'oat\authLdap\model\LdapAdapter', + 'config' => SEE_ABOVE + 'mapping' => array( + 'http://www.tao.lu/Ontologies/TAOGroup.rdf#member' => array( + 'type' => 'value', + 'value' => array('http://localnamespace.com/install#i123456789') + ), + 'http://www.w3.org/2000/01/rdf-schema#label' => array( + 'type' => 'attributeValue', + 'attribute' => 'username' + ) + ); + ), + +This would use the value of the LDAP attribute 'username' as label (RDFS_LABEL) for the user, overriding the default rule to use 'displayName'. + +------------------------------ + +For more advanced cases there is the type 'callback' which allows you to programmatically enhance the mapping of the LDAP attributes to the TAO properties. See oat\authLdap\model\LdapUserFactory for details. diff --git a/model/LdapAdapter.php b/model/LdapAdapter.php index 09904fd..aae61a7 100644 --- a/model/LdapAdapter.php +++ b/model/LdapAdapter.php @@ -45,6 +45,10 @@ use common_persistence_Manager; */ class LdapAdapter implements LoginAdapter { + const OPTION_ADAPTER_CONFIG = 'config'; + + const OPTION_USER_MAPPING = 'mapping'; + /** @var $username string */ private $username; @@ -54,33 +58,43 @@ class LdapAdapter implements LoginAdapter /** @var $configuration array $configuration */ protected $configuration; - /** @var $mapping array $mapping */ - protected $mapping; - /** * @var \Zend\Authentication\Adapter\Ldap */ protected $adapter; + /** + * Create an adapter from the configuration + * + * @param array $configuration + * @return oat\authLdap\model\LdapAdapter + */ + public static function createFromConfig(array $configuration) { + $adapter = new self(); + $adapter->setOptions($configuration); + return $adapter; + } + /** * Instantiates Zend Ldap adapter */ public function __construct() { $this->adapter = new Ldap(); } - - /** - * (non-PHPdoc) - * @see \oat\oatbox\user\auth\LoginAdapter::setOptions() - */ + public function setOptions(array $options) { $this->configuration = $options; $this->adapter->setOptions($options['config']); - if (isset($options['mapping'])) { - $this->setMapping($options['mapping']); - } } - + + public function getOption($name) { + return $this->configuration[$name]; + } + + public function hasOption($name) { + return isset($this->configuration[$name]); + } + /** * Set the credential * @@ -105,10 +119,13 @@ class LdapAdapter implements LoginAdapter $result = $adapter->getAccountObject(); $params = get_object_vars($result); + + $mapping = $this->hasOption(self::OPTION_USER_MAPPING) + ? $this->getOption(self::OPTION_USER_MAPPING) + : array(); + $factory = new LdapUserFactory($mapping); + $user = $factory->createUser($params); - $user = new LdapUser($this->getMapping()); - - $user->setUserRawParameters($params); return $user; } else { @@ -151,24 +168,6 @@ class LdapAdapter implements LoginAdapter return $this->configuration; } - /** - * @param array $mapping - */ - public function setMapping($mapping) - { - $this->mapping = $mapping; - } - - /** - * @return array - */ - public function getMapping() - { - return $this->mapping; - } - - - /** * @param string $password */ diff --git a/model/LdapUser.php b/model/LdapUser.php index c848075..9c571ad 100644 --- a/model/LdapUser.php +++ b/model/LdapUser.php @@ -33,230 +33,32 @@ use common_user_User; use core_kernel_classes_Resource; use core_kernel_classes_Property; use common_Logger; -use SebastianBergmann\Exporter\Exception; class LdapUser extends common_user_User { - - /** @var array of configuration */ - protected $configuration; - - /** - * @var array - */ - protected $userRawParameters; - - /** - * @var array - */ - protected $userExtraParameters = array(); - - /** - * @var string - */ - protected $identifier; - - /** @var array $roles */ - protected $roles; - - /** - * Array that contains the language code as a single string - * - * @var array - */ - protected $languageUi = array(DEFAULT_LANG); - - /** - * Array that contains the language code as a single string - * - * @var array - */ - protected $languageDefLg = array(DEFAULT_LANG); - - - /** - * The mapping of custom parameter from ldap to TAO property - * - * @var array - */ - protected $mapping; - - - public function __construct(array $mapping = null){ - $this->mapping = $mapping; - } - - - /** - * @return array - */ - public function getMapping() + + private $identifier; + + private $cache; + + public function __construct($id, $data) { - return $this->mapping; + $this->identifier = $id; + $this->cache = $data; } - - /** - * Sets the language URI - * - * @param string $languageDefLgUri - */ - public function setLanguageDefLg($languageDefLgUri) + + public function getIdentifier() { - $this->languageDefLg = array((string)$languageDefLgUri); - - return $this; - } - - /** - * Returns the language code - * - * @return array - */ - public function getLanguageDefLg() - { - return $this->languageDefLg; - } - - /** - * @param $property string - * @param $value string - */ - public function setUserParameter($property, $value){ - $this->userRawParameters[$property] = $value; - } - - - public function getUserParameter($property) { - if (isset ($this->userRawParameters[$property] ) ) - return $this->userRawParameters[$property]; - - return null; - } - - /** - * @param array $params - * @return AuthKeyValueUser - */ - public function setUserRawParameters(array $params) - { - $this->setRoles(array('http://www.tao.lu/Ontologies/TAO.rdf#DeliveryRole')); - - // initialize parameter that should be set - isset($params['preferredlanguage']) ? $this->setLanguageUi($params['preferredlanguage']) : DEFAULT_LANG; - isset($params['preferredlanguage']) ? $this->setLanguageDefLg($params['preferredlanguage']) : DEFAULT_LANG; - isset($params['mail']) ? $this->setUserParameter(PROPERTY_USER_MAIL, $params['mail']) : ''; - isset($params['displayname']) ? $this->setUserParameter(PROPERTY_USER_LASTNAME, $params['displayname']) : $this->setUserParameter(PROPERTY_USER_LASTNAME, $params['cn']) ; - - - $mapping = $this->getMapping(); - foreach($params as $key => $value) { - - if(! in_array($key, array('preferredlanguage','mail', 'displayname'))) { - - if(array_key_exists($key, $mapping)){ - $this->setUserParameter($mapping[$key], $value); - } - } - - } - - - return $this; - } - - /** - * @return array - */ - public function getUserRawParameters() - { - return $this->userRawParameters; - } - - - /** - * @param mixed $language - */ - public function setLanguageUi($languageUri) - { - $this->languageUi = array((string)$languageUri); - - return $this; - } - - /** - * @return array - */ - public function getLanguageUi() - { - return $this->languageUi; - } - - - /** - * @return string - */ - public function getIdentifier(){ return $this->identifier; } - - /** - * @param $identifier - * @return $this - */ - public function setIdentifier($identifier){ - $this->identifier = $identifier; - - return $this; - } - - - /** - * @param $property string - * @return array|null - */ + public function getPropertyValues($property) { - $returnValue = null; - - switch ($property) { - case PROPERTY_USER_DEFLG : - $returnValue = $this->getLanguageDefLg(); - break; - case PROPERTY_USER_UILG : - $returnValue = $this->getLanguageUi(); - break; - case PROPERTY_USER_ROLES : - $returnValue = $this->getRoles(); - break; - default: - $returnValue = array($this->getUserParameter($property)); - } - - return $returnValue; + return isset($this->cache[$property]) + ? $this->cache[$property] + : array(); } - - - /** - * Function that will refresh the parameters. - */ + public function refresh() { + return false; } - - - /** - * @return array - */ - public function getRoles() { - return $this->roles; - } - - /** - * @param array $roles - * @return $this - */ - public function setRoles(array $roles ) { - $this->roles = $roles; - - return $this; - } - } diff --git a/model/LdapUserFactory.php b/model/LdapUserFactory.php new file mode 100644 index 0000000..6758ef8 --- /dev/null +++ b/model/LdapUserFactory.php @@ -0,0 +1,122 @@ +getRules() as $property => $rule) { + $data[$property] = $this->map($rule, $rawData); + } + + return new LdapUser($id, $data); + } + + public function map($propertyConfig, $rawData) { + $data = array(); + switch ($propertyConfig['type']) { + case 'value' : + $data = $propertyConfig['value']; + break; + case 'attributeValue' : + if (isset($rawData[$propertyConfig['attribute']])) { + $value = $rawData[$propertyConfig['attribute']]; + $data = is_array($value) ? $value : array($value); + } + break; + case 'callback' : + if (isset($rawData[$propertyConfig['attribute']])) { + $callback = $propertyConfig['callable']; + if (is_callable($callback)) { + $data = call_user_func($callback, $rawData[$propertyConfig['attribute']]); + } + } + break; + default : + throw new \common_exception_InconsistentData('Unknown mapping: '.$propertyConfig['type']); + } + return $data; + } + + public function getRules() { + $rules = self::getDefaultConfig(); + foreach ($this->getOptions() as $key => $value) { + $rules[$key] = $value; + } + return $rules; + } + + static public function getDefaultConfig() + { + return array( + PROPERTY_USER_ROLES => self::rawValue(INSTANCE_ROLE_DELIVERY) + ,PROPERTY_USER_UILG => self::rawValue(DEFAULT_LANG) + ,PROPERTY_USER_DEFLG => self::rawValue(DEFAULT_LANG) + ,PROPERTY_USER_TIMEZONE => self::rawValue(TIME_ZONE) + ,PROPERTY_USER_MAIL => self::attributeValue('mail') + ,PROPERTY_USER_FIRSTNAME => self::attributeValue('givenName') + ,PROPERTY_USER_LASTNAME => self::attributeValue('sn') + ,RDFS_LABEL => self::attributeValue('displayName') + ); + } + + static protected function rawValue($value) { + return array( + 'type' => 'value', + 'value' => array($value) + ); + } + + static protected function attributeValue($attributeName) { + return array( + 'type' => 'attributeValue', + 'attribute' => $attributeName + ); + } + + static protected function callback($callable, $attributeName) { + return array( + 'type' => 'callback', + 'callable' => $callable, + 'attribute' => $attributeName + ); + } +}