/** * testObservers * * @return void */ public function testObservers() { $event1 = array('name' => 'foo1', 'data' => 'bar1'); $event2 = array('name' => 'foo2', 'data' => 'bar2'); $mock = new OpenID_Observer_Mock(); OpenID::attach($mock); // Test skipping existing observers OpenID::attach($mock); try { OpenID::setLastEvent($event1['name'], $event1['data']); // should not execute $this->assertTrue(false); } catch (OpenID_Exception $e) { } $this->assertSame($event1, OpenID::getLastEvent()); OpenID::detach($mock); // Test skipping missing observers OpenID::detach($mock); OpenID::setLastEvent($event2['name'], $event2['data']); $this->assertSame($event2, OpenID::getLastEvent()); }
/** * Checks the signature of an OpenID_Message using this association * * @param OpenID_Message $message Instance of OpenID_Message * * @throws OpenID_Association_Exception if the handles don't match * @return bool true if the signatures match, false otherwise */ public function checkMessageSignature(OpenID_Message $message) { // Make sure the handles match for this OP and response if ($this->assocHandle != $message->get('openid.assoc_handle')) { throw new OpenID_Association_Exception('Association handles do not match'); } // Make sure the OP Endpoints match for this association and response if ($this->uri != $message->get('openid.op_endpoint')) { throw new OpenID_Association_Exception('Endpoint URLs do not match'); } if (!strlen($message->get('openid.signed'))) { OpenID::setLastEvent(__METHOD__, 'openid.signed is empty'); return false; } $list = explode(',', $message->get('openid.signed')); // Create a message with only keys in the signature $signedOnly = $this->getMessageForSigning($message); $signedOnlyDigest = base64_encode($this->hashHMAC($signedOnly)); $event = array('assocHandle' => $this->assocHandle, 'algo' => $this->getAlgorithm(), 'secret' => $this->sharedSecret, 'openid.sig' => $message->get('openid.sig'), 'signature' => $signedOnlyDigest, 'SignedKVFormat' => $signedOnly, 'MessageHTTPFormat' => $message->getHTTPFormat(), 'phpInput' => file_get_contents('php://input')); OpenID::setLastEvent(__METHOD__, print_r($event, true)); return $signedOnlyDigest == $message->get('openid.sig'); }
/** * Adds a nonce to the openid.return_to URL parameter. Only used in OpenID 1.1 * * @return void */ protected function addNonce() { $nonce = $this->getNonce()->createNonceAndStore(); $returnToURL = new Net_URL2($this->message->get('openid.return_to')); $returnToURL->setQueryVariable(OpenID_Nonce::RETURN_TO_NONCE, urlencode($nonce)); $this->message->set('openid.return_to', $returnToURL->getURL()); // Observing $logMessage = "Nonce: {$nonce}\n"; $logMessage = 'New ReturnTo: ' . $returnToURL->getURL() . "\n"; $logMessage .= 'OP URIs: ' . print_r($this->serviceEndpoint->getURIs(), true); OpenID::setLastEvent(__METHOD__, $logMessage); }
/** * Actually sends the assocition request to the OP Endpoing URL. * * @return OpenID_Message * @see associate() */ protected function sendAssociationRequest() { if ($this->message->get('openid.session_type') == self::SESSION_TYPE_NO_ENCRYPTION) { $this->message->delete('openid.dh_consumer_public'); $this->message->delete('openid.dh_modulus'); $this->message->delete('openid.dh_gen'); } else { $this->initDH(); } $response = $this->directRequest($this->opEndpointURL, $this->message); $message = new OpenID_Message($response->getBody(), OpenID_Message::FORMAT_KV); OpenID::setLastEvent(__METHOD__, print_r($message->getArrayFormat(), true)); return $message; }
/** * Validates the nonce embedded in the openid.return_to paramater and deletes * it from storage.. (For use with OpenID 1.1 only) * * @return void * @throws OpenID_Assertion_Exception on invalid or non-existing nonce */ protected function validateReturnToNonce() { $returnTo = $this->message->get('openid.return_to'); if ($returnTo === null) { // Must be a checkid_immediate negative assertion. $rtURL2 = new Net_URL2($this->message->get('openid.user_setup_url')); $rtqs = $rtURL2->getQueryVariables(); $returnTo = $rtqs['openid.return_to']; $identity = $rtqs['openid.identity']; } $netURL = new Net_URL2($returnTo); $qs = $netURL->getQueryVariables(); if (!array_key_exists(OpenID_Nonce::RETURN_TO_NONCE, $qs)) { throw new OpenID_Assertion_Exception('Missing OpenID 1.1 return_to nonce'); } if (!isset($identity)) { $identity = $this->message->get('openid.identity'); } $nonce = $qs[OpenID_Nonce::RETURN_TO_NONCE]; $discover = $this->getDiscover($identity); $endPoint = $discover->services[0]; $URIs = $endPoint->getURIs(); $opURL = array_shift($URIs); $fromStore = self::getStore()->getNonce(urldecode($nonce), $opURL); // Observing $logMessage = "returnTo: {$returnTo}\n"; $logMessage .= 'OP URIs: ' . print_r($endPoint->getURIs(), true) . "\n"; $logMessage .= 'Nonce in storage?: ' . var_export($fromStore, true) . "\n"; OpenID::setLastEvent(__METHOD__, $logMessage); if (!$fromStore) { throw new OpenID_Assertion_Exception('Invalid OpenID 1.1 return_to nonce in response'); } self::getStore()->deleteNonce($nonce, $opURL); }
/** * Verifies an assertion response from the OP. If the openid.mode is error, an * exception is thrown. * * @param Net_URL2 $requestedURL The requested URL (that the user was * directed to by the OP) as a Net_URL2 * object * @param OpenID_Message $message The OpenID_Message instance, as extractd * from the input (GET or POST) * * @throws OpenID_Exception on error or invalid openid.mode * @return OpenID_Assertion_Response */ public function verify(Net_URL2 $requestedURL, OpenID_Message $message) { // Unsolicited assertion? if ($this->normalizedID === null) { $unsolicitedID = $message->get('openid.claimed_id'); $this->normalizedID = OpenID::normalizeIdentifier($unsolicitedID); } $mode = $message->get('openid.mode'); $result = new OpenID_Assertion_Result(); OpenID::setLastEvent(__METHOD__, print_r($message->getArrayFormat(), true)); switch ($mode) { case OpenID::MODE_ID_RES: if ($message->get('openid.ns') === null && $message->get('openid.user_setup_url') !== null) { // Negative 1.1 checkid_immediate response $result->setAssertionMethod($mode); $result->setUserSetupURL($message->get('openid.user_setup_url')); return $result; } break; case OpenID::MODE_CANCEL: case OpenID::MODE_SETUP_NEEDED: $result->setAssertionMethod($mode); return $result; case OpenID::MODE_ERROR: throw new OpenID_Exception($message->get('openid.error')); default: throw new OpenID_Exception('Unknown mode: ' . $mode); } $discover = $this->getDiscover(); $serviceEndpoint = $discover->services[0]; $URIs = $serviceEndpoint->getURIs(); $opEndpointURL = array_shift($URIs); $assertion = $this->getAssertionObject($message, $requestedURL); $result->setDiscover($discover); // Check via associations if ($this->useAssociations) { if ($message->get('openid.invalidate_handle') === null) { // Don't fall back to check_authentication $result->setAssertionMethod(OpenID::MODE_ASSOCIATE); $assoc = $this->getStore()->getAssociation($opEndpointURL, $message->get('openid.assoc_handle')); OpenID::setLastEvent(__METHOD__, print_r($assoc, true)); if ($assoc instanceof OpenID_Association && $assoc->checkMessageSignature($message)) { $result->setAssertionResult(true); } // If it's not an unsolicited assertion, just return if (!isset($unsolicitedID)) { return $result; } } else { // Invalidate handle requested. Delete it and fall back to // check_authenticate $this->getStore()->deleteAssociation($opEndpointURL); } } // Check via check_authenticate $result->setAssertionMethod(OpenID::MODE_CHECK_AUTHENTICATION); $result->setCheckAuthResponse($assertion->checkAuthentication()); if ($result->getCheckAuthResponse()->get('is_valid') == 'true') { $result->setAssertionResult(true); } return $result; }