/** * @test */ public function searchGroupsReturnsExactlyOneResult() { $baseDn = 'cn=read-only-admin,dc=example,dc=com'; $filter = '(ou=*)'; $groups = $this->fixture->search($baseDn, $filter, array(), TRUE); $this->assertEquals('ou=mathematicians,dc=example,dc=com', $groups['dn']); }
/** * Checks the LDAP connection and prepares a Flash message if unavailable. * * @return bool */ protected function checkLdapConnection() { try { $success = $this->ldap->connect(Configuration::getLdapConfiguration()); } catch (\Causal\IgLdapSsoAuth\Exception\UnresolvedPhpDependencyException $e) { // Possible known exception: 1409566275, LDAP extension is not available for PHP $this->addFlashMessage($e->getMessage(), 'Error ' . $e->getCode(), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR); return FALSE; } return $success; }
/** * Returns a LDAP user. * * @param string $dn * @return array */ protected static function getLdapUser($dn = NULL) { // Restricting the list of returned attributes sometimes makes the ldap_search() method issue a PHP warning: // Warning: ldap_search(): Array initialization wrong // so we just ask for every attribute ("TRUE" below)! if (TRUE || Configuration::hasExtendedMapping(static::$config['users']['mapping'])) { $attributes = array(); } else { // Currently never called ever again due to the warning found sometimes (see above) $attributes = Configuration::getLdapAttributes(static::$config['users']['mapping']); if (strpos(static::$config['groups']['filter'], '{USERUID}') !== FALSE) { $attributes[] = 'uid'; $attributes = array_unique($attributes); } } $users = Ldap::getInstance()->search($dn, str_replace('{USERNAME}', '*', static::$config['users']['filter']), $attributes); $user = is_array($users[0]) ? $users[0] : NULL; static::getLogger()->debug(sprintf('Retrieving LDAP user from DN "%s"', $dn), $user); return $user; }
/** * Returns groups associated to a given user (identified either by his DN or his uid attribute). * * @param string $baseDn * @param string $filter * @param string $userDn * @param string $userUid * @param array $attributes * @return array */ public static function selectFromUser($baseDn, $filter = '', $userDn = '', $userUid = '', array $attributes = array()) { $filter = str_replace('{USERDN}', Ldap::getInstance()->escapeDnForFilter($userDn), $filter); $filter = str_replace('{USERUID}', Ldap::getInstance()->escapeDnForFilter($userUid), $filter); $groups = Ldap::getInstance()->search($baseDn, $filter, $attributes); return $groups; }
/** * Returns TRUE is a previous call to @see fetchLdapUsers() returned * a partial result set. * * @return bool */ public function hasMoreLdapUsers() { return Ldap::getInstance()->isPartialSearchResult(); }
/** * Escapes a string for use in a LDAP filter statement. * * To find the groups of a user by filtering the groups where the * authenticated user is in the members list some characters * in the users distinguished name can make the filter expression * invalid. * * At the moment this problem was experienced with brackets which * are also used in the filter, e.g.: * (&(objectClass=group)(member={USERDN})) * * Additionally a single backslash (that is used for escaping special * characters like commas) needs to be escaped. E.g.: * CN=Lastname\, Firstname,DC=company,DC=tld needs to be escaped like: * CN=Lastname\\, Firstname,DC=company,DC=tld * * @param string $dn * @return string Escaped $dn * @deprecated since 3.0 will be removed in 3.2, use \Causal\IgLdapSsoAuth\Library\Ldap::getInstance()->escapeDnForFilter() instead */ public static function escapeDnForFilter($dn) { return \Causal\IgLdapSsoAuth\Library\Ldap::getInstance()->escapeDnForFilter($dn); }
/** * Performs the synchronization of LDAP users according to selected parameters. * * @return boolean Returns TRUE on successful execution, FALSE on error * @throws ImportUsersException */ public function execute() { // Assemble a list of configuration and contexts for import /** @var \Causal\IgLdapSsoAuth\Domain\Repository\ConfigurationRepository $configurationRepository */ $configurationRepository = GeneralUtility::makeInstance('Causal\\IgLdapSsoAuth\\Domain\\Repository\\ConfigurationRepository'); if (empty($this->configuration)) { $ldapConfigurations = $configurationRepository->findAll(); } else { $configuration = $configurationRepository->findByUid($this->configuration); $ldapConfigurations = array(); if ($configuration !== NULL) { $ldapConfigurations[] = $configuration; } } if ($this->context === 'both') { $executionContexts = array('fe', 'be'); } else { $executionContexts = array($this->context); } $mode = $this->getMode(); // Start a database transaction with all our changes // Syntax is compatible with MySQL, Oracle, MSSQL and PostgreSQL $this->getDatabaseConnection()->sql_query('START TRANSACTION'); // Loop on each configuration and context and import the related users $failures = 0; foreach ($ldapConfigurations as $configuration) { foreach ($executionContexts as $aContext) { /** @var \Causal\IgLdapSsoAuth\Utility\UserImportUtility $importUtility */ $importUtility = GeneralUtility::makeInstance('Causal\\IgLdapSsoAuth\\Utility\\UserImportUtility', $configuration, $aContext); $config = $importUtility->getConfiguration(); if (empty($config['users']['filter'])) { // Current context is not configured for this LDAP configuration record static::getLogger()->debug(sprintf('Configuration record %s is not configured for context "%s"', $configuration->getUid(), $aContext)); unset($importUtility); continue; } // Start by connecting to the designated LDAP/AD server $success = Ldap::getInstance()->connect(Configuration::getLdapConfiguration()); // Proceed with import if successful if (!$success) { $failures++; unset($importUtility); continue; } $ldapUsers = $importUtility->fetchLdapUsers(); // Consider that fetching no users from LDAP is an error if (count($ldapUsers) === 0) { static::getLogger()->error(sprintf('No users (%s) found for configuration record %s', $aContext, $configuration->getUid())); $failures++; } else { // Disable or delete users, according to settings if ($this->missingUsersHandling === 'disable') { static::getLogger()->debug(sprintf('Disabling users (%s) for configuration record %s', $aContext, $configuration->getUid())); $importUtility->disableUsers(); } elseif ($this->missingUsersHandling === 'delete') { static::getLogger()->debug(sprintf('Deleting users (%s) for configuration record %s', $aContext, $configuration->getUid())); $importUtility->deleteUsers(); } // Proceed with import (handle partial result sets until every LDAP record has been returned) do { $typo3Users = $importUtility->fetchTypo3Users($ldapUsers); // Loop on all users and import them foreach ($ldapUsers as $index => $aUser) { if ($mode === 'sync' && empty($typo3Users[$index]['uid'])) { // New LDAP user => skip it since only existing TYPO3 users should get synchronized continue; } // Merge LDAP and TYPO3 information $user = Authentication::merge($aUser, $typo3Users[$index], $config['users']['mapping']); // Import the user using information from LDAP $importUtility->import($user, $aUser, $this->restoredUsersHandling); } static::getLogger()->info(sprintf('Configuration record %s: processed %s LDAP users (%s)', $configuration->getUid(), count($ldapUsers), $aContext)); // Free memory before going on $typo3Users = NULL; $ldapUsers = NULL; $ldapUsers = $importUtility->hasMoreLdapUsers() ? $importUtility->fetchLdapUsers(TRUE) : array(); } while (count($ldapUsers) > 0); } // Clean up unset($importUtility); Ldap::getInstance()->disconnect(); } } // If some failures were registered, rollback the whole transaction and report error if ($failures > 0) { $this->getDatabaseConnection()->sql_query('ROLLBACK'); $message = 'Some or all imports failed. Synchronisation was aborted. Check your settings or your network connection'; static::getLogger()->error($message); throw new ImportUsersException($message, 1410774015); } else { // Everything went fine, commit the changes $this->getDatabaseConnection()->sql_query('COMMIT'); } return TRUE; }