/** * Query the LDAP directory with the given filter. * * @param string $filter The string to filter by, e.g. (objectClass=user) * @param null|string $baseDn The DN to search from. Default is the baseDn option in the connection if not given * @param int $scope The scope to perform the search. Zend_Ldap::SEARCH_SCOPE_ONE, Zend_LDAP::SEARCH_SCOPE_BASE. Default is Zend_Ldap::SEARCH_SCOPE_SUB * @param array $attributes Restrict to specific AD attributes. An empty array will return all attributes * @param string $sort Sort results by this attribute if given * @return array */ protected function search($filter, $baseDn = null, $scope = Zend\Ldap\Ldap::SEARCH_SCOPE_SUB, $attributes = array(), $sort = '') { $records = $this->ldap->search($filter, $baseDn, $scope, $attributes, $sort); $results = array(); foreach ($records as $record) { foreach ($record as $attribute => $value) { // if the value is an array with a single value, e.g. 'samaccountname' => array(0 => 'myusername') // then make sure it's just set in the results as 'samaccountname' => 'myusername' so that it // can be used directly by ArrayData if (is_array($value) && count($value) == 1) { $value = $value[0]; } // ObjectGUID and ObjectSID attributes are in binary, we need to convert those to strings if ($attribute == 'objectguid') { $value = LDAPUtil::bin_to_str_guid($value); } if ($attribute == 'objectsid') { $value = LDAPUtil::bin_to_str_sid($value); } $record[$attribute] = $value; } $results[] = $record; } return $results; }
/** * Assertion Consumer Service * * The user gets sent back here after authenticating with the IdP, off-site. * The earlier redirection to the IdP can be found in the SAMLAuthenticator::authenticate. * * After this handler completes, we end up with a rudimentary Member record (which will be created on-the-fly * if not existent), with the user already logged in. Login triggers memberLoggedIn hooks, which allows * LDAP side of this module to finish off loading Member data. * * @throws OneLogin_Saml2_Error */ public function acs() { $auth = Injector::inst()->get('SAMLHelper')->getSAMLAuth(); $auth->processResponse(); $error = $auth->getLastErrorReason(); if (!empty($error)) { SS_Log::log($error, SS_Log::ERR); Form::messageForForm("SAMLLoginForm_LoginForm", "Authentication error: '{$error}'", 'bad'); Session::save(); return $this->getRedirect(); } if (!$auth->isAuthenticated()) { Form::messageForForm("SAMLLoginForm_LoginForm", _t('Member.ERRORWRONGCRED'), 'bad'); Session::save(); return $this->getRedirect(); } $decodedNameId = base64_decode($auth->getNameId()); // check that the NameID is a binary string (which signals that it is a guid if (ctype_print($decodedNameId)) { Form::messageForForm("SAMLLoginForm_LoginForm", "Name ID provided by IdP is not a binary GUID.", 'bad'); Session::save(); return $this->getRedirect(); } // transform the NameId to guid $guid = LDAPUtil::bin_to_str_guid($decodedNameId); if (!LDAPUtil::validGuid($guid)) { $errorMessage = "Not a valid GUID '{$guid}' recieved from server."; SS_Log::log($errorMessage, SS_Log::ERR); Form::messageForForm("SAMLLoginForm_LoginForm", $errorMessage, 'bad'); Session::save(); return $this->getRedirect(); } // Write a rudimentary member with basic fields on every login, so that we at least have something // if LDAP synchronisation fails. $member = Member::get()->filter('GUID', $guid)->limit(1)->first(); if (!($member && $member->exists())) { $member = new Member(); $member->GUID = $guid; } $attributes = $auth->getAttributes(); foreach ($member->config()->claims_field_mappings as $claim => $field) { if (!isset($attributes[$claim][0])) { SS_Log::log(sprintf('Claim rule \'%s\' configured in LDAPMember.claims_field_mappings, but wasn\'t passed through. Please check IdP claim rules.', $claim), SS_Log::WARN); continue; } $member->{$field} = $attributes[$claim][0]; } $member->SAMLSessionIndex = $auth->getSessionIndex(); // This will throw an exception if there are two distinct GUIDs with the same email address. // We are happy with a raw 500 here at this stage. $member->write(); // This will trigger LDAP update through LDAPMemberExtension::memberLoggedIn. // Both SAML and LDAP identify Members by the GUID field. $member->logIn(); return $this->getRedirect(); }