Merge branch 'oauth-1.0a' into 0.9.x

This commit is contained in:
Zach Copley 2010-10-12 17:52:04 -07:00
commit 04f3f57e2e
23 changed files with 1659 additions and 877 deletions

View File

@ -2,7 +2,8 @@
/** /**
* StatusNet, the distributed open-source microblogging tool * StatusNet, the distributed open-source microblogging tool
* *
* Exchange an authorized OAuth request token for an access token * Action for getting OAuth token credentials (exchange an authorized
* request token for an access token)
* *
* PHP version 5 * PHP version 5
* *
@ -34,7 +35,8 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/lib/apioauth.php'; require_once INSTALLDIR . '/lib/apioauth.php';
/** /**
* Exchange an authorized OAuth request token for an access token * Action for getting OAuth token credentials (exchange an authorized
* request token for an access token)
* *
* @category API * @category API
* @package StatusNet * @package StatusNet
@ -45,6 +47,8 @@ require_once INSTALLDIR . '/lib/apioauth.php';
class ApiOauthAccessTokenAction extends ApiOauthAction class ApiOauthAccessTokenAction extends ApiOauthAction
{ {
protected $reqToken = null;
protected $verifier = null;
/** /**
* Class handler. * Class handler.
@ -65,30 +69,58 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
$atok = null; $atok = null;
// XXX: Insist that oauth_token and oauth_verifier be populated?
// Spec doesn't say they MUST be.
try { try {
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request();
$this->reqToken = $req->get_parameter('oauth_token');
$this->verifier = $req->get_parameter('oauth_verifier');
$atok = $server->fetch_access_token($req); $atok = $server->fetch_access_token($req);
} catch (OAuthException $e) { } catch (OAuthException $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
common_debug(var_export($req, true)); common_debug(var_export($req, true));
$this->outputError($e->getMessage()); $code = $e->getCode();
return; $this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
} }
if (empty($atok)) { if (empty($atok)) {
common_debug('couldn\'t get access token.');
print "Token exchange failed. Has the request token been authorized?\n"; // Token exchange failed -- log it
list($proxy, $ip) = common_client_ip();
$msg = sprintf(
'API OAuth - Failure exchanging request token for access token, '
. 'request token = %s, verifier = %s, IP = %s, proxy = %s',
$this->reqToken,
$this->verifier,
$ip,
$proxy
);
common_log(LOG_WARNING, $msg);
$this->clientError(_("Invalid request token or verifier.", 400, 'text'));
} else { } else {
print $atok; $this->showAccessToken($atok);
} }
} }
function outputError($msg) /*
* Display OAuth token credentials
*
* @param OAuthToken token the access token
*/
function showAccessToken($token)
{ {
header('HTTP/1.1 401 Unauthorized'); header('Content-Type: application/x-www-form-urlencoded');
header('Content-Type: text/html; charset=utf-8'); print $token;
print $msg . "\n";
} }
} }

View File

