Ejemplo n.º 1
0
 /**
  * @test
  * @dataProvider usernameSsoProvider
  */
 public function validateUserSupportsSSO($filter, $username, $expected)
 {
     $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['ig_ldap_sso_auth'] = serialize(array('enableFESSO' => 1));
     \Causal\IgLdapSsoAuth\Library\Configuration::initialize('fe', new \Causal\IgLdapSsoAuth\Domain\Model\Configuration());
     $result = $this->fixture->validateUser($username, NULL, 'cn=read-only-admin,dc=example,dc=com', $filter);
     $this->assertEquals($expected, $result);
 }
Ejemplo n.º 2
0
 /**
  * Hooks into \TYPO3\CMS\Core\DataHandling\DataHandler after records have been saved to the database.
  *
  * @param string $operation
  * @param string $table
  * @param mixed $id
  * @param array $fieldArray
  * @param \TYPO3\CMS\Core\DataHandling\DataHandler $pObj
  * @return void
  */
 public function processDatamap_afterDatabaseOperations($operation, $table, $id, array $fieldArray, \TYPO3\CMS\Core\DataHandling\DataHandler $pObj)
 {
     if ($table !== 'tx_igldapssoauth_config') {
         // Early return
         return;
     }
     if ($operation === 'new' && !is_numeric($id)) {
         $id = $pObj->substNEWwithIDs[$id];
     }
     $row = \TYPO3\CMS\Backend\Utility\BackendUtility::getRecord($table, $id);
     if ($row['group_membership'] == Configuration::GROUP_MEMBERSHIP_FROM_MEMBER) {
         $warningMessageKeys = array();
         if (!empty($row['be_users_basedn']) && !empty($row['be_groups_basedn'])) {
             // Check backend mapping
             $mapping = Configuration::parseMapping($row['be_users_mapping']);
             if (!isset($mapping['usergroup'])) {
                 $warningMessageKeys[] = 'tx_igldapssoauth_config.group_membership.fe.missingUsergroupMapping';
             }
         }
         if (!empty($row['fe_users_basedn']) && !empty($row['fe_groups_basedn'])) {
             // Check frontend mapping
             $mapping = Configuration::parseMapping($row['fe_users_mapping']);
             if (!isset($mapping['usergroup'])) {
                 $warningMessageKeys[] = 'tx_igldapssoauth_config.group_membership.be.missingUsergroupMapping';
             }
         }
         foreach ($warningMessageKeys as $key) {
             /** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
             $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $this->getLanguageService()->sL('LLL:EXT:ig_ldap_sso_auth/Resources/Private/Language/locallang_db.xlf:' . $key, TRUE), '', \TYPO3\CMS\Core\Messaging\FlashMessage::WARNING, TRUE);
             /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
             $flashMessageService = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessageService');
             /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
             $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
             $defaultFlashMessageQueue->enqueue($flashMessage);
         }
     }
 }
Ejemplo n.º 3
0
 /**
  * Authenticates a user (Check various conditions for the user that might invalidate its
  * authentication, e.g., password match, domain, IP, etc.).
  *
  * @param array $user Data of user.
  * @return int|FALSE
  */
 public function authUser(array $user)
 {
     if (!Configuration::isInitialized()) {
         // Early return since LDAP is not configured
         return static::STATUS_AUTHENTICATION_FAILURE_CONTINUE;
     }
     if (TYPO3_MODE === 'BE') {
         $status = Configuration::getValue('BEfailsafe') ? static::STATUS_AUTHENTICATION_FAILURE_CONTINUE : static::STATUS_AUTHENTICATION_FAILURE_BREAK;
     } else {
         $status = static::STATUS_AUTHENTICATION_FAILURE_CONTINUE;
     }
     $enableFrontendSso = TYPO3_MODE === 'FE' && (bool) $this->config['enableFESSO'] && !empty($_SERVER['REMOTE_USER']);
     if (($this->login['uident'] && $this->login['uname'] || $enableFrontendSso) && !empty($user['tx_igldapssoauth_dn'])) {
         if (isset($user['tx_igldapssoauth_from'])) {
             $status = static::STATUS_AUTHENTICATION_SUCCESS_BREAK;
         } elseif (TYPO3_MODE === 'BE' && Configuration::getValue('BEfailsafe')) {
             return static::STATUS_AUTHENTICATION_FAILURE_CONTINUE;
         } else {
             // Failed login attempt (wrong password) - write that to the log!
             static::getLogger()->warning('Password not accepted: ' . array('username' => $this->login['uname'], 'remote' => sprintf('%s (%s)', $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'])));
             $status = static::STATUS_AUTHENTICATION_FAILURE_BREAK;
         }
         // Checking the domain (lockToDomain)
         if ($status && $user['lockToDomain'] && $user['lockToDomain'] != $this->authInfo['HTTP_HOST']) {
             // Lock domain didn't match, so error:
             static::getLogger()->error(sprintf('Locked domain "%s" did not match "%s"', $user['lockToDomain'], $this->authInfo['HTTP_HOST']), array('username' => $user[$this->db_user['username_column']], 'remote' => sprintf('%s (%s)', $this->authInfo['REMOTE_ADDR'], $this->authInfo['REMOTE_HOST'])));
             $status = static::STATUS_AUTHENTICATION_FAILURE_BREAK;
         }
     }
     return $status;
 }
Ejemplo n.º 4
0
    /**
     * @test
     */
    public function mappingWithTypoScriptIsExtended()
    {
        $mapping = <<<EOT
\t\t\tname = <sn>, <givenName>
\t\t\tname.wrap = |
EOT;
        $mapping = Configuration::parseMapping($mapping);
        $result = Configuration::hasExtendedMapping($mapping);
        $this->assertTrue($result);
    }
Ejemplo n.º 5
0
 /**
  * Merges a field from LDAP into a TYPO3 record.
  *
  * @param array $ldap
  * @param array $typo3
  * @param string $field
  * @param string $value
  * @return array Modified $typo3 array
  * @throws \UnexpectedValueException
  */
 protected static function mergeSimple(array $ldap, array $typo3, $field, $value)
 {
     // Standard marker or custom function
     if (preg_match("`{([^\$]*)}`", $value, $matches)) {
         switch ($value) {
             case '{DATE}':
                 $mappedValue = $GLOBALS['EXEC_TIME'];
                 break;
             case '{RAND}':
                 $mappedValue = rand();
                 break;
             default:
                 $mappedValue = '';
                 $parameters = explode(';', $matches[1]);
                 $hookParameters = array();
                 foreach ($parameters as $parameter) {
                     list($parameterKey, $parameterValue) = explode('|', $parameter, 2);
                     $hookParameters[trim($parameterKey)] = $parameterValue;
                 }
                 if (empty($hookParameters['hookName'])) {
                     throw new \UnexpectedValueException(sprintf('Custom marker hook parameter "hookName" is undefined: %s', $matches[0]), 1430138379);
                 }
                 $hookName = $hookParameters['hookName'];
                 $ldapAttributes = Configuration::getLdapAttributes(array($value));
                 // hook for processing user information once inserted or updated in the database
                 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ig_ldap_sso_auth']['extraMergeField']) && !empty($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ig_ldap_sso_auth']['extraMergeField'][$hookName])) {
                     $_procObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['ig_ldap_sso_auth']['extraMergeField'][$hookName]);
                     if (!is_callable(array($_procObj, 'extraMerge'))) {
                         throw new \UnexpectedValueException(sprintf('Custom marker hook "%s" does not have a method "extraMerge"', $hookName), 1430140817);
                     }
                     $mappedValue = $_procObj->extraMerge($field, $typo3, $ldap, $ldapAttributes, $hookParameters);
                 }
                 break;
         }
         // LDAP attribute
     } elseif (preg_match("`<([^\$]*)>`", $value, $attribute)) {
         if ($field === 'tx_igldapssoauth_dn' || $field === 'title' && $value === '<dn>') {
             $mappedValue = $ldap[strtolower($attribute[1])];
         } else {
             $mappedValue = static::replaceLdapMarkers($value, $ldap);
         }
         // Constant
     } else {
         $mappedValue = $value;
     }
     // If field exists in TYPO3, set it to the mapped value
     if (array_key_exists($field, $typo3)) {
         $typo3[$field] = $mappedValue;
         // Otherwise, it is some extra value, which we store in a special sub-array
         // This may be data that is meant to be mapped onto other database tables
     } else {
         if (!isset($typo3['__extraData'])) {
             $typo3['__extraData'] = array();
         }
         $typo3['__extraData'][$field] = $mappedValue;
     }
     return $typo3;
 }
Ejemplo n.º 6
0
 /**
  * Processes the username according to current configuration.
  *
  * @param string $username
  * @return string
  */
 public static function setUsername($username)
 {
     if (Configuration::getValue('forceLowerCaseUsername')) {
         // Possible enhancement: use \TYPO3\CMS\Core\Charset\CharsetConverter::conv_case instead
         $username = strtolower($username);
     }
     return $username;
 }
Ejemplo n.º 7
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);
    }
Ejemplo n.º 8
0
 /**
  * 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;
 }
Ejemplo n.º 9
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;
 }
Ejemplo n.º 10
0
 /**
  * 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;
 }
Ejemplo n.º 11
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;
 }