From d5d2ad9b0346fccc4b25faae2567b6829b896e54 Mon Sep 17 00:00:00 2001 From: Joel Bout Date: Fri, 13 Mar 2015 18:39:02 +0100 Subject: [PATCH 1/4] Added complex rules to map the user attributes --- model/LdapAdapter.php | 16 ++- model/LdapUser.php | 230 +++----------------------------------- model/LdapUserFactory.php | 122 ++++++++++++++++++++ 3 files changed, 150 insertions(+), 218 deletions(-) create mode 100644 model/LdapUserFactory.php diff --git a/model/LdapAdapter.php b/model/LdapAdapter.php index 2174a1f..764b841 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; @@ -80,9 +84,13 @@ class LdapAdapter implements LoginAdapter $this->adapter = new Ldap(); $this->adapter->setOptions($configuration['config']); - $this->setMapping($configuration['mapping']); + $this->setMapping(isset($configuration['mapping']) ? $configuration['mapping'] : array()); } + + public function getOption($name) { + return $this->configuration[$name]; + } /** * Set the credential @@ -108,10 +116,10 @@ class LdapAdapter implements LoginAdapter $result = $adapter->getAccountObject(); $params = get_object_vars($result); + + $factory = new LdapUserFactory($this->getOption(self::OPTION_USER_MAPPING)); + $user = $factory->createUser($params); - $user = new LdapUser($this->getMapping()); - - $user->setUserRawParameters($params); return $user; } else { 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 + ); + } +} From ae186f3bf527b3d83deda18d0451256b84587b70 Mon Sep 17 00:00:00 2001 From: Joel Bout Date: Mon, 21 Dec 2015 15:07:34 +0100 Subject: [PATCH 2/4] Fix double constructor --- model/LdapAdapter.php | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/model/LdapAdapter.php b/model/LdapAdapter.php index 4b7b314..aae61a7 100644 --- a/model/LdapAdapter.php +++ b/model/LdapAdapter.php @@ -82,17 +82,6 @@ class LdapAdapter implements LoginAdapter $this->adapter = new Ldap(); } - /** - * @param array $configuration - */ - public function __construct(array $configuration) { - $this->configuration = $configuration; - - $this->adapter = new Ldap(); - $this->adapter->setOptions($configuration['config']); - - } - public function setOptions(array $options) { $this->configuration = $options; $this->adapter->setOptions($options['config']); @@ -101,7 +90,11 @@ class LdapAdapter implements LoginAdapter public function getOption($name) { return $this->configuration[$name]; } - + + public function hasOption($name) { + return isset($this->configuration[$name]); + } + /** * Set the credential * @@ -127,7 +120,10 @@ class LdapAdapter implements LoginAdapter $result = $adapter->getAccountObject(); $params = get_object_vars($result); - $factory = new LdapUserFactory($this->getOption(self::OPTION_USER_MAPPING)); + $mapping = $this->hasOption(self::OPTION_USER_MAPPING) + ? $this->getOption(self::OPTION_USER_MAPPING) + : array(); + $factory = new LdapUserFactory($mapping); $user = $factory->createUser($params); return $user; From ee2974c8bedee52551a31b803f222df697b0c1fb Mon Sep 17 00:00:00 2001 From: Joel Bout Date: Wed, 23 Dec 2015 17:35:12 +0100 Subject: [PATCH 3/4] added mapping examples and explanations --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/README.md b/README.md index a7fb666..18c89b1 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 for the user. + +------------------------------ + +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. From 2e43d18c536426d359977e3ad477d7ccef8bc959 Mon Sep 17 00:00:00 2001 From: Joel Bout Date: Wed, 23 Dec 2015 17:37:43 +0100 Subject: [PATCH 4/4] specified override of example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18c89b1..74d4913 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Alternatively if you want to take over a value of an LDAP attribute you would ad ); ), -This would use the value of the LDAP attribute 'username' as label for the user. +This would use the value of the LDAP attribute 'username' as label (RDFS_LABEL) for the user, overriding the default rule to use 'displayName'. ------------------------------