@ -32,6 +32,7 @@ if (!defined('STATUSNET')) {
} }
require_once INSTALLDIR . '/lib/apioauth.php'; require_once INSTALLDIR . '/lib/apioauth.php';
require_once INSTALLDIR . '/lib/info.php';
/** /**
* Authorize an OAuth request token * Authorize an OAuth request token
@ -43,9 +44,10 @@ require_once INSTALLDIR . '/lib/apioauth.php';
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiOauthAuthorizeAction extends ApiOauthAction class ApiOauthAuthorizeAction extends Action
{ {
var $oauth_token; var $oauthTokenParam;
var $reqToken;
var $callback; var $callback;
var $app; var $app;
var $nickname; var $nickname;
@ -69,10 +71,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$this->nickname = $this->trimmed('nickname'); $this->nickname = $this->trimmed('nickname');
$this->password = $this->arg('password'); $this->password = $this->arg('password');
$this->oauth_token = $this->arg('oauth_token'); $this->oauthTokenParam = $this->arg('oauth_token');
$this->callback = $this->arg('oauth_callback'); $this->callback = $this->arg('oauth_callback');
$this->store = new ApiStatusNetOAuthDataStore(); $this->store = new ApiStatusNetOAuthDataStore();
$this->app = $this->store->getAppByRequestToken($this->oauth_token);
try {
$this->app = $this->store->getAppByRequestToken($this->oauthTokenParam);
} catch (Exception $e) {
$this->clientError($e->getMessage());
}
return true; return true;
} }
@ -97,14 +104,30 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
} else { } else {
if (empty($this->oauth_token)) { // Make sure a oauth_token parameter was provided
if (empty($this->oauthTokenParam)) {
$this->clientError(_('No oauth_token parameter provided.')); $this->clientError(_('No oauth_token parameter provided.'));
return; } else {
// Check to make sure the token exists
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
if (empty($this->reqToken)) {
$this->serverError(
_('Invalid request token.')
);
} else {
// Check to make sure we haven't already authorized the token
if ($this->reqToken->state != 0) {
$this->clientError("Invalid request token.");
}
}
} }
// make sure there's an app associated with this token
if (empty($this->app)) { if (empty($this->app)) {
$this->clientError(_('Invalid token.')); $this->clientError(_('Invalid request token.'));
return;
} }
$name = $this->app->name; $name = $this->app->name;
@ -120,8 +143,8 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$token = $this->trimmed('token'); $token = $this->trimmed('token');
if (!$token || $token != common_session_token()) { if (!$token || $token != common_session_token()) {
$this->showForm(_('There was a problem with your session token. '. $this->showForm(
'Try again, please.')); _('There was a problem with your session token. Try again, please.'));
return; return;
} }
@ -130,6 +153,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$user = null; $user = null;
if (!common_logged_in()) { if (!common_logged_in()) {
// XXX Force credentials check?
// XXX OpenID
$user = common_check_user($this->nickname, $this->password); $user = common_check_user($this->nickname, $this->password);
if (empty($user)) { if (empty($user)) {
$this->showForm(_("Invalid nickname / password!")); $this->showForm(_("Invalid nickname / password!"));
@ -141,9 +169,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if ($this->arg('allow')) { if ($this->arg('allow')) {
// mark the req token as authorized // fetch the token
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
$this->store->authorize_token($this->oauth_token); // mark the req token as authorized
try {
$this->store->authorize_token($this->oauthTokenParam);
} catch (Exception $e) {
$this->serverError($e->getMessage());
}
// Check to see if there was a previous token associated // Check to see if there was a previous token associated
// with this user/app and kill it. If the user is doing this she // with this user/app and kill it. If the user is doing this she
@ -156,8 +190,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!$result) { if (!$result) {
common_log_db_error($appUser, 'DELETE', __FILE__); common_log_db_error($appUser, 'DELETE', __FILE__);
throw new ServerException(_('Database error deleting OAuth application user.')); $this->serverError(_('Database error deleting OAuth application user.'));
return;
} }
} }
@ -175,20 +208,19 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// granted. The OAuth app user record then gets updated // granted. The OAuth app user record then gets updated
// with the new access token and access type. // with the new access token and access type.
$appUser->token = $this->oauth_token; $appUser->token = $this->oauthTokenParam;
$appUser->created = common_sql_now(); $appUser->created = common_sql_now();
$result = $appUser->insert(); $result = $appUser->insert();
if (!$result) { if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__); common_log_db_error($appUser, 'INSERT', __FILE__);
throw new ServerException(_('Database error inserting OAuth application user.')); $this->serverError(_('Database error inserting OAuth application user.'));
return;
} }
// if we have a callback redirect and provide the token // If we have a callback redirect and provide the token
// A callback specified in the app setup overrides whatever // Note: A callback specified in the app setup overrides whatever
// is passed in with the request. // is passed in with the request.
if (!empty($this->app->callback_url)) { if (!empty($this->app->callback_url)) {
@ -197,40 +229,40 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!empty($this->callback)) { if (!empty($this->callback)) {
$target_url = $this->getCallback($this->callback, $targetUrl = $this->getCallback(
array('oauth_token' => $this->oauth_token)); $this->callback,
array(
'oauth_token' => $this->oauthTokenParam,
'oauth_verifier' => $this->reqToken->verifier // 1.0a
)
);
// Redirect the user to the provided OAuth callback
common_redirect($targetUrl, 303);
common_redirect($target_url, 303);
} else { } else {
common_debug("callback was empty!"); common_log(
LOG_INFO,
"No oauth_callback parameter provided for application ID "
. $this->app->id
. " when authorizing request token."
);
} }
// otherwise inform the user that the rt was authorized // Otherwise, inform the user that the rt was authorized
$this->showAuthorized();
$this->elementStart('p'); } else if ($this->arg('cancel')) {
// XXX: Do OAuth 1.0a verifier code try {
$this->store->revoke_token($this->oauthTokenParam, 0);
$this->showCanceled();
} catch (Exception $e) {
$this->ServerError($e->getMessage());
}
$this->raw(sprintf(_("The request token %s has been authorized. " .
'Please exchange it for an access token.'),
$this->oauth_token));
$this->elementEnd('p');
} else if ($this->arg('deny')) {
$datastore = new ApiStatusNetOAuthDataStore();
$datastore->revoke_token($this->oauth_token, 0);
$this->elementStart('p');
$this->raw(sprintf(_("The request token %s has been denied and revoked."),
$this->oauth_token));
$this->elementEnd('p');
} else { } else {
$this->clientError(_('Unexpected form submission.')); $this->clientError(_('Unexpected form submission.'));
return;
} }
} }
@ -276,7 +308,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
_('Allow or deny access')); _('Allow or deny access'));
$this->hidden('token', common_session_token()); $this->hidden('token', common_session_token());
$this->hidden('oauth_token', $this->oauth_token); $this->hidden('oauth_token', $this->oauthTokenParam);
$this->hidden('oauth_callback', $this->callback); $this->hidden('oauth_callback', $this->callback);
$this->elementStart('ul', 'form_data'); $this->elementStart('ul', 'form_data');
@ -321,11 +353,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
} }
$this->element('input', array('id' => 'deny_submit', $this->element('input', array('id' => 'cancel_submit',
'class' => 'submit submit form_action-primary', 'class' => 'submit submit form_action-primary',
'name' => 'deny', 'name' => 'cancel',
'type' => 'submit', 'type' => 'submit',
'value' => _('Deny'))); 'value' => _('Cancel')));
$this->element('input', array('id' => 'allow_submit', $this->element('input', array('id' => 'allow_submit',
'class' => 'submit submit form_action-secondary', 'class' => 'submit submit form_action-secondary',
@ -348,7 +380,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
function getInstructions() function getInstructions()
{ {
return _('Allow or deny access to your account information.'); return _('Authorize access to your account information.');
} }
/** /**
@ -388,4 +420,107 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// NOP // NOP
} }
/*
* Show a nice message confirming the authorization
* operation was canceled.
*
* @return nothing
*/
function showCanceled()
{
$info = new InfoAction(
_('Authorization canceled.'),
sprintf(
_('The request token %s has been revoked.'),
$this->oauthTokenParm
)
);
$info->showPage();
}
/*
* Show a nice message that the authorization was successful.
* If the operation is out-of-band, show a pin.
*
* @return nothing
*/
function showAuthorized()
{
$title = sprintf(
_("You have successfully authorized %s."),
$this->app->name
);
$msg = sprintf(
_('Please return to %s and enter the following security code to complete the process.'),
$this->app->name
);
if ($this->reqToken->verified_callback == 'oob') {
$pin = new ApiOauthPinAction($title, $msg, $this->reqToken->verifier);
$pin->showPage();
} else {
// NOTE: This would only happen if an application registered as
// a web application but sent in 'oob' for the oauth_callback
// parameter. Usually web apps will send in a callback and
// not use the pin-based workflow.
$info = new InfoAction(
$title,
$msg,
$this->oauthTokenParam,
$this->reqToken->verifier
);
$info->showPage();
}
}
/*
* Properly format the callback URL and parameters so it's
* suitable for a redirect in the OAuth dance
*
* @param string $url the URL
* @param array $params an array of parameters
*
* @return string $url a URL to use for redirecting to
*/
function getCallback($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar(
$url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v)
);
}
return $url;
}
/*
* Append a new query parameter after any existing query
* parameters.
*
* @param string $url the URL
* @prarm string $k the parameter name
* @param string $v value of the paramter
*
* @return string $url the new URL with added parameter
*/
function appendQueryVar($url, $k, $v) {
$url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
$url = substr($url, 0, -1);
if (strpos($url, '?') === false) {
return ($url . '?' . $k . '=' . $v);
} else {
return ($url . '&' . $k . '=' . $v);
}
}
} }

