/** * Authenticates using LDAP and returns a user record or FALSE * if operation fails. * * @param string $username * @param string $password * @return bool|array TRUE or array of user info on success, otherwise FALSE * @throws \Causal\IgLdapSsoAuth\Exception\UnresolvedPhpDependencyException when LDAP extension for PHP is not available */ public static function ldapAuthenticate($username, $password = NULL) { static::$lastAuthenticationDiagnostic = ''; if ($username && Configuration::getValue('forceLowerCaseUsername')) { // Possible enhancement: use \TYPO3\CMS\Core\Charset\CharsetConverter::conv_case instead $username = strtolower($username); } // Valid user only if username and connect to LDAP server. if ($username && Ldap::getInstance()->connect(Configuration::getLdapConfiguration())) { // Set extension configuration from TYPO3 mode (BE/FE). static::initializeConfiguration(); // Valid user from LDAP server if ($userdn = Ldap::getInstance()->validateUser($username, $password, static::$config['users']['basedn'], static::$config['users']['filter'])) { static::getLogger()->info(sprintf('Successfully authenticated' . ($password === NULL ? ' SSO' : '') . ' user "%s" with LDAP', $username)); if ($userdn === TRUE) { return TRUE; } return static::synchroniseUser($userdn, $username); } else { static::$lastAuthenticationDiagnostic = Ldap::getInstance()->getLastBindDiagnostic(); if (!empty(static::$lastAuthenticationDiagnostic)) { static::getLogger()->notice(static::$lastAuthenticationDiagnostic); } } // LDAP authentication failed. Ldap::getInstance()->disconnect(); // This is a notice because it is fine to fallback to standard TYPO3 authentication static::getLogger()->notice(sprintf('Could not authenticate user "%s" with LDAP', $username)); return FALSE; } // LDAP authentication failed. static::getLogger()->warning('Cannot connect to LDAP or username is empty', array('username' => $username)); Ldap::getInstance()->disconnect(); return FALSE; }
/** * 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 the corresponding DN if a given user is provided, otherwise FALSE. * * @param string $username * @param string $password User's password. If NULL password will not be checked * @param string $baseDn * @param string $filter * @return bool|string */ public function validateUser($username = NULL, $password = NULL, $baseDn = NULL, $filter = NULL) { // If user found on ldap server. if ($this->ldapUtility->search($baseDn, str_replace('{USERNAME}', $username, $filter), array('dn'))) { // Validate with password. if ($password !== NULL) { // Bind DN of user with password. if (empty($password)) { $this->lastBindDiagnostic = 'Empty password provided!'; return FALSE; } elseif ($this->ldapUtility->bind($this->ldapUtility->getDn(), $password)) { $dn = $this->ldapUtility->getDn(); // Restore last LDAP binding $config = Configuration::getLdapConfiguration(); $this->ldapUtility->bind($config['binddn'], $config['password']); $this->lastBindDiagnostic = ''; return $dn; } else { $status = $this->ldapUtility->getStatus(); $this->lastBindDiagnostic = $status['bind']['diagnostic']; return FALSE; // Password does not match } // If enable, SSO authentication without password } elseif ($password === NULL && Configuration::getValue('SSOAuthentication')) { return $this->ldapUtility->getDn(); } else { // User invalid. Authentication failed. return FALSE; } } return FALSE; }
/** * 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; }