/** * @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); }
/** * Default constructor. * * @param \Causal\IgLdapSsoAuth\Domain\Model\Configuration $configuration * @param string $context */ public function __construct(\Causal\IgLdapSsoAuth\Domain\Model\Configuration $configuration, $context) { // Load the configuration Configuration::initialize($context, $configuration); // Store current context and get related configuration $this->context = $context; $this->configuration = strtolower($context) === 'fe' ? Configuration::getFrontendConfiguration() : Configuration::getBackendConfiguration(); // Define related tables if ($context === 'be') { $this->userTable = 'be_users'; $this->groupTable = 'be_groups'; } else { $this->userTable = 'fe_users'; $this->groupTable = 'fe_groups'; } }
/** * 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; }
/** * Actual import of user groups using AJAX. * * @param \Causal\IgLdapSsoAuth\Domain\Model\Configuration $configuration * @param string $mode * @param string $dn * @return void */ public function importUserGroupsAjaxAction(\Causal\IgLdapSsoAuth\Domain\Model\Configuration $configuration = NULL, $mode, $dn) { $data = array(); Configuration::initialize($mode, $configuration); $config = $mode === 'be' ? Configuration::getBackendConfiguration() : Configuration::getFrontendConfiguration(); try { $success = $this->ldap->connect(Configuration::getLdapConfiguration()); } catch (\Exception $e) { $data['message'] = $e->getMessage(); $success = FALSE; } if ($success) { list($filter, $baseDn) = explode(',', $dn, 2); $ldapGroup = $this->ldap->search($baseDn, '(' . $filter . ')', array(), TRUE); $pid = Configuration::getPid($config['groups']['mapping']); $table = $mode === 'be' ? 'be_groups' : 'fe_groups'; $typo3Groups = Authentication::getTypo3Groups(array($ldapGroup), $table, $pid); // Merge LDAP and TYPO3 information $group = Authentication::merge($ldapGroup, $typo3Groups[0], $config['groups']['mapping']); if ((int) $group['uid'] === 0) { $group = Typo3GroupRepository::add($table, $group); } else { // Restore group that may have been previously deleted $group['deleted'] = 0; $success = Typo3GroupRepository::update($table, $group); } if (!empty($config['groups']['mapping']['parentGroup'])) { $fieldParent = $config['groups']['mapping']['parentGroup']; if (preg_match("`<([^\$]*)>`", $fieldParent, $attribute)) { $fieldParent = $attribute[1]; if (is_array($group[$fieldParent])) { unset($group[$fieldParent]['count']); $this->setParentGroup($group[$fieldParent], $fieldParent, $group['uid'], $pid, $mode); } } } $data['id'] = (int) $group['uid']; } $this->returnAjax(array_merge($data, array('success' => $success))); }