67
actions/apioauthpin.php Normal file
View File

@ -0,0 +1,67 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action for displaying an OAuth verifier pin
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 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/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR . '/lib/info.php';
/**
* Class for displaying an OAuth verifier pin
*
* XXX: I'm pretty sure we don't need to check the logged in state here. -- Zach
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiOauthPinAction extends InfoAction
{
function __construct($title, $message, $verifier)
{
$this->verifier = $verifier;
$this->title = $title;
parent::__construct($title, $message);
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'info'), $this->message);
$this->element('div', array('id' => 'oauth_pin'), $this->verifier);
}
}

View File

@ -2,7 +2,7 @@
/** /**
* StatusNet, the distributed open-source microblogging tool * StatusNet, the distributed open-source microblogging tool
* *
* Get an OAuth request token * Issue temporary OAuth credentials (a request token)
* *
* PHP version 5 * PHP version 5
* *
@ -34,7 +34,7 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/lib/apioauth.php'; require_once INSTALLDIR . '/lib/apioauth.php';
/** /**
* Get an OAuth request token * Issue temporary OAuth credentials (a request token)
* *
* @category API * @category API
* @package StatusNet * @package StatusNet
@ -58,22 +58,23 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
{ {
parent::prepare($args); parent::prepare($args);
$this->callback = $this->arg('oauth_callback'); // XXX: support "force_login" parameter like Twitter? (Forces the user to enter
// their credentials to ensure the correct users account is authorized.)
if (!empty($this->callback)) {
common_debug("callback: $this->callback");
}
return true; return true;
} }
/** /**
* Class handler. * Handle a request for temporary OAuth credentials
*
* Make sure the request is kosher, then emit a set of temporary
* credentials -- AKA an unauthorized request token.
* *
* @param array $args array of arguments * @param array $args array of arguments
* *
* @return void * @return void
*/ */
function handle($args) function handle($args)
{ {
parent::handle($args); parent::handle($args);
@ -85,14 +86,78 @@ class ApiOauthRequestTokenAction extends ApiOauthAction
$server->add_signature_method($hmac_method); $server->add_signature_method($hmac_method);
try { try {
$req = OAuthRequest::from_request(); $req = OAuthRequest::from_request();
// verify callback
if (!$this->verifyCallback($req->get_parameter('oauth_callback'))) {
throw new OAuthException(
"You must provide a valid URL or 'oob' in oauth_callback.",
400
);
}
// check signature and issue a new request token
$token = $server->fetch_request_token($req); $token = $server->fetch_request_token($req);
print $token;
common_log(
LOG_INFO,
sprintf(
"API OAuth - Issued request token %s for consumer %s with oauth_callback %s",
$token->key,
$req->get_parameter('oauth_consumer_key'),
"'" . $req->get_parameter('oauth_callback') ."'"
)
);
// return token to the client
$this->showRequestToken($token);
} catch (OAuthException $e) { } catch (OAuthException $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: text/html; charset=utf-8'); // Return 401 for for bad credentials or signature problems,
print $e->getMessage() . "\n"; // and 400 for missing or unsupported parameters
$code = $e->getCode();
$this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
}
}
/*
* Display temporary OAuth credentials
*/
function showRequestToken($token)
{
header('Content-Type: application/x-www-form-urlencoded');
print $token;
print '&oauth_callback_confirmed=true';
}
/* Make sure the callback parameter contains either a real URL
* or the string 'oob'.
*
* @todo Check for evil/banned URLs here
*
* @return boolean true or false
*/
function verifyCallback($callback)
{
if ($callback == "oob") {
common_debug("OAuth request token requested for out of bounds client.");
// XXX: Should we throw an error if a client is registered as a
// web application but requests the pin based workflow? For now I'm
// allowing the workflow to proceed and issuing a pin. --Zach
return true;
} else {
return Validate::uri(
$callback,
array('allowed_schemes' => array('http', 'https'))
);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1244,23 +1244,29 @@ class ApiAction extends Action
// Do not emit error header for JSONP // Do not emit error header for JSONP
if (!isset($this->callback)) { if (!isset($this->callback)) {
header('HTTP/1.1 '.$code.' '.$status_string); header('HTTP/1.1 ' . $code . ' ' . $status_string);
} }
if ($format == 'xml') { switch($format) {
case 'xml':
$this->initDocument('xml'); $this->initDocument('xml');
$this->elementStart('hash'); $this->elementStart('hash');
$this->element('error', null, $msg); $this->element('error', null, $msg);
$this->element('request', null, $_SERVER['REQUEST_URI']); $this->element('request', null, $_SERVER['REQUEST_URI']);
$this->elementEnd('hash'); $this->elementEnd('hash');
$this->endDocument('xml'); $this->endDocument('xml');
} elseif ($format == 'json'){ break;
case 'json':
$this->initDocument('json'); $this->initDocument('json');
$error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
print(json_encode($error_array)); print(json_encode($error_array));
$this->endDocument('json'); $this->endDocument('json');
} else { break;
case 'text':
header('Content-Type: text/plain; charset=utf-8');
print $msg;
break;
default:
// If user didn't request a useful format, throw a regular client error // If user didn't request a useful format, throw a regular client error
throw new ClientException($msg, $code); throw new ClientException($msg, $code);
} }

View File

@ -30,13 +30,12 @@
if (!defined('STATUSNET')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
require_once INSTALLDIR . '/lib/apiaction.php';
require_once INSTALLDIR . '/lib/apioauthstore.php'; require_once INSTALLDIR . '/lib/apioauthstore.php';
/** /**
* Base action for API OAuth enpoints. Clean up the * Base action for API OAuth enpoints. Clean up the
* the request, and possibly some other common things * request. Some other common functions.
* here.
* *
* @category API * @category API
* @package StatusNet * @package StatusNet
@ -44,7 +43,7 @@ require_once INSTALLDIR . '/lib/apioauthstore.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
class ApiOauthAction extends Action class ApiOauthAction extends ApiAction
{ {
/** /**
* Is this a read-only action? * Is this a read-only action?
@ -77,6 +76,12 @@ class ApiOauthAction extends Action
self::cleanRequest(); self::cleanRequest();
} }
/*
* Clean up the request so the OAuth library doesn't find
* any extra parameters or anything else it's not expecting.
* I'm looking at you, p parameter.
*/
static function cleanRequest() static function cleanRequest()
{ {
// kill evil effects of magical slashing // kill evil effects of magical slashing
@ -86,31 +91,19 @@ class ApiOauthAction extends Action
} }
// strip out the p param added in index.php // 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($_GET['p']);
unset($_POST['p']); unset($_POST['p']);
unset($_REQUEST['p']);
$queryArray = explode('&', $_SERVER['QUERY_STRING']);
for ($i = 0; $i < sizeof($queryArray); $i++) {
if (substr($queryArray[$i], 0, 2) == 'p=') {
unset($queryArray[$i]);
}
} }
function getCallback($url, $params) $_SERVER['QUERY_STRING'] = implode('&', $queryArray);
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar($url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v));
} }
return $url;
}
function appendQueryVar($url, $k, $v) {
$url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
$url = substr($url, 0, -1);
if (strpos($url, '?') === false) {
return ($url . '?' . $k . '=' . $v);
} else {
return ($url . '&' . $k . '=' . $v);
}
}
} }

