/** * Gets a user link from the adapter map table. * * @param string|integer $user Either a username or Joomla user ID. * @param boolean $isId If the user input is a user ID then set to true. * * @return array Array of links for user or empty array if none found. * * @since 2.1 */ public static function getUser($user = null, $isId = false) { // Use I_ for joomla_id and U_ for usernames $cacheKey = ($isId ? 'I_' : 'U_') . $user; if (isset(self::$userCache[$cacheKey])) { return self::$userCache[$cacheKey]; } if ($isId) { $links = self::lookupFromJoomlaId(self::TYPE_USER, $user); } else { $links = self::lookupFromUsername($user); } if ($links) { self::$userCache[$cacheKey] = $links; if (!$isId) { // There should be only ever one Joomla_Id for a user therefore we can cache that now foreach ($links as $link) { self::$userCache['I_' . $link['joomla_id']] = array($link); } } return self::$userCache[$cacheKey]; } // Backwards compatibility for 2.0 (*inefficient methods*) if ($type = SHUserHelper::getTypeParam($user)) { $domain = SHUserHelper::getDomainParam($user); return array(array('type' => self::TYPE_USER, 'adapter' => $type, 'domain' => $domain, 'adapter_id' => null, 'joomla_id' => null)); } return array(); }
/** * Method handles login logic and report back to the subject. * * @param array $user Holds the user data. * @param array $options Extra options such as autoregister. * * @return boolean Cancels login on False. * * @since 2.0 */ public function onUserLogin($user, $options = array()) { // Check if we have a user adapter already established for this user if (!isset(SHFactory::$adapters[strtolower($user['username'])])) { // SHAdapter did not log this in, get out now return; } // Get the processed user adapter directly from the static adapter holder $adapter = SHFactory::$adapters[strtolower($user['username'])]; if (!(isset($user['type']) && $adapter::getType($user['type']) && $adapter::getType('LDAP'))) { // Incorrect authentication type for this adapter OR is not compatible with LDAP return; } // Lets pass the getUser method the adapter so it can get extra values $options['adapter'] = $adapter; try { // Get a handle to the Joomla User object ready to be passed to the individual plugins $instance = SHUserHelper::getUser($user, $options); } catch (Exception $e) { // Failed to get the user either due to save error or autoregister SHLog::add($e, 10991, JLog::ERROR, 'ldap'); return false; } // Fire the ldap specific on login events $result = SHLdapHelper::triggerEvent('onUserLogin', array(&$instance, $options)); if ($result === false) { // Due to Joomla's inbuilt User Plugin, we have to raise an exception to abort login throw new RuntimeException(JText::sprintf('LIB_SHLDAPEVENTBOUNCER_ERR_10999', $user['username']), 10999); } // Check if any changes were made that need to be saved if ($result === true || isset($options['change'])) { SHLog::add(JText::sprintf('LIB_SHLDAPEVENTBOUNCER_DEBUG_10984', $user['username']), 10984, JLog::DEBUG, 'ldap'); try { // Save the user back to the Joomla database if (!SHUserHelper::save($instance)) { SHLog::add(JText::sprintf('LIB_SHLDAPEVENTBOUNCER_ERR_10988', $user['username']), 10988, JLog::ERROR, 'ldap'); } } catch (Exception $e) { SHLog::add($e, 10989, JLog::ERROR, 'ldap'); } } // Allow Ldap events to be called $this->isLdap = true; return true; }
/** * Method is called before user data is stored in the database. * * Saves profile data to LDAP if a profile form is detected. * * @param array $user Holds the old user data. * @param boolean $isNew True if a new user is stored. * @param array $new Holds the new user data. * * @return boolean Cancels the save if False. * * @since 2.0 */ public function onUserBeforeSave($user, $isNew, $new) { if (!$this->params->get('allow_ldap_save', 1)) { // Not allowed to save back to LDAP return; } // Default the return result to true $result = true; try { // Get username for adapter $username = SHUtilArrayhelper::getValue($user, 'username', false, 'string'); if (empty($username)) { // The old username isn't present so use new username $username = SHUtilArrayhelper::getValue($new, 'username', false, 'string'); } // Include the mandatory Joomla fields (fullname and email) $this->saveMandatoryToLdap($username, $new['name'], $new['email']); // Check there is a profile to save (i.e. this event may not have been called from the profile form) if ($this->use_profile && (isset($new[self::FORM_FIELDS_NAME]) && (count($new[self::FORM_FIELDS_NAME])))) { $xml = $this->getXMLFields(SHUserHelper::getDomainParam($new)); // Only get profile data and enabled elements from the input $profileData = $this->cleanInput($xml, $new[self::FORM_FIELDS_NAME]); // Save the profile back to LDAP $result = $this->saveProfileToLdap($xml, $username, $profileData); } } catch (Exception $e) { SHLog::add($e, 12232, JLog::ERROR, 'ldap'); return false; } return $result; }
/** * Entry point for the script. * * @return void * * @since 2.0 */ public function doExecute() { // Setup some stats $failed = 0; $success = 0; $errors = array(); // It appears we have to tell the system we are running with the site otherwise bad things happen JFactory::getApplication('site'); $this->out(JText::_('CLI_SHMANIC_LDAP_INFO_13001')); // Get all the valid configurations if (!($configs = SHLdapHelper::getConfig())) { // Failed to find any Ldap configs $this->out(JText::_('CLI_SHMANIC_LDAP_ERR_13003')); $this->close(1); } // Check if only a single config was found if ($configs instanceof JRegistry) { /* * To make things easier, we pretend we returned multiple Ldap configs * by casting the single entry into an array. */ $configs = array($configs); } $count = count($configs); $this->out(JText::sprintf('CLI_SHMANIC_LDAP_INFO_13002', $count))->out(); // Loop around each LDAP configuration foreach ($configs as $config) { try { // Get a new Ldap object $ldap = new SHLdap($config); // Bind with the proxy user if (!$ldap->authenticate(SHLdap::AUTH_PROXY)) { // Something is wrong with this LDAP configuration - cannot bind to proxy user $errors[] = new Exception(JText::sprintf('CLI_SHMANIC_LDAP_ERR_13011', $ldap->info), 13011); unset($ldap); continue; } // Get all the Ldap users in the directory if (!($result = $ldap->search(null, $ldap->allUserFilter, array('dn', $ldap->keyUid)))) { // Failed to search for all users in the directory $errors[] = new Exception(JText::sprintf('CLI_SHMANIC_LDAP_ERR_13012', $ldap->getErrorMsg()), 13012); unset($ldap); continue; } // Loop around each Ldap user for ($i = 0; $i < $result->countEntries(); ++$i) { // Get the Ldap username (case insensitive) if (!($username = strtolower($result->getValue($i, $ldap->keyUid, 0)))) { continue; } try { // Check if this user is in the blacklist if ($blacklist = (array) json_decode(SHFactory::getConfig()->get('user.blacklist'))) { if (in_array($username, $blacklist)) { throw new RuntimeException(JText::_('CLI_SHMANIC_LDAP_ERR_13025'), 13025); } } // Create the new user adapter $adapter = new SHUserAdaptersLdap(array('username' => $username), $config); // Get the Ldap DN if (!($dn = $adapter->getId(false))) { continue; } $this->out(JText::sprintf('CLI_SHMANIC_LDAP_INFO_13020', $username)); // Get the Ldap user attributes $source = $adapter->getAttributes(); // Get the core mandatory J! user fields $username = $adapter->getUid(); $fullname = $adapter->getFullname(); $email = $adapter->getEmail(); if (empty($fullname)) { // Full name doesnt exist; use the username instead $fullname = $username; } if (empty($email)) { // Email doesnt exist; cannot proceed throw new Exception(JText::_('CLI_SHMANIC_LDAP_ERR_13022'), 13022); } // Create the user array to enable creating a JUser object $user = array('fullname' => $fullname, 'username' => $username, 'password_clear' => null, 'email' => $email); // Create a JUser object from the Ldap user $options = array('adapter' => &$adapter); $instance = SHUserHelper::getUser($user, $options); if ($instance === false) { // Failed to get the user either due to save error or autoregister throw new Exception(JText::_('CLI_SHMANIC_LDAP_ERR_13024'), 13024); } // Fire the Ldap specific on Sync feature $sync = SHLdapHelper::triggerEvent('onLdapSync', array(&$instance, $options)); // Check if the synchronise was successfully and report if ($sync !== false) { // Even if the sync does not need a save, do it anyway as Cron efficiency doesnt matter too much SHUserHelper::save($instance); // Update the user map linker SHAdapterMap::setUser($adapter, $instance->id); // Above should throw an exception on error so therefore we can report success $this->out(JText::sprintf('CLI_SHMANIC_LDAP_INFO_13029', $username)); ++$success; } else { throw new Exception(JText::_('CLI_SHMANIC_LDAP_ERR_13026'), 13026); } unset($adapter); } catch (Exception $e) { unset($adapter); ++$failed; $errors[] = new Exception(JText::sprintf('CLI_SHMANIC_LDAP_ERR_13028', $username, $e->getMessage()), $e->getCode()); } } } catch (Exception $e) { $errors[] = new Exception(JText::_('CLI_SHMANIC_LDAP_ERR_13004'), 13004); } } // Print out some results and stats $this->out()->out()->out(JText::_('CLI_SHMANIC_LDAP_INFO_13032'))->out(); $this->out(JText::_('CLI_SHMANIC_LDAP_INFO_13038')); foreach ($errors as $error) { if ($error instanceof Exception) { $this->out(' ' . $error->getCode() . ': ' . $error->getMessage()); } else { $this->out(' ' . (string) $error); } } $this->out()->out(JText::sprintf('CLI_SHMANIC_LDAP_INFO_13034', $success)); $this->out(JText::sprintf('CLI_SHMANIC_LDAP_INFO_13036', $failed)); $this->out()->out('============================'); }
/** * Method to initialise all the properties based on the parameters * specified in the plugin. * * @return boolean True on valid entries in the mapping list. * * @since 2.0 */ protected function doSetup() { static $done = null; if (is_null($done)) { // Assign class properties based on parameters from the plugin $this->sync_groups = (bool) $this->params->get('sync_groups', false); $this->addition = (bool) $this->params->get('addition', true); $this->removal = (int) $this->params->get('removal', self::YESDEFAULT); $this->unmanaged = array_map('intval', explode(';', $this->params->get('unmanaged'))); $this->public_id = (int) $this->params->get('public_id'); $this->dn_validate = $this->params->get('dn_validate', 1); $this->lookup_type = (int) $this->params->get('lookup_type', self::LOOKUP_FORWARD); $this->memberof_attribute = $this->params->get('memberof_attribute'); $this->member_attribute = $this->params->get('member_attribute', 'member'); $this->member_dn = $this->params->get('member_dn', 'dn'); $this->recursion = (bool) $this->params->get('recursion', false); $this->dn_attribute = $this->params->get('dn_attribute', 'distinguishedName'); $this->recursion_depth = (int) $this->params->get('recursion_depth', 0); $this->entries = array(); $this->list = array(); $list = preg_split('/\\r\\n|\\n|\\r/', $this->params->get('list')); // Loops around each mapping entry parameter foreach ($list as $item) { // Remove any accidental whitespace from the entry $item = trim($item); // Find the right most (outside of the distinguished name) to split groups if ($pos = strrpos($item, ':')) { // Store distinguished name in a string and Joomla groups in an array $entryDn = trim(substr($item, 0, $pos)); if ($entryGroups = array_map('intval', explode(',', substr($item, $pos + 1)))) { // Store as a parameter for validation later $this->list[$entryDn] = $entryGroups; } } } // Get all the Joomla user groups $JUserGroups = SHUserHelper::getJUserGroups(); $JUserGroupsKey = array_fill_keys($JUserGroups, ''); /* * Process the map list parameter into validated entries. * Then ensure that there is atleast 1 valid entry to * proceed with the mapping process. */ foreach ($this->list as $dn => $groups) { foreach ($groups as $key => $group) { if (!isset($JUserGroupsKey[$group])) { // This isn't a valid Joomla group unset($this->list[$dn][$key]); continue; } } if (empty($this->list[$dn])) { // This DN doesn't have any valid Joomla groups unset($this->list[$dn]); continue; } /* * Add the entry to a new mapping entry object then check if it is * valid. If so then we can assume this entry has no syntax errors. */ $entry = new SHLdapMappingEntry($dn, $this->list[$dn], $this->dn_validate); if ($entry->isValid()) { // Add as a valid entry $this->entries[] = $entry; // Add as a managed group if ($this->removal === self::YES) { /* * Yes means we want to add only groups that are defined in the mapping list. * Looping around dn group parameter list, ensuring its not already there and not in unmanaged. */ $this->managed = array_merge($this->managed, array_diff(array_diff($this->list[$dn], $this->unmanaged), $this->managed)); } } } if ($this->removal === self::YESDEFAULT) { // Yesdefault means we want to add all Joomla groups to the managed pool $this->managed = array_diff($JUserGroups, $this->unmanaged); } $done = true; if (!count($this->entries)) { // No valid entries here SHLog::add(JText::_('PLG_LDAP_MAPPING_DEBUG_12006'), 12006, JLog::DEBUG, 'ldap'); $done = false; } } return $done; }
/** * Gets the user adapter for the user specified. Creates a new user * adapter if one doesnt already exist for the user. * * @param array|string $user Either a username string or array of credentials including JUser ID and domain. * @param string $type Type of adapter (e.g. ldap, xml, federated). * @param array $options An array of optional options including isNew. * * @return SHUserAdapter Object to user adapter. * * @since 2.0 * @throws Exception */ public static function getUserAdapter($user, $type = null, $options = array()) { if (is_array($user)) { $username = strtolower(SHUtilArrayhelper::getValue($user, 'username', null, 'string')); $credentials = $user; } else { $username = strtolower((string) $user); $credentials = array('username' => $username); } if (empty($username)) { throw new RuntimeException(JText::_('LIB_SHFACTORY_ERR_2121'), 2121); } if (!isset(self::$adapters[$username])) { $config = self::getConfig(); // Check if this user is in the blacklist if ($blacklist = (array) json_decode($config->get('user.blacklist'))) { if (in_array($username, $blacklist)) { throw new RuntimeException(JText::sprintf('LIB_SHFACTORY_ERR_2125', $username), 2125); } } // If the JUser ID has been specified then use it (more efficient) if ($id = isset($credentials['id']) ? (int) $credentials['id'] : JUserHelper::getUserId($username)) { $jUser = JFactory::getUser($id); if ((boolean) $config->get('user.usedomain', true)) { if (!isset($credentials['domain'])) { // Attempt to get the domain for this user $credentials['domain'] = SHUserHelper::getDomainParam($jUser); } } else { unset($credentials['domain']); } if (!isset($credentials['type'])) { // Attempt to get the User Adapter type $type = SHUserHelper::getTypeParam($jUser); } } if (is_null($type)) { // Get the default/primary user adpater type from the database $type = $config->get('user.type'); } // Camel case friendly for class name $type = ucfirst(strtolower($type)); $class = "SHUserAdapters${type}"; if (class_exists($class)) { // Create the adapter (note: remember to unset if using multiple adapters!) self::$adapters[$username] = new $class($credentials, null, $options); } else { throw new RuntimeException(JText::sprintf('LIB_SHFACTORY_ERR_2123', $class), 2123); } } else { // Update credentials if required if ($password = SHUtilArrayhelper::getValue($user, 'password', false)) { self::$adapters[$username]->updateCredential($password, $options); } } return self::$adapters[$username]; }
/** * Returns if the current or specified user was authenticated * via LDAP. * * @param JUser|integer|array $user Optional user id (if null then uses current user). * * @return boolean True if user is Ldap authenticated or False otherwise. * * @since 2.0 */ public static function isUserLdap($user = null) { $type = SHUserHelper::getTypeParam($user); // Create a new adapter if ($type = ucfirst(strtolower($type))) { $class = "SHUserAdapters${type}"; $adapter = new $class(array('username' => '', 'password' => '')); return $adapter->getType('LDAP') ? true : false; } return false; }