You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by ch...@apache.org on 2008/08/01 21:17:25 UTC

svn commit: r681787 - /incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php

Author: chabotc
Date: Fri Aug  1 12:17:25 2008
New Revision: 681787

URL: http://svn.apache.org/viewvc?rev=681787&view=rev
Log:
SHINDIG-447 signing fetcher fix by Karsten Beyer, cleaned up to apply by Gonzalo Aune

Modified:
    incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php

Modified: incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php?rev=681787&r1=681786&r2=681787&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php (original)
+++ incubator/shindig/trunk/php/src/gadgets/SigningFetcher.php Fri Aug  1 12:17:25 2008
@@ -1,4 +1,4 @@
-<?php
+<?php
 /*
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -11,8 +11,8 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- */
-
+ */
+
 /**
  * Implements signed fetch based on the OAuth request signing algorithm.
  *
@@ -21,32 +21,30 @@
  *
  * Instances of this class are only accessed by a single thread at a time,
  * but instances may be created by multiple threads.
- */
-class SigningFetcher extends RemoteContentFetcher {
-
-	protected static $OPENSOCIAL_OWNERID = "opensocial_owner_id";
-	protected static $OPENSOCIAL_VIEWERID = "opensocial_viewer_id";
-	protected static $OPENSOCIAL_APPID = "opensocial_app_id";
-	protected static $XOAUTH_PUBLIC_KEY = "xoauth_signature_publickey";
-	protected static $ALLOWED_PARAM_NAME = "[-:\\w]+";
-	
-	//protected final TimeSource clock = new TimeSource();
-	
+ */
+class SigningFetcher extends RemoteContentFetcher {
+	
+	protected static $OPENSOCIAL_OWNERID = "opensocial_owner_id";
+	protected static $OPENSOCIAL_VIEWERID = "opensocial_viewer_id";
+	protected static $OPENSOCIAL_APPID = "opensocial_app_id";
+	protected static $XOAUTH_PUBLIC_KEY = "xoauth_signature_publickey";
+	protected static $ALLOWED_PARAM_NAME = '^[-_[:alnum:]]+$';	
+
 	/**
 	 * Authentication token for the user and gadget making the request.
-	 */
-	protected $authToken;
-	
+	 */
+	protected $authToken;
+	
 	/**
 	 * Private key we pass to the OAuth RSA_SHA1 algorithm.This can be a
 	 * PrivateKey object, or a PEM formatted private key, or a DER encoded byte
 	 * array for the private key.(No, really, they accept any of them.)
-	 */
-	protected $privateKeyObject;
-	
+	 */
+	protected $privateKeyObject;
+	
 	/**
 	 * The name of the key, included in the fetch to help with key rotation.
-	 */
+	 */
 	protected $keyName;
 
 	/**
@@ -91,11 +89,11 @@
 		$this->authToken = $authToken;
 		$this->keyName = $keyName;
 		$this->privateKeyObject = $privateKeyObject;
-	}
-
+	}
+
 	public function fetchRequest($request)
-	{
-		return $this->getNextFetcher()->fetchRequest($request);
+	{
+		return $this->getNextFetcher()->fetchRequest($request);
 	}
 
 	public function fetch($url, $method)
@@ -109,59 +107,74 @@
 		try {
 			// Parse the request into parameters for OAuth signing, stripping out
 			// any OAuth or OpenSocial parameters injected by the client
-			///////////////////////////////////////////////
 			$parsedUri = parse_url($url);
 			$resource = $url;
 			$queryParams = $this->sanitize($_GET);
 			$postParams = $this->sanitize($_POST);
+			// The data that is supposed to be posted to the target page is contained in the postData field
+			// in the $_POST to the Shindig proxy server
+			// Here we parse it and put it into the $postDataParams array which then is merged into the postParams
+			// to be used for the GET/POST request and the building of the signature
+			$postDataParams = array();			
+			if (isset($_POST['postData']) && count($postDataParts = split('&', urldecode($_POST['postData']))) > 0) {
+				foreach ($postDataParts as $postDataPart) {
+					$postDataPartsPair = split('=', $postDataPart);
+					if (count($postDataPartsPair) === 2) {
+						$postDataParams[$postDataPartsPair[0]] = $postDataPartsPair[1];
+					}
+				}
+			}
+			$postParams = array_merge($postParams, $this->sanitize($postDataParams));
 			$msgParams = array();
 			$msgParams = array_merge($msgParams, $queryParams);
 			$msgParams = array_merge($msgParams, $postParams);
-			
-			// TODO: is this ok?
-			//$msgParams = array();
-			$this->addOpenSocialParams($msgParams);		
+			$this->addOpenSocialParams($msgParams);
 			$this->addOAuthParams($msgParams);
-			
-			// Build and sign the OAuthMessage; note that the resource here has
-			// no query string, the parameters are all in msgParams
-			//$message  = new OAuthMessage($method, $resource, $msgParams);
-	
-			////////////////////////////////////////////////    
 			$consumer = new OAuthConsumer(NULL, NULL, NULL);
 			$consumer->setProperty(OAuthSignatureMethod_RSA_SHA1::$PRIVATE_KEY, $this->privateKeyObject);
 			$signatureMethod = new OAuthSignatureMethod_RSA_SHA1();
-			
 			$req_req = OAuthRequest::from_consumer_and_token($consumer, NULL, $method, $resource, $msgParams);
 			$req_req->sign_request($signatureMethod, $consumer, NULL);
-			
 			// Rebuild the query string, including all of the parameters we added.
 			// We have to be careful not to copy POST parameters into the query.
 			// If post and query parameters share a name, they end up being removed
 			// from the query.
-			$forPost = array();
-			foreach ($postParams as $key => $param) {
-				$forPost[$key] = $param;
+			$forPost = array();			
+			$postData = false;
+			if ($method == 'POST') {
+				foreach ($postParams as $key => $param) {
+					$forPost[$key] = $param;
+					if ($postData === false) {
+						$postData = array();
+					}
+					$postData[] = OAuthUtil::urlencodeRFC3986($key) . "=" . OAuthUtil::urlencodeRFC3986($param);
+				}
+				if ($postData !== false) {
+					$postData = implode("&", $postData);
+				}
 			}
 			$newQuery = '';
 			foreach ($req_req->get_parameters() as $key => $param) {
 				if (! isset($forPost[$key])) {
-					$newQuery .= urlencode($key).'='.urlencode($param).'&';
+					$newQuery .= urlencode($key) . '=' . urlencode($param) . '&';
 				}
 			}
 			// and stick on the original query params too
-			if (isset($parsedUri['query']) && !empty($parsedUri['query'])) {
+			if (isset($parsedUri['query']) && ! empty($parsedUri['query'])) {
 				$oldQuery = array();
 				parse_str($parsedUri['query'], $oldQuery);
 				foreach ($oldQuery as $key => $val) {
-					$newQuery .= urlencode($key).'='.urlencode($val).'&';
+					$newQuery .= urlencode($key) . '=' . urlencode($val) . '&';
 				}
 			}
 			// Careful here; the OAuth form encoding scheme is slightly different than
 			// the normal form encoding scheme, so we have to use the OAuth library
 			// formEncode method.
-			$url = $parsedUri['scheme'].'://'.$parsedUri['host'].$parsedUri['path'].'?'.$newQuery;
-			return new RemoteContentRequest($url);
+			$url = $parsedUri['scheme'] . '://' . $parsedUri['host'] . $parsedUri['path'] . '?' . $newQuery;
+			// The headers are transmitted in the POST-data array in the field 'headers'
+			// if no post should be made, the value should be false for this parameter
+			$postHeaders = ((isset($_POST['headers']) && $method == 'POST') ? $_POST['headers'] : false);
+			return new RemoteContentRequest($url, $postHeaders, $postData);
 		} catch (Exception $e) {
 			throw new GadgetException($e);
 		}
@@ -188,7 +201,7 @@
 		$msgParams[OAuth::$OAUTH_TOKEN] = '';
 		$domain = $this->authToken->getDomain();
 		if ($domain != null) {
-			$msgParams[OAuth::$OAUTH_CONSUMER_KEY] = $domain;
+			$msgParams[OAuth::$OAUTH_CONSUMER_KEY] = $domain;
 		}
 		if ($this->keyName != null) {
 			$msgParams[SigningFetcher::$XOAUTH_PUBLIC_KEY] = $this->keyName;
@@ -214,9 +227,24 @@
 		return $list;
 	}
 
+	/*
 	private function allowParam($paramName)
 	{
 		$canonParamName = strtolower($paramName);
 		return (! (substr($canonParamName, 0, 5) == "oauth" || substr($canonParamName, 0, 6) == "xoauth" || substr($canonParamName, 0, 9) == "opensocial")) && ereg(SigningFetcher::$ALLOWED_PARAM_NAME, $canonParamName);
 	}
+	*/
+	
+	private function allowParam($paramName)
+	{
+		$canonParamName = strtolower($paramName);
+		// Exclude the fields which are only used to tell the proxy what to do
+		// and the fields which should be added by signing the request later on
+		if ($canonParamName == "output" || $canonParamName == "httpmethod" || $canonParamName == "authz" || $canonParamName == "st" || $canonParamName == "headers" || $canonParamName == "url" || $canonParamName == "contenttype" || $canonParamName == "postdata" || $canonParamName == "numentries" || $canonParamName == "getsummaries" || $canonParamName == "signowner" || $canonParamName == "signviewer" || $canonParamName == "gadget" || $canonParamName == "bypassspeccache" || substr($canonParamName, 0, 5) == "oauth" || substr($canonParamName, 0, 6) == "xoauth" || substr($canonParamName, 0, 9) == "opensocial") {
+			return false;
+		}
+		// make a last sanity check on the key of the data by using a regular expression
+		return ereg(SigningFetcher::$ALLOWED_PARAM_NAME, $canonParamName);
+	}
+
 }