View File

@ -71,33 +71,37 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
} }
} }
function new_access_token($token, $consumer) function new_access_token($token, $consumer, $verifier)
{ {
common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); common_debug(
'new_access_token("' . $token->key . '","' . $consumer->key. '","' . $verifier . '")',
__FILE__
);
$rt = new Token(); $rt = new Token();
$rt->consumer_key = $consumer->key; $rt->consumer_key = $consumer->key;
$rt->tok = $token->key; $rt->tok = $token->key;
$rt->type = 0; // request $rt->type = 0; // request
$app = Oauth_application::getByConsumerKey($consumer->key); $app = Oauth_application::getByConsumerKey($consumer->key);
assert(!empty($app));
if (empty($app)) { if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized
common_debug("empty app!");
}
if ($rt->find(true) && $rt->state == 1) { // authorized
common_debug('request token found.', __FILE__); common_debug('request token found.', __FILE__);
// find the associated user of the app // find the associated user of the app
$appUser = new Oauth_application_user(); $appUser = new Oauth_application_user();
$appUser->application_id = $app->id; $appUser->application_id = $app->id;
$appUser->token = $rt->tok; $appUser->token = $rt->tok;
$result = $appUser->find(true); $result = $appUser->find(true);
if (!empty($result)) { if (!empty($result)) {
common_debug("Oath app user found."); common_debug("Ouath app user found.");
} else { } else {
common_debug("Oauth app user not found. app id $app->id token $rt->tok"); common_debug("Oauth app user not found. app id $app->id token $rt->tok");
return null; return null;
@ -110,6 +114,8 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
$at->tok = common_good_rand(16); $at->tok = common_good_rand(16);
$at->secret = common_good_rand(16); $at->secret = common_good_rand(16);
$at->type = 1; // access $at->type = 1; // access
$at->verifier = $verifier;
$at->verified_callback = $rt->verified_callback; // 1.0a
$at->created = DB_DataObject_Cast::dateTime(); $at->created = DB_DataObject_Cast::dateTime();
if (!$at->insert()) { if (!$at->insert()) {
@ -183,4 +189,40 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
throw new Exception(_('Failed to delete revoked token.')); throw new Exception(_('Failed to delete revoked token.'));
} }
} }
/*
* Create a new request token. Overrided to support OAuth 1.0a callback
*
* @param OAuthConsumer $consumer the OAuth Consumer for this token
* @param string $callback the verified OAuth callback URL
*
* @return OAuthToken $token a new unauthorized OAuth request token
*/
function new_request_token($consumer, $callback)
{
$t = new Token();
$t->consumer_key = $consumer->key;
$t->tok = common_good_rand(16);
$t->secret = common_good_rand(16);
$t->type = 0; // request
$t->state = 0; // unauthorized
$t->verified_callback = $callback;
if ($callback === 'oob') {
// six digit pin
$t->verifier = mt_rand(0, 9999999);
} else {
$t->verifier = common_good_rand(8);
}
$t->created = DB_DataObject_Cast::dateTime();
if (!$t->insert()) {
return null;
} else {
return new OAuthToken($t->tok, $t->secret);
}
}
} }

View File

@ -12,7 +12,7 @@
* @link http://status.net/ * @link http://status.net/
* *
* StatusNet - the distributed open-source microblogging tool * StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc. * Copyright (C) 2008-2010 StatusNet, Inc.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by * it under the terms of the GNU Affero General Public License as published by
@ -32,7 +32,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1); exit(1);
} }
require_once INSTALLDIR.'/lib/error.php'; require_once INSTALLDIR . '/lib/error.php';
/** /**
* Class for displaying HTTP client errors * Class for displaying HTTP client errors
@ -90,4 +90,26 @@ class ClientErrorAction extends ErrorAction
$this->showPage(); $this->showPage();
} }
/**
* To specify additional HTTP headers for the action
*
* @return void
*/
function extraHeaders()
{
$status_string = @self::$status[$this->code];
header('HTTP/1.1 '.$this->code.' '.$status_string);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return @self::$status[$this->code];
}
} }

View File

@ -116,9 +116,9 @@ class ConnectSettingsNav extends Widget
} }
$menu['oauthconnectionssettings'] = array( $menu['oauthconnectionssettings'] = array(
// TRANS: Menu item for OAth connection settings. // TRANS: Menu item for OuAth connection settings.
_m('MENU','Connections'), _m('MENU','Connections'),
// TRANS: Tooltip for connected applications (Connections through OAth) menu item. // TRANS: Tooltip for connected applications (Connections through OAuth) menu item.
_('Authorized connected applications') _('Authorized connected applications')
); );

View File

