/**
  * Find a user (eg. look up the user record in database when a login is sent)
  *
  * @return mixed user array or FALSE
  * @throws UnsupportedLoginSecurityLevelException
  */
 public function getUser()
 {
     $user = FALSE;
     $userRecordOrIsValid = FALSE;
     $enableFrontendSso = TYPO3_MODE === 'FE' && (bool) $this->config['enableFESSO'] && !empty($_SERVER['REMOTE_USER']);
     // This simple check is the key to prevent your log being filled up with warnings
     // due to the AJAX calls to maintain the session active if your configuration forces
     // the authentication stack to always fetch the user:
     // $TYPO3_CONF_VARS['SVCONF']['auth']['setup']['BE_alwaysFetchUser'] = true;
     // This is the case, e.g., when using EXT:crawler.
     if ($this->login['status'] !== 'login' && !$enableFrontendSso) {
         return $user;
     }
     /** @var \Causal\IgLdapSsoAuth\Domain\Repository\ConfigurationRepository $configurationRepository */
     $configurationRepository = GeneralUtility::makeInstance('Causal\\IgLdapSsoAuth\\Domain\\Repository\\ConfigurationRepository');
     $configurationRecords = $configurationRepository->findAll();
     if (count($configurationRecords) === 0) {
         // Early return since LDAP is not configured
         static::getLogger()->warning('Skipping LDAP authentication as extension is not yet configured');
         $this->cleanUpExtbaseDataMapper();
         return FALSE;
     }
     foreach ($configurationRecords as $configurationRecord) {
         Configuration::initialize(TYPO3_MODE, $configurationRecord);
         if (!Configuration::isEnabledForCurrentHost()) {
             $msg = sprintf('Configuration record #%s is not enabled for domain %s', $configurationRecord->getUid(), GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'));
             static::getLogger()->info($msg);
             continue;
         }
         // Enable feature
         $userRecordOrIsValid = FALSE;
         // Single Sign-On authentication
         if ($enableFrontendSso) {
             $remoteUser = $_SERVER['REMOTE_USER'];
             // Strip the domain name
             if ($pos = strpos($remoteUser, '@')) {
                 $remoteUser = substr($remoteUser, 0, $pos);
             } elseif ($pos = strrpos($remoteUser, '\\')) {
                 $remoteUser = substr($remoteUser, $pos + 1);
             }
             $userRecordOrIsValid = Authentication::ldapAuthenticate($remoteUser);
             if (is_array($userRecordOrIsValid)) {
                 // Useful for debugging purpose
                 $this->login['uname'] = $remoteUser;
             }
             // Authenticate user from LDAP
         } elseif ($this->login['status'] === 'login' && $this->login['uident']) {
             // Configuration of authentication service.
             $loginSecurityLevel = $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['loginSecurityLevel'];
             // normal case
             // Check if $loginSecurityLevel is set to "challenged" or "superchallenged" and throw an error if the configuration allows it
             // By default, it will not throw an exception
             if (isset($this->config['throwExceptionAtLogin']) && $this->config['throwExceptionAtLogin'] == 1) {
                 if ($loginSecurityLevel === 'challenged' || $loginSecurityLevel === 'superchallenged') {
                     $message = "ig_ldap_sso_auth error: current login security level '" . $loginSecurityLevel . "' is not supported.";
                     $message .= " Try to use 'normal' or 'rsa' (highly recommended): ";
                     $message .= "\$GLOBALS['TYPO3_CONF_VARS']['" . TYPO3_MODE . "']['loginSecurityLevel'] = 'rsa';";
                     $this->cleanUpExtbaseDataMapper();
                     throw new UnsupportedLoginSecurityLevelException($message, 1324313489);
                 }
             }
             // normal case
             $password = $this->login['uident_text'];
             try {
                 if ($password !== NULL) {
                     $userRecordOrIsValid = Authentication::ldapAuthenticate($this->login['uname'], $password);
                 } else {
                     // Could not decrypt password
                     $userRecordOrIsValid = FALSE;
                 }
             } catch (UnresolvedPhpDependencyException $e) {
                 // Possible known exception: 1409566275, LDAP extension is not available for PHP
                 $userRecordOrIsValid = FALSE;
             }
         }
         if (is_array($userRecordOrIsValid)) {
             $user = $userRecordOrIsValid;
             break;
         } elseif ($userRecordOrIsValid) {
             // Authentication is valid
             break;
         } else {
             $diagnostic = Authentication::getLastAuthenticationDiagnostic();
             $info = array('username' => $this->login['uname'], 'remote' => sprintf('%s (%s)', $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST']), 'diagnostic' => $diagnostic, 'configUid' => $configurationRecord->getUid());
             static::getLogger()->error('Authentication failed', $info);
             NotificationUtility::dispatch(__CLASS__, 'authenticationFailed', $info);
         }
         // Continue and try with next configuration record...
     }
     if (!$user && $userRecordOrIsValid) {
         $user = $this->fetchUserRecord($this->login['uname']);
     }
     if (is_array($user)) {
         static::getLogger()->debug(sprintf('User found: "%s"', $this->login['uname']));
     }
     $this->cleanUpExtbaseDataMapper();
     return $user;
 }
Example #2
0
    /**
     * @test
     */
    public function parentGroupIsNotMerged()
    {
        $mapping = <<<EOT
\t\t\tpid = 1
\t\t\ttitle = <cn>
\t\t\tdescription = LDAP Group <cn>
\t\t\tparentGroup = <memberOf>
EOT;
        $expected = $this->typo3GroupFixture;
        $expected['pid'] = '1';
        $expected['title'] = 'Scientists';
        $expected['description'] = 'LDAP Group Scientists';
        $mapping = Configuration::parseMapping($mapping);
        $group = Authentication::merge($this->ldapGroupFixture, $this->typo3GroupFixture, $mapping);
        $this->assertEquals($expected, $group);
    }
Example #3
0
 /**
  * Imports a given user to the TYPO3 database.
  *
  * @param array $user Local user information
  * @param array $ldapUser LDAP user information
  * @param string $restoreBehavior How to restore users (only for update)
  * @return array Modified user data
  * @throws ImportUsersException
  */
 public function import($user, $ldapUser, $restoreBehavior = 'both')
 {
     // Store the extra data for later restore and remove it
     if (isset($user['__extraData'])) {
         $extraData = $user['__extraData'];
         unset($user['__extraData']);
     }
     if (empty($user['uid'])) {
         // Set other necessary information for a new user
         // First make sure to be acting in the right context
         Configuration::setMode($this->context);
         $user['username'] = Typo3UserRepository::setUsername($user['username']);
         $user['password'] = Typo3UserRepository::setRandomPassword();
         $typo3Groups = Authentication::getUserGroups($ldapUser, $this->configuration, $this->groupTable);
         if ($typo3Groups === NULL) {
             // Required LDAP groups are missing: quit!
             return $user;
         }
         $user = Typo3UserRepository::setUserGroups($user, $typo3Groups);
         $user = Typo3UserRepository::add($this->userTable, $user);
         $this->usersAdded++;
     } else {
         // Restore user that may have been previously deleted or disabled, depending on chosen behavior
         // (default to both undelete and re-enable)
         switch ($restoreBehavior) {
             case 'enable':
                 $user[$GLOBALS['TCA'][$this->userTable]['ctrl']['enablecolumns']['disabled']] = 0;
                 break;
             case 'undelete':
                 $user[$GLOBALS['TCA'][$this->userTable]['ctrl']['delete']] = 0;
                 break;
             case 'nothing':
                 break;
             default:
                 $user[$GLOBALS['TCA'][$this->userTable]['ctrl']['enablecolumns']['disabled']] = 0;
                 $user[$GLOBALS['TCA'][$this->userTable]['ctrl']['delete']] = 0;
         }
         $typo3Groups = Authentication::getUserGroups($ldapUser, $this->configuration, $this->groupTable);
         $user = Typo3UserRepository::setUserGroups($user, $typo3Groups === NULL ? array() : $typo3Groups);
         $success = Typo3UserRepository::update($this->userTable, $user);
         if ($success) {
             $this->usersUpdated++;
         }
     }
     // Restore the extra data and trigger a signal
     if (isset($extraData)) {
         $user['__extraData'] = $extraData;
         // Hook for processing the extra data
         if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ig_ldap_sso_auth']['extraDataProcessing'])) {
             foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ig_ldap_sso_auth']['extraDataProcessing'] as $className) {
                 /** @var $postProcessor \Causal\IgLdapSsoAuth\Utility\ExtraDataProcessorInterface */
                 $postProcessor = GeneralUtility::getUserObj($className);
                 if ($postProcessor instanceof \Causal\IgLdapSsoAuth\Utility\ExtraDataProcessorInterface) {
                     $postProcessor->processExtraData($this->userTable, $user);
                 } else {
                     throw new ImportUsersException(sprintf('Invalid post-processing class %s. It must implement the \\Causal\\IgLdapSsoAuth\\Utility\\ExtraDataProcessorInterface interface', $className), 1414136057);
                 }
             }
         }
     }
     return $user;
 }
Example #4
0
 /**
  * Returns the LDAP user groups with information merged with local TYPO3 user groups.
  *
  * @param \Causal\IgLdapSsoAuth\Domain\Model\Configuration $configuration
  * @param string $mode
  * @return array
  */
 protected function getAvailableUserGroups(\Causal\IgLdapSsoAuth\Domain\Model\Configuration $configuration, $mode)
 {
     $userGroups = array();
     $config = $mode === 'be' ? Configuration::getBackendConfiguration() : Configuration::getFrontendConfiguration();
     $ldapGroups = array();
     if (!empty($config['groups']['basedn'])) {
         $filter = Configuration::replaceFilterMarkers($config['groups']['filter']);
         $attributes = Configuration::getLdapAttributes($config['groups']['mapping']);
         $ldapGroups = Ldap::getInstance()->search($config['groups']['basedn'], $filter, $attributes);
         unset($ldapGroups['count']);
     }
     // Populate an array of TYPO3 group records corresponding to the LDAP groups
     // If a given LDAP group has no associated group in TYPO3, a fresh record
     // will be created so that $ldapGroups[i] <=> $typo3Groups[i]
     $typo3GroupPid = Configuration::getPid($config['groups']['mapping']);
     $table = $mode === 'be' ? 'be_groups' : 'fe_groups';
     $typo3Groups = Authentication::getTypo3Groups($ldapGroups, $table, $typo3GroupPid);
     foreach ($ldapGroups as $index => $ldapGroup) {
         $userGroup = Authentication::merge($ldapGroup, $typo3Groups[$index], $config['groups']['mapping']);
         // Attempt to free memory by unsetting fields which are unused in the view
         $keepKeys = array('uid', 'pid', 'deleted', 'title', 'tx_igldapssoauth_dn');
         $keys = array_keys($userGroup);
         foreach ($keys as $key) {
             if (!in_array($key, $keepKeys)) {
                 unset($userGroup[$key]);
             }
         }
         $userGroups[] = $userGroup;
     }
     return $userGroups;
 }
Example #5
0
 /**
  * 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;
 }
 /**
  * Returns the title for a given user.
  *
  * @param array $ldap_user
  * @param array $mapping
  * @return null|string
  * @deprecated since 3.0, will be removed in 3.2
  */
 public static function get_title($ldap_user = array(), $mapping = array())
 {
     if (!$mapping) {
         return NULL;
     }
     if (isset($mapping['title']) && preg_match('`<([^$]*)>`', $mapping['title'], $attribute)) {
         if ($attribute[1] === 'dn') {
             return $ldap_user[$attribute[1]];
         }
         return Authentication::replaceLdapMarkers($mapping['title'], $ldap_user);
     }
     return NULL;
 }