summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach Copley <zach@status.net>2010-10-06 19:20:47 -0700
committerZach Copley <zach@status.net>2010-10-06 19:20:47 -0700
commit69e621a3e882cd060eb4314554aada7167edd897 (patch)
tree9d6d31b7e72d8f22e1029a3a90316046d1d73828
parentf71912440a17f468b1d60db2388fc6030631fce6 (diff)
- Update ApiOauthAuthorizeAction to 1.0a
- Fix enumerable bugs - New page for displaying 1.0a verifier (still needs work)
-rw-r--r--actions/apioauthauthorize.php231
-rw-r--r--actions/apioauthpin.php69
-rw-r--r--lib/apioauth.php27
-rw-r--r--lib/apioauthstore.php8
-rw-r--r--lib/oauthstore.php11
-rw-r--r--lib/serverexception.php2
6 files changed, 270 insertions, 78 deletions
diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php
index c2fbbcdd8..6772052f2 100644
--- a/actions/apioauthauthorize.php
+++ b/actions/apioauthauthorize.php
@@ -32,6 +32,7 @@ if (!defined('STATUSNET')) {
}
require_once INSTALLDIR . '/lib/apioauth.php';
+require_once INSTALLDIR . '/lib/info.php';
/**
* Authorize an OAuth request token
@@ -43,9 +44,10 @@ require_once INSTALLDIR . '/lib/apioauth.php';
* @link http://status.net/
*/
-class ApiOauthAuthorizeAction extends ApiOauthAction
+class ApiOauthAuthorizeAction extends Action
{
- var $oauth_token;
+ var $oauthTokenParam;
+ var $reqToken;
var $callback;
var $app;
var $nickname;
@@ -67,12 +69,17 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
{
parent::prepare($args);
- $this->nickname = $this->trimmed('nickname');
- $this->password = $this->arg('password');
- $this->oauth_token = $this->arg('oauth_token');
- $this->callback = $this->arg('oauth_callback');
- $this->store = new ApiStatusNetOAuthDataStore();
- $this->app = $this->store->getAppByRequestToken($this->oauth_token);
+ $this->nickname = $this->trimmed('nickname');
+ $this->password = $this->arg('password');
+ $this->oauthTokenParam = $this->arg('oauth_token');
+ $this->callback = $this->arg('oauth_callback');
+ $this->store = new ApiStatusNetOAuthDataStore();
+
+ try {
+ $this->app = $this->store->getAppByRequestToken($this->oauthTokenParam);
+ } catch (Exception $e) {
+ $this->clientError($e->getMessage());
+ }
return true;
}
@@ -97,14 +104,30 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
} 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.'));
- 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)) {
- $this->clientError(_('Invalid token.'));
- return;
+ $this->clientError(_('Invalid request token.'));
}
$name = $this->app->name;
@@ -120,8 +143,8 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
- $this->showForm(_('There was a problem with your session token. '.
- 'Try again, please.'));
+ $this->showForm(
+ _('There was a problem with your session token. Try again, please.'));
return;
}
@@ -130,6 +153,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
$user = null;
if (!common_logged_in()) {
+
+ // XXX Force credentials check?
+
+ // XXX OpenID
+
$user = common_check_user($this->nickname, $this->password);
if (empty($user)) {
$this->showForm(_("Invalid nickname / password!"));
@@ -141,9 +169,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
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
// with this user/app and kill it. If the user is doing this she
@@ -156,8 +190,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!$result) {
common_log_db_error($appUser, 'DELETE', __FILE__);
- throw new ServerException(_('Database error deleting OAuth application user.'));
- return;
+ $this->serverError(_('Database error deleting OAuth application user.'));
}
}
@@ -175,20 +208,19 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// granted. The OAuth app user record then gets updated
// with the new access token and access type.
- $appUser->token = $this->oauth_token;
+ $appUser->token = $this->oauthTokenParam;
$appUser->created = common_sql_now();
$result = $appUser->insert();
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
- throw new ServerException(_('Database error inserting OAuth application user.'));
- return;
+ $this->serverError(_('Database error inserting OAuth application user.'));
}
- // 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.
if (!empty($this->app->callback_url)) {
@@ -197,40 +229,40 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
if (!empty($this->callback)) {
- $target_url = $this->getCallback($this->callback,
- array('oauth_token' => $this->oauth_token));
+ $targetUrl = $this->getCallback(
+ $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 {
- 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
-
- $this->elementStart('p');
-
- // XXX: Do OAuth 1.0a verifier code
-
- $this->raw(sprintf(_("The request token %s has been authorized. " .
- 'Please exchange it for an access token.'),
- $this->oauth_token));
+ // Otherwise, inform the user that the rt was authorized
+ $this->showAuthorized();
- $this->elementEnd('p');
+ } else if ($this->arg('cancel')) {
- } 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));
+ try {
+ $this->store->revoke_token($this->oauthTokenParam, 0);
+ $this->showCanceled();
+ } catch (Exception $e) {
+ $this->ServerError($e->getMessage());
+ }
- $this->elementEnd('p');
} else {
$this->clientError(_('Unexpected form submission.'));
- return;
}
}
@@ -276,7 +308,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
_('Allow or deny access'));
$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->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',
- 'name' => 'deny',
+ 'name' => 'cancel',
'type' => 'submit',
- 'value' => _('Deny')));
+ 'value' => _('Cancel')));
$this->element('input', array('id' => 'allow_submit',
'class' => 'submit submit form_action-secondary',
@@ -348,7 +380,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
function getInstructions()
{
- return _('Allow or deny access to your account information.');
+ return _('Authorize access to your account information.');
}
/**
@@ -388,4 +420,97 @@ class ApiOauthAuthorizeAction extends ApiOauthAction
// 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()
+ {
+
+ if ($this->reqToken->verified_callback == 'oob') {
+
+ $pin = new ApiOauthPinAction($this->reqToken->verifier);
+ $pin->showPage();
+
+ } else {
+
+ $info = new InfoAction(
+ _("Authorization succeeded."),
+ sprintf(
+ _('The request token %s has been authorized. Please exchange it for an access token using this verifier: %s'),
+ $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);
+ }
+ }
}
diff --git a/actions/apioauthpin.php b/actions/apioauthpin.php
new file mode 100644
index 000000000..5a88b5e59
--- /dev/null
+++ b/actions/apioauthpin.php
@@ -0,0 +1,69 @@
+<?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
+ *
+ * @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($verifier)
+ {
+ $this->verifier = $verifier;
+ $title = _('Authorization succeeded.');
+ parent::__construct($title, $title);
+ }
+
+ // TODO: Check for logged in state!
+
+ /**
+ * Display content.
+ *
+ * @return nothing
+ */
+ function showContent()
+ {
+ // XXX: make this much nicer
+ $this->element('div', array('class' => 'info'), $this->verifier);
+ }
+
+}
diff --git a/lib/apioauth.php b/lib/apioauth.php
index 75b0b3c57..54cecf92a 100644
--- a/lib/apioauth.php
+++ b/lib/apioauth.php
@@ -34,9 +34,8 @@ require_once INSTALLDIR . '/lib/apiaction.php';
require_once INSTALLDIR . '/lib/apioauthstore.php';
/**
- * Base action for API OAuth enpoints. Clean up the
- * the request, and possibly some other common things
- * here.
+ * Base action for API OAuth enpoints. Clean up the
+ * request. Some other common functions.
*
* @category API
* @package StatusNet
@@ -82,6 +81,7 @@ class ApiOauthAction extends ApiAction
* any extra parameters or anything else it's not expecting.
* I'm looking at you, p parameter.
*/
+
static function cleanRequest()
{
// kill evil effects of magical slashing
@@ -106,25 +106,4 @@ class ApiOauthAction extends ApiAction
$_SERVER['QUERY_STRING'] = implode('&', $queryArray);
}
- function getCallback($url, $params)
- {
- 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);
- }
- }
-
}
diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php
index 620f0947f..4d141286b 100644
--- a/lib/apioauthstore.php
+++ b/lib/apioauthstore.php
@@ -202,6 +202,14 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
$t->type = 0; // request
$t->state = 0; // unauthorized
$t->verified_callback = $callback;
+
+ if ($callback === 'oob') {
+ // six digit pin
+ $t->verifier = mt_rand(0, 999999);
+ } else {
+ $t->verifier = common_good_rand(8);
+ }
+
$t->created = DB_DataObject_Cast::dateTime();
if (!$t->insert()) {
return null;
diff --git a/lib/oauthstore.php b/lib/oauthstore.php
index f3ee629fd..537667678 100644
--- a/lib/oauthstore.php
+++ b/lib/oauthstore.php
@@ -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
// "The Consumer SHALL then generate a Nonce value that is unique for
// all requests with that timestamp."
diff --git a/lib/serverexception.php b/lib/serverexception.php
index 7dc9765ad..0dfbd04ff 100644
--- a/lib/serverexception.php
+++ b/lib/serverexception.php
@@ -22,7 +22,7 @@
* @category Exception
* @package StatusNet
* @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
* @link http://status.net/
*/