@ -33,6 +33,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1); exit(1);
} }
require_once INSTALLDIR . '/lib/info.php';
/** /**
* Base class for displaying HTTP errors * Base class for displaying HTTP errors
* *
@ -42,7 +44,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/ * @link http://status.net/
*/ */
class ErrorAction extends Action class ErrorAction extends InfoAction
{ {
static $status = array(); static $status = array();
@ -52,7 +54,7 @@ class ErrorAction extends Action
function __construct($message, $code, $output='php://output', $indent=null) function __construct($message, $code, $output='php://output', $indent=null)
{ {
parent::__construct($output, $indent); parent::__construct(null, $message, $output, $indent);
$this->code = $code; $this->code = $code;
$this->message = $message; $this->message = $message;
@ -64,43 +66,6 @@ class ErrorAction extends Action
$this->prepare($_REQUEST); $this->prepare($_REQUEST);
} }
/**
* To specify additional HTTP headers for the action
*
* @return void
*/
function extraHeaders()
{
$status_string = @self::$status[$this->code];
header('HTTP/1.1 '.$this->code.' '.$status_string);
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'error'), $this->message);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return @self::$status[$this->code];
}
function isReadOnly($args)
{
return true;
}
function showPage() function showPage()
{ {
if ($this->minimal) { if ($this->minimal) {
@ -116,32 +81,16 @@ class ErrorAction extends Action
exit(); exit();
} }
// Overload a bunch of stuff so the page isn't too bloated /**
* Display content.
function showBody() *
* @return nothing
*/
function showContent()
{ {
$this->elementStart('body', array('id' => 'error')); $this->element('div', array('class' => 'error'), $this->message);
$this->elementStart('div', array('id' => 'wrap'));
$this->showHeader();
$this->showCore();
$this->showFooter();
$this->elementEnd('div');
$this->elementEnd('body');
} }
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
$this->showContentBlock();
$this->elementEnd('div');
}
function showHeader()
{
$this->elementStart('div', array('id' => 'header'));
$this->showLogo();
$this->showPrimaryNav();
$this->elementEnd('div');
}
} }

118
lib/info.php Normal file
View File

@ -0,0 +1,118 @@
<?php
/**
* Information action
*
* PHP version 5
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Base class for displaying dialog box like messages to the user
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* @see ErrorAction
*/
class InfoAction extends Action
{
var $message = null;
function __construct($title, $message, $output='php://output', $indent=null)
{
parent::__construct($output, $indent);
$this->message = $message;
$this->title = $title;
// XXX: hack alert: usually we aren't going to
// call this page directly, but because it's
// an action it needs an args array anyway
$this->prepare($_REQUEST);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return empty($this->title) ? '' : $this->title;
}
function isReadOnly($args)
{
return true;
}
// Overload a bunch of stuff so the page isn't too bloated
function showBody()
{
$this->elementStart('body', array('id' => 'error'));
$this->elementStart('div', array('id' => 'wrap'));
$this->showHeader();
$this->showCore();
$this->showFooter();
$this->elementEnd('div');
$this->elementEnd('body');
}
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
$this->showContentBlock();
$this->elementEnd('div');
}
function showHeader()
{
$this->elementStart('div', array('id' => 'header'));
$this->showLogo();
$this->showPrimaryNav();
$this->elementEnd('div');
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'info'), $this->message);
}
}

View File

@ -55,6 +55,17 @@ class StatusNetOAuthDataStore extends OAuthDataStore
} }
} }
function getTokenByKey($token_key)
{
$t = new Token();
$t->tok = $token_key;
if ($t->find(true)) {
return $t;
} else {
return null;
}
}
// http://oauth.net/core/1.0/#nonce // http://oauth.net/core/1.0/#nonce
// "The Consumer SHALL then generate a Nonce value that is unique for // "The Consumer SHALL then generate a Nonce value that is unique for
// all requests with that timestamp." // all requests with that timestamp."

View File

@ -96,4 +96,27 @@ class ServerErrorAction extends ErrorAction
$this->showPage(); $this->showPage();
} }
/**
* To specify additional HTTP headers for the action
*
* @return void
*/
function extraHeaders()
{
$status_string = @self::$status[$this->code];
header('HTTP/1.1 '.$this->code.' '.$status_string);
}
/**
* Page title.
*
* @return page title
*/
function title()
{
return @self::$status[$this->code];
}
} }

View File

