From 61b3f5664780850bf1d20bd437f6dde1cdb6bf89 Mon Sep 17 00:00:00 2001
From: Zach Copley <zach@status.net>
Date: Mon, 11 Jan 2010 17:30:56 -0800
Subject: [PATCH] Make API auth handle OAuth requests w/access tokens

---
 lib/apiauth.php | 114 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 112 insertions(+), 2 deletions(-)

diff --git a/lib/apiauth.php b/lib/apiauth.php
index 7102764cba..3229ab19fd 100644
--- a/lib/apiauth.php
+++ b/lib/apiauth.php
@@ -28,7 +28,7 @@
  * @author    Evan Prodromou <evan@status.net>
  * @author    mEDI <medi@milaro.net>
  * @author    Sarven Capadisli <csarven@status.net>
- * @author    Zach Copley <zach@status.net> 
+ * @author    Zach Copley <zach@status.net>
  * @copyright 2009 StatusNet, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://status.net/
@@ -39,6 +39,7 @@ if (!defined('STATUSNET')) {
 }
 
 require_once INSTALLDIR . '/lib/api.php';
+require_once INSTALLDIR . '/lib/apioauthstore.php';
 
 /**
  * Actions extending this class will require auth
@@ -52,6 +53,8 @@ require_once INSTALLDIR . '/lib/api.php';
 
 class ApiAuthAction extends ApiAction
 {
+    var $access_token;
+    var $oauth_access_type;
 
     /**
      * Take arguments for running, and output basic auth header if needed
@@ -67,12 +70,119 @@ class ApiAuthAction extends ApiAction
         parent::prepare($args);
 
         if ($this->requiresAuth()) {
-            $this->checkBasicAuthUser();
+
+	    $this->consumer_key = $this->arg('oauth_consumer_key');
+	    $this->access_token = $this->arg('oauth_token');
+
+	    if (!empty($this->access_token)) {
+		$this->checkOAuthRequest();
+	    } else {
+		$this->checkBasicAuthUser();
+	    }
         }
 
         return true;
     }
 
+    function checkOAuthRequest()
+    {
+	common_debug("We have an OAuth request.");
+
+	$datastore   = new ApiStatusNetOAuthDataStore();
+	$server      = new OAuthServer($datastore);
+	$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
+
+	$server->add_signature_method($hmac_method);
+
+	$this->cleanRequest();
+
+	try {
+
+	    $req  = OAuthRequest::from_request();
+	    $server->verify_request($req);
+
+	    common_debug("Good OAuth request!");
+
+	    $app = Oauth_application::getByConsumerKey($this->consumer_key);
+
+	    if (empty($app)) {
+
+		// this should really not happen
+		common_log(LOG_WARN,
+			   "Couldn't find the OAuth app for consumer key: $this->consumer_key");
+
+		throw new OAuthException('No application for that consumer key.');
+	    }
+
+	    $appUser = Oauth_application_user::staticGet('token',
+							 $this->access_token);
+
+	    // XXX: check that app->id and appUser->application_id and consumer all
+	    // match?
+
+	    if (!empty($appUser)) {
+
+		// read or read-write
+		$this->oauth_access_type = $appUser->access_type;
+
+		// If access_type == 0 we have either a request token
+		// or a bad / revoked access token
+
+		if ($this->oauth_access_type != 0) {
+
+		    $this->auth_user = User::staticGet('id', $appUser->profile_id);
+
+		    $msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " .
+		      "application '%s' (id: %d).";
+
+		    common_log(LOG_INFO, sprintf($msg,
+						 $this->auth_user->nickname,
+						 $this->auth_user->id,
+						 $app->name,
+						 $app->id));
+		    return true;
+		} else {
+		    throw new OAuthException('Bad access token.');
+		}
+	    } else {
+
+		// also should not happen
+		throw new OAuthException('No user for that token.');
+	    }
+
+	} catch (OAuthException $e) {
+	    common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage());
+	    common_debug(var_export($req, true));
+	    $this->showOAuthError($e->getMessage());
+	    exit();
+	}
+    }
+
+    function showOAuthError($msg)
+    {
+	header('HTTP/1.1 401 Unauthorized');
+	header('Content-Type: text/html; charset=utf-8');
+	print $msg . "\n";
+    }
+
+    function cleanRequest()
+    {
+	// kill evil effects of magical slashing
+
+	if(get_magic_quotes_gpc() == 1) {
+	    $_POST = array_map('stripslashes', $_POST);
+	    $_GET = array_map('stripslashes', $_GET);
+	}
+
+	// strip out the p param added in index.php
+
+	// XXX: should we strip anything else?  Or alternatively
+	// only allow a known list of params?
+
+	unset($_GET['p']);
+	unset($_POST['p']);
+    }
+
     /**
      * Does this API resource require authentication?
      *
-- 
2.39.5