authSource = new SimpleSAML_Auth_Simple($config->getString('auth')); $this->usernameAttribute = $config->getString('username_attribute'); $this->authProc = array( 'authproc' => $config->getArray('authproc', array())); try { $store = new Auth_OpenID_FileStore($config->getString('filestore')); $this->server = new Auth_OpenID_Server($store, $this->getServerURL()); } catch (Exception $e) { throw $e; } $this->trustStoreDir = realpath($config->getString('filestore')) . '/truststore'; if (!is_dir($this->trustStoreDir)) { $res = mkdir($this->trustStoreDir, 0777, TRUE); if (!$res) { throw new SimpleSAML_Error_Exception('Failed to create directory: ' . $this->trustStoreDir); } } } /** * Retrieve the authentication source used by the OpenID Provider. * * @return SimpleSAML_Auth_Simple The authentication source. */ public function getAuthSource() { return $this->authSource; } /** * Retrieve the current user ID. * * @return string The current user ID, or NULL if the user isn't authenticated. */ public function getUserId() { if (!$this->authSource->isAuthenticated()) { return NULL; } $attributes = $this->authSource->getAttributes(); if (!array_key_exists($this->usernameAttribute, $attributes)) { throw new SimpleSAML_Error_Exception('Missing username attribute ' . var_export($this->usernameAttribute, TRUE) . ' in the attributes of the user.'); } $values = array_values($attributes[$this->usernameAttribute]); if (empty($values)) { throw new SimpleSAML_Error_Exception('Username attribute was empty.'); } if (count($values) > 1) { throw new SimpleSAML_Error_Exception('More than one attribute value in username.'); } $userId = $values[0]; return $userId; } /** * Retrieve the current identity. * * @return string The current identity, or NULL if the user isn't authenticated. */ public function getIdentity() { $userId = $this->getUserId(); if ($userId === NULL) { return NULL; } $identity = SimpleSAML_Module::getModuleURL('openidProvider/user.php/' . $userId); return $identity; } /** * Retrieve the URL of the server. * * @return string The URL of the OpenID server. */ public function getServerURL() { return SimpleSAML_Module::getModuleURL('openidProvider/server.php'); } /** * Get the file that contains the trust roots for the user. * * @param string $identity The identity of the user. * @return string The file name. */ private function getTrustFile($identity) { assert('is_string($identity)'); $path = $this->trustStoreDir . '/' . sha1($identity) . '.serialized'; return $path; } /** * Get the sites the user trusts. * * @param string $identity The identity of the user. * @param array $trustRoots The trust roots the user trusts. */ public function saveTrustRoots($identity, array $trustRoots) { assert('is_string($identity)'); $file = $this->getTrustFile($identity); $tmpFile = $file . '.new.' . getmypid(); $data = serialize($trustRoots); $ok = file_put_contents($tmpFile, $data); if ($ok === FALSE) { throw new SimpleSAML_Error_Exception('Failed to save file ' . var_export($tmpFile, TRUE)); } $ok = rename($tmpFile, $file); if ($ok === FALSE) { throw new SimpleSAML_Error_Exception('Failed rename ' . var_export($tmpFile, TRUE) . ' to ' . var_export($file, TRUE) . '.'); } } /** * Get the sites the user trusts. * * @param string $identity The identity of the user. * @return array The trust roots the user trusts. */ public function getTrustRoots($identity) { assert('is_string($identity)'); $file = $this->getTrustFile($identity); if (!file_exists($file)) { return array(); } $data = file_get_contents($file); if ($data === FALSE) { throw new SimpleSAML_Error_Exception('Failed to load file ' . var_export($file, TRUE). '.'); } $data = unserialize($data); if ($data === FALSE) { throw new SimpleSAML_Error_Exception('Error unserializing trust roots from file ' . var_export($file, TRUE) . '.'); } return $data; } /** * Add the given trust root to the user. * * @param string $identity The identity of the user. * @param string $trustRoot The trust root. */ public function addTrustRoot($identity, $trustRoot) { assert('is_string($identity)'); assert('is_string($trustRoot)'); $trs = $this->getTrustRoots($identity); if (!in_array($trustRoot, $trs, TRUE)) { $trs[] = $trustRoot; } $this->saveTrustRoots($identity, $trs); } /** * Remove the given trust root from the trust list of the user. * * @param string $identity The identity of the user. * @param string $trustRoot The trust root. */ public function removeTrustRoot($identity, $trustRoot) { assert('is_string($identity)'); assert('is_string($trustRoot)'); $trs = $this->getTrustRoots($identity); $i = array_search($trustRoot, $trs, TRUE); if ($i === FALSE) { return; } array_splice($trs, $i, 1, array()); $this->saveTrustRoots($identity, $trs); } /** * Is the given trust root trusted by the user? * * @param string $identity The identity of the user. * @param string $trustRoot The trust root. * @return TRUE if it is trusted, FALSE if not. */ private function isTrusted($identity, $trustRoot) { assert('is_string($identity)'); assert('is_string($trustRoot)'); $trs = $this->getTrustRoots($identity); return in_array($trustRoot, $trs, TRUE); } /** * Save the state, and return a URL that can contain a reference to the state. * * @param string $page The name of the page. * @param array $state The state array. * @return string A URL with the state ID as a parameter. */ private function getStateURL($page, array $state) { assert('is_string($page)'); $stateId = SimpleSAML_Auth_State::saveState($state, 'openidProvider:resumeState'); $stateURL = SimpleSAML_Module::getModuleURL('openidProvider/' . $page); $stateURL = SimpleSAML_Utilities::addURLparameter($stateURL, array('StateID' => $stateId)); return $stateURL; } /** * Retrieve state by ID. * * @param string $stateId The state ID. * @return array The state array. */ public function loadState($stateId) { assert('is_string($stateId)'); // sanitize the input $sid = SimpleSAML_Utilities::parseStateID($stateId); if (!is_null($sid['url'])) { SimpleSAML_Utilities::checkURLAllowed($sid['url']); } return SimpleSAML_Auth_State::loadState($stateId, 'openidProvider:resumeState'); } /** * Send an OpenID response. * * This function never returns. * * @param Auth_OpenID_ServerResponse $response The response. */ private function sendResponse(Auth_OpenID_ServerResponse $response) { SimpleSAML_Logger::debug('openidProvider::sendResponse'); $webresponse = $this->server->encodeResponse($response); if ($webresponse->code !== 200) { header('HTTP/1.1 ' . $webresponse->code, TRUE, $webresponse->code); } foreach ($webresponse->headers as $k => $v) { header($k . ': ' . $v); } header('Connection: Close'); print($webresponse->body); exit(0); } /** * Process a request. * * This function never returns. * * @param Auth_OpenID_Request $request The request we are processing. */ public function processRequest(array $state) { assert('isset($state["request"])'); $request = $state['request']; $sreg_req = Auth_OpenID_SRegRequest::fromOpenIDRequest($request); $ax_req = Auth_OpenId_AX_FetchRequest::fromOpenIDRequest($request); /* In resume.php there should be a way to display data requested through sreg or ax. */ if (!$this->authSource->isAuthenticated()) { if ($request->immediate) { /* Not logged in, and we cannot show a login form. */ $this->sendResponse($request->answer(FALSE)); } $resumeURL = $this->getStateURL('resume.php', $state); $this->authSource->requireAuth(array('ReturnTo' => $resumeURL)); } $identity = $this->getIdentity(); assert('$identity !== FALSE'); /* Should always be logged in here. */ if (!$request->idSelect() && $identity !== $request->identity) { /* The identity in the request doesn't match the one of the logged in user. */ throw new SimpleSAML_Error_Exception('Logged in as different user than the one requested.'); } if ($this->isTrusted($identity, $request->trust_root)) { $trusted = TRUE; } elseif (isset($state['TrustResponse'])) { $trusted = (bool)$state['TrustResponse']; } else { if ($request->immediate) { /* Not trusted, and we cannot show a trust-form. */ $this->sendResponse($request->answer(FALSE)); } $trustURL = $this->getStateURL('trust.php', $state); SimpleSAML_Utilities::redirectTrustedURL($trustURL); } if (!$trusted) { /* The user doesn't trust this site. */ $this->sendResponse($request->answer(FALSE)); } $response = $request->answer(TRUE, NULL, $identity); //Process attributes $attributes = $this->authSource->getAttributes(); foreach ($attributes as $key=>$attr) { if (is_array($attr) && count($attr) === 1) { $attributes[$key] = $attr[0]; } } $pc = new SimpleSAML_Auth_ProcessingChain($this->authProc, array(), 'idp'); $state = array( 'Attributes'=>$attributes, 'isPassive'=>TRUE ); $pc->processStatePassive($state); $attributes = $state['Attributes']; //Process SREG requests $sreg_resp = Auth_OpenID_SRegResponse::extractResponse($sreg_req, $attributes); $sreg_resp->toMessage($response->fields); //Process AX requests $ax_resp = new Auth_OpenID_AX_FetchResponse(); foreach($ax_req->iterTypes() as $type_uri) { if(isset($attributes[$type_uri])) { $ax_resp->addValue($type_uri, $attributes[$type_uri]); } } $ax_resp->toMessage($response->fields); /* The user is authenticated, and trusts this site. */ $this->sendResponse($response); } /** * Receive an incoming request. * * This function never returns. */ public function receiveRequest() { $request = $this->server->decodeRequest(); if (!in_array($request->mode, array('checkid_immediate', 'checkid_setup'), TRUE)) { $this->sendResponse($this->server->handleRequest($request)); } $state = array( 'request' => $request, ); $this->processRequest($state); } }