@ -22,7 +22,7 @@
* @category Exception * @category Exception
* @package StatusNet * @package StatusNet
* @author Evan Prodromou <evan@status.net> * @author Evan Prodromou <evan@status.net>
* @copyright 2008 StatusNet, Inc. * @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -1,22 +1,160 @@
Some very rough test scripts for hitting up the OAuth endpoints. Some very rough test scripts for hitting up the OAuth endpoints.
Note: this works best if you register an OAuth application, leaving These instructions assume you understand the basics of how OAuth
the callback URL blank. works. You may want to read up about it first. Here are some good
resources for learning about OAuth:
Put your instance info and consumer key and secret in oauth.ini http://hueniverse.com/oauth/
http://tools.ietf.org/html/rfc5849
Example usage: To use these scripts (and OAuth in general) first you will need to
-------------- register and OAuth client application with your StatusNet instance:
php getrequesttoken.php http://example.status.net/settings/oauthapps
Gets a request token, token secret and a url to authorize it. Once oauth.ini
you authorize the request token you can exchange it for an access token... ---------
php exchangetokens.php --oauth_token=b9a79548a88c1aa9a5bea73103c6d41d --token_secret=4a47d9337fc0202a14ab552e17a3b657 Using oauth.ini.sample as a guide, put your StatusNet OAuth endpoints
and consumer key and secret in a file called oauth.ini and save it
in the same directory as these scripts.
Once you have your access token, go ahead and try a protected API fetch_temp_creds.php
resource: --------------------
php verifycreds.php --oauth_token=cf2de7665f0dda0a82c2dc39b01be7f9 --token_secret=4524c3b712200138e1a4cff2e9ca83d8 Will fetch a request token, token secret and a URL to authorize the
token. Once you authorize the request token, you can exchange it
for an access token.
example usage:
$ php fetch_temp_creds.php
Request Token
- oauth_token = 89d481e376edc622f08da5791e6a4446
- oauth_token_secret = 6d028bcd1ea125cbed7da2f254219885
Authorize URL
http://example.status.net/api/oauth/authorize?oauth_token=89d481e376edc622f08da5791e6a4446
Now paste the Authorize URL into your browser and authorize your temporary credentials.
fetch_token_creds.php
---------------------
After you have authorized your request token, you will be presented
with a verifier code, or pin, in your browser, which you will need
to get an access token. Make sure you copy it into a text buffer
or write it down or something. Then call fetch_token_credentials.php
to exchange your temporary credentials for real token credentials.
example usage:
$ php fetch_token_creds.php -t 89d481e376edc622f08da5791e6a4446 -s 6d028bcd1ea125cbed7da2f254219885 -v 305162
Access Token
- oauth_token = 9b354df102d8e2b4621122c85d8d045c
- oauth_token_secret = 1800a88f1574b47d595214a74e5b1ec5
oauth_verify_credentials.php
----------------------------
Now you should have real token credentials (an OAuth access token)
and you can access protected API resources. This is an example
script that calls /api/account/verify_credentials.xml.
example usage:
$ php oauth_verify_creds.php -t 80305cd15c5c69834364ac02d7f9178c -s 673e3b2978b1b92c8edbfe172505fee1
<?xml version="1.0" encoding="UTF-8"?>
<user xmlns:statusnet="http://status.net/schema/api/1/">
<id>23</id>
<name>zach</name>
<screen_name>zach</screen_name>
<location></location>
<description></description>
<profile_image_url>http://example.status.net/theme/default/default-avatar-stream.png</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>0</followers_count>
<profile_background_color></profile_background_color>
<profile_text_color></profile_text_color>
<profile_link_color></profile_link_color>
<profile_sidebar_fill_color></profile_sidebar_fill_color>
<profile_sidebar_border_color></profile_sidebar_border_color>
<friends_count>0</friends_count>
<created_at>Thu Sep 30 23:11:00 +0000 2010</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url></profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>4</statuses_count>
<following>true</following>
<statusnet:blocking>false</statusnet:blocking>
<notifications>true</notifications>
<status>
<text>gar</text>
<truncated>false</truncated>
<created_at>Wed Oct 06 23:40:14 +0000 2010</created_at>
<in_reply_to_status_id></in_reply_to_status_id>
<source>web</source>
<id>7</id>
<in_reply_to_user_id></in_reply_to_user_id>
<in_reply_to_screen_name></in_reply_to_screen_name>
<geo></geo>
<favorited>false</favorited>
<statusnet:html>gar</statusnet:html>
</status>
<statusnet:profile_url>http://example.status.net/statusnet/zach</statusnet:profile_url>
</user>
oauth_post_notice.php
---------------------
This is another test script that lets you post a notice via OAuth.
example usage:
$ php oauth_post_notice.php -t 80305cd15c5c69834364ac02d7f9178c -s 673e3b2978b1b92c8edbfe172505fee1 -u 'Test test test...'
<?xml version="1.0" encoding="UTF-8"?>
<status xmlns:statusnet="http://status.net/schema/api/1/">
<text>Test test test...</text>
<truncated>false</truncated>
<created_at>Fri Oct 08 02:37:35 +0000 2010</created_at>
<in_reply_to_status_id></in_reply_to_status_id>
<source>&lt;a href=&quot;http://banana.com&quot; rel=&quot;nofollow&quot;&gt;Banana&lt;/a&gt;</source>
<id>8</id>
<in_reply_to_user_id></in_reply_to_user_id>
<in_reply_to_screen_name></in_reply_to_screen_name>
<geo></geo>
<favorited>false</favorited>
<user>
<id>23</id>
<name>zach</name>
<screen_name>zach</screen_name>
<location></location>
<description></description>
<profile_image_url>http://example.status.net/statusnet/theme/default/default-avatar-stream.png</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>0</followers_count>
<profile_background_color></profile_background_color>
<profile_text_color></profile_text_color>
<profile_link_color></profile_link_color>
<profile_sidebar_fill_color></profile_sidebar_fill_color>
<profile_sidebar_border_color></profile_sidebar_border_color>
<friends_count>0</friends_count>
<created_at>Thu Sep 30 23:11:00 +0000 2010</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url></profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>5</statuses_count>
<following>true</following>
<statusnet:blocking>false</statusnet:blocking>
<notifications>true</notifications>
<statusnet:profile_url>http://example.status.net/statusnet/zach</statusnet:profile_url>
</user>
<statusnet:html>Test test test...</statusnet:html>
</status>

View File

@ -1,105 +0,0 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$at_endpoint = $ini['apiroot'] . $ini['access_token_url'];
$shortoptions = 't:s:';
$longoptions = array('oauth_token=', 'token_secret=');
$helptext = <<<END_OF_ETOKENS_HELP
exchangetokens.php [options]
Exchange an authorized OAuth request token for an access token
-t --oauth_token authorized request token
-s --token_secret authorized request token secret
END_OF_ETOKENS_HELP;
require_once INSTALLDIR . '/scripts/commandline.inc';
$token = null;
$token_secret = null;
if (have_option('t', 'oauth_token')) {
$token = get_option_value('oauth_token');
}
if (have_option('s', 'token_secret')) {
$token_secret = get_option_value('s', 'token_secret');
}
if (empty($token)) {
print "Please specify a request token.\n";
exit(1);
}
if (empty($token_secret)) {
print "Please specify a request token secret.\n";
exit(1);
}
$rt = new OAuthToken($token, $token_secret);
common_debug("Exchange request token = " . var_export($rt, true));
$parsed = parse_url($at_endpoint);
$params = array();
parse_str($parsed['query'], $params);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $rt, "GET", $at_endpoint, $params);
$req_req->sign_request($hmac_method, $test_consumer, $rt);
$r = httpRequest($req_req->to_url());
common_debug("Exchange request token = " . var_export($rt, true));
common_debug("Exchange tokens URL: " . $req_req->to_url());
$body = $r->getBody();
$token_stuff = array();
parse_str($body, $token_stuff);
print 'Access token : ' . $token_stuff['oauth_token'] . "\n";
print 'Access token secret : ' . $token_stuff['oauth_token_secret'] . "\n";
function httpRequest($url)
{
$request = HTTPClient::start();
$request->setConfig(array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
return $request->get($url);
}

106
tests/oauth/fetch_temp_creds.php Executable file
View File

@ -0,0 +1,106 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/scripts/commandline.inc';
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
// Check to make sure we have everything we need from the ini file
foreach(array('consumer_key', 'consumer_secret', 'apiroot', 'request_token_url') as $inikey) {
if (empty($ini[$inikey])) {
print "You forgot to specify a $inikey in your oauth.ini file.\n";
exit(1);
}
}
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . $ini['request_token_url'];
$parsed = parse_url($endpoint);
$params = array();
parse_str($parsed['query'], $params);
$params['oauth_callback'] = 'oob'; // out-of-band
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
try {
$req = OAuthRequest::from_consumer_and_token(
$consumer,
null,
"POST",
$endpoint,
$params
);
$req->sign_request($hmac_method, $consumer, NULL);
$r = httpRequest($endpoint, $req->to_postdata());
} catch (Exception $e) {
// oh noez
print $e->getMessage();
print "\nOAuth Request:\n";
var_dump($req);
exit(1);
}
$body = $r->getBody();
$tokenStuff = array();
parse_str($body, $tokenStuff);
$tok = $tokenStuff['oauth_token'];
$confirmed = $tokenStuff['oauth_callback_confirmed'];
if (empty($tokenStuff['oauth_token'])
|| empty($tokenStuff['oauth_token_secret'])
|| empty($confirmed)
|| $confirmed != 'true')
{
print "Error! HTTP response body: $body\n";
exit(1);
}
$authurl = $ini['apiroot'] . $ini['authorize_url'] . '?oauth_token=' . $tok;
print "Request Token\n";
print ' - oauth_token = ' . $tokenStuff['oauth_token'] . "\n";
print ' - oauth_token_secret = ' . $tokenStuff['oauth_token_secret'] . "\n";
print "Authorize URL\n $authurl\n\n";
print "Now paste the Authorize URL into your browser and authorize your temporary credentials.\n";
function httpRequest($endpoint, $poststr)
{
$request = HTTPClient::start();
$request->setConfig(
array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
)
);
// Turn signed request query string back into an array
parse_str($poststr, $postdata);
return $request->post($endpoint, null, $postdata);
}

146
tests/oauth/fetch_token_creds.php Executable file
View File

@ -0,0 +1,146 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
// Check to make sure we have everything we need from the ini file
foreach(array('consumer_key', 'consumer_secret', 'apiroot', 'access_token_url') as $inikey) {
if (empty($ini[$inikey])) {
print "You forgot to specify a $inikey in your oauth.ini file.\n";
exit(1);
}
}
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . $ini['access_token_url'];
$shortoptions = 't:s:v:';
$longoptions = array('oauth_token=', 'oauth_token_secret=', 'oauth_verifier=');
$helptext = <<<END_OF_ETOKENS_HELP
fetch_token_creds.php [options]
Exchange authorized OAuth temporary credentials for token credentials
(an authorized request token for an access token)
-t --oauth_token authorized request token
-s --oauth_token_secret authorized request token secret
-v --oauth_verifier authorized request token verifier
END_OF_ETOKENS_HELP;
require_once INSTALLDIR . '/scripts/commandline.inc';
$token = $secret = $verifier = null;
if (have_option('t', 'oauth_token')) {
$token = get_option_value('t', 'oauth_token');
}
if (have_option('s', 'oauth_token_secret')) {
$secret = get_option_value('s', 'oauth_token_secret');
}
if (have_option('v', 'oauth_verifier')) {
$verifier = get_option_value('v', 'oauth_verifier');
}
if (empty($token)) {
print "Please specify the request token (--help for help).\n";
exit(1);
}
if (empty($secret)) {
print "Please specify the request token secret (--help for help).\n";
exit(1);
}
if (empty($verifier)) {
print "Please specify the request token verifier (--help for help).\n";
exit(1);
}
$rtok = new OAuthToken($token, $secret);
$parsed = parse_url($endpoint);
parse_str($parsed['query'], $params);
$params['oauth_verifier'] = $verifier; // 1.0a
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
try {
$oauthReq = OAuthRequest::from_consumer_and_token(
$consumer,
$rtok,
"POST",
$endpoint,
$params
);
$oauthReq->sign_request($hmac_method, $consumer, $rtok);
$httpReq = httpRequest($endpoint, $oauthReq->to_postdata());
$body = $httpReq->getBody();
} catch (Exception $e) {
// oh noez
print $e->getMessage();
print "\nOAuth Request:\n";
var_dump($oauthReq);
exit(1);
}
$tokenStuff = array();
parse_str($body, $tokenStuff);
if (empty($tokenStuff['oauth_token']) || empty($tokenStuff['oauth_token_secret'])) {
print "Error! HTTP response body: $body\n";
exit(1);
}
print "Access Token\n";
print ' - oauth_token = ' . $tokenStuff['oauth_token'] . "\n";
print ' - oauth_token_secret = ' . $tokenStuff['oauth_token_secret'] . "\n";
function httpRequest($endpoint, $poststr)
{
$request = HTTPClient::start();
$request->setConfig(
array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
)
);
parse_str($poststr, $postdata);
return $request->post($endpoint, null, $postdata);
}

View File

@ -1,71 +0,0 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/scripts/commandline.inc';
require_once INSTALLDIR . '/extlib/OAuth.php';
$ini = parse_ini_file("oauth.ini");
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$rt_endpoint = $ini['apiroot'] . $ini['request_token_url'];
$parsed = parse_url($rt_endpoint);
$params = array();
parse_str($parsed['query'], $params);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, NULL, "GET", $rt_endpoint, $params);
$req_req->sign_request($hmac_method, $test_consumer, NULL);
$r = httpRequest($req_req->to_url());
$body = $r->getBody();
$token_stuff = array();
parse_str($body, $token_stuff);
$authurl = $ini['apiroot'] . $ini['authorize_url'] . '?oauth_token=' . $token_stuff['oauth_token'];
print 'Request token : ' . $token_stuff['oauth_token'] . "\n";
print 'Request token secret : ' . $token_stuff['oauth_token_secret'] . "\n";
print "Authorize URL : $authurl\n";
//var_dump($req_req);
function httpRequest($url)
{
$request = HTTPClient::start();
$request->setConfig(array(
'follow_redirects' => true,
'connect_timeout' => 120,
'timeout' => 120,
'ssl_verify_peer' => false,
'ssl_verify_host' => false
));
return $request->get($url);
}

View File

@ -22,15 +22,15 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php'; require_once INSTALLDIR . '/extlib/OAuth.php';
$shortoptions = 'o:s:u:'; $shortoptions = 't:s:u:';
$longoptions = array('oauth_token=', 'token_secret=', 'update='); $longoptions = array('oauth_token=', 'token_secret=', 'update=');
$helptext = <<<END_OF_VERIFY_HELP $helptext = <<<END_OF_VERIFY_HELP
statusupdate.php [options] oauth_post_notice.php [options]
Update your status using OAuth Update your status via OAuth
-o --oauth_token access token -t --oauth_token access token
-s --token_secret access token secret -s --oauth_token_secret access token secret
-u --update status update -u --update status update
@ -42,12 +42,12 @@ $update = null;
require_once INSTALLDIR . '/scripts/commandline.inc'; require_once INSTALLDIR . '/scripts/commandline.inc';
if (have_option('o', 'oauth_token')) { if (have_option('t', 'oauth_token')) {
$token = get_option_value('oauth_token'); $token = get_option_value('t', 'oauth_token');
} }
if (have_option('s', 'token_secret')) { if (have_option('s', 'oauth_token_secret')) {
$token_secret = get_option_value('s', 'token_secret'); $token_secret = get_option_value('s', 'oauth_token_secret');
} }
if (have_option('u', 'update')) { if (have_option('u', 'update')) {
@ -70,46 +70,55 @@ if (empty($update)) {
} }
$ini = parse_ini_file("oauth.ini"); $ini = parse_ini_file("oauth.ini");
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . '/statuses/update.xml'; $endpoint = $ini['apiroot'] . '/statuses/update.xml';
print "$endpoint\n"; $atok = new OAuthToken($token, $token_secret);
$at = new OAuthToken($token, $token_secret);
$parsed = parse_url($endpoint); $parsed = parse_url($endpoint);
$params = array();
parse_str($parsed['query'], $params); parse_str($parsed['query'], $params);
$params['status'] = $update; $params['status'] = $update;
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); $hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $at, 'POST', $endpoint, $params); try {
$req_req->sign_request($hmac_method, $test_consumer, $at);
$r = httpRequest($req_req->to_url()); $oauthReq = OAuthRequest::from_consumer_and_token(
$consumer,
$atok,
'POST',
$endpoint,
$params
);
$body = $r->getBody(); $oauthReq->sign_request($hmac_method, $consumer, $atok);
print "$body\n"; $httpReq = httpRequest($endpoint, $oauthReq->to_postdata());
//print $req_req->to_url() . "\n\n"; print $httpReq->getBody();
function httpRequest($url) } catch (Exception $e) {
print "Error! . $e->getMessage() . 'HTTP reponse body: " . $httpReq->getBody();
exit(1);
}
function httpRequest($endpoint, $poststr)
{ {
$request = HTTPClient::start(); $request = HTTPClient::start();
$request->setConfig(array( $request->setConfig(
array(
'follow_redirects' => true, 'follow_redirects' => true,
'connect_timeout' => 120, 'connect_timeout' => 120,
'timeout' => 120, 'timeout' => 120,
'ssl_verify_peer' => false, 'ssl_verify_peer' => false,
'ssl_verify_host' => false 'ssl_verify_host' => false
)); )
);
return $request->post($url); // Turn signed request query string back into an array
parse_str($poststr, $postdata);
return $request->post($endpoint, null, $postdata);
} }

View File

@ -22,15 +22,15 @@ define('INSTALLDIR', realpath(dirname(__FILE__) . '/../..'));
require_once INSTALLDIR . '/extlib/OAuth.php'; require_once INSTALLDIR . '/extlib/OAuth.php';
$shortoptions = 'o:s:'; $shortoptions = 't:s:';
$longoptions = array('oauth_token=', 'token_secret='); $longoptions = array('oauth_token=', 'oauth_token_secret=');
$helptext = <<<END_OF_VERIFY_HELP $helptext = <<<END_OF_VERIFY_HELP
verifycreds.php [options] oauth_verify_creds.php [options]
Use an access token to verify credentials thru the api Access /api/account/verify_credentials.xml with OAuth
-o --oauth_token access token -t --oauth_token access token
-s --token_secret access token secret -s --oauth_token_secret access token secret
END_OF_VERIFY_HELP; END_OF_VERIFY_HELP;
@ -39,63 +39,69 @@ $token_secret = null;
require_once INSTALLDIR . '/scripts/commandline.inc'; require_once INSTALLDIR . '/scripts/commandline.inc';
if (have_option('o', 'oauth_token')) { if (have_option('t', 'oauth_token')) {
$token = get_option_value('oauth_token'); $token = get_option_value('t', 'oauth_token');
} }
if (have_option('s', 'token_secret')) { if (have_option('s', 'token_secret')) {
$token_secret = get_option_value('s', 'token_secret'); $token_secret = get_option_value('s', 'oauth_token_secret');
} }
if (empty($token)) { if (empty($token)) {
print "Please specify an access token.\n"; print "Please specify an access token (--help for help).\n";
exit(1); exit(1);
} }
if (empty($token_secret)) { if (empty($token_secret)) {
print "Please specify an access token secret.\n"; print "Please specify an access token secret (--help for help).\n";
exit(1); exit(1);
} }
$ini = parse_ini_file("oauth.ini"); $ini = parse_ini_file("oauth.ini");
$consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$test_consumer = new OAuthConsumer($ini['consumer_key'], $ini['consumer_secret']);
$endpoint = $ini['apiroot'] . '/account/verify_credentials.xml'; $endpoint = $ini['apiroot'] . '/account/verify_credentials.xml';
print "$endpoint\n"; $atok = new OAuthToken($token, $token_secret);
$at = new OAuthToken($token, $token_secret);
$parsed = parse_url($endpoint); $parsed = parse_url($endpoint);
$params = array();
parse_str($parsed['query'], $params); parse_str($parsed['query'], $params);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); try {
$req_req = OAuthRequest::from_consumer_and_token($test_consumer, $at, "GET", $endpoint, $params); $hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$req_req->sign_request($hmac_method, $test_consumer, $at);
$r = httpRequest($req_req->to_url()); $oauthReq = OAuthRequest::from_consumer_and_token(
$consumer,
$atok,
"GET",
$endpoint,
$params
);
$body = $r->getBody(); $oauthReq->sign_request($hmac_method, $consumer, $atok);
print "$body\n"; $httpReq = httpRequest($oauthReq->to_url());
//print $req_req->to_url() . "\n\n"; } catch (Exception $e) {
print "Error! HTTP response body: " . $httpReq->getBody();
exit(1);
}
print $httpReq->getBody();
function httpRequest($url) function httpRequest($url)
{ {
$request = HTTPClient::start(); $request = HTTPClient::start();
$request->setConfig(array( $request->setConfig(
array(
'follow_redirects' => true, 'follow_redirects' => true,
'connect_timeout' => 120, 'connect_timeout' => 120,
'timeout' => 120, 'timeout' => 120,
'ssl_verify_peer' => false, 'ssl_verify_peer' => false,
'ssl_verify_host' => false 'ssl_verify_host' => false
)); )
);
return $request->get($url); return $request->get($url);
} }