From: Mikael Nordfeldth Date: Sun, 6 Oct 2013 10:40:58 +0000 (+0200) Subject: OAuth related syntax fixes, nothing big X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=cc34bb48c7243f78e198ad4d8c1806d5fe886a81;p=quix0rs-gnu-social.git OAuth related syntax fixes, nothing big Making better use of class autoloading too. --- diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php index 76b06c28a2..f3d64de225 100644 --- a/actions/apioauthaccesstoken.php +++ b/actions/apioauthaccesstoken.php @@ -28,11 +28,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/apioauth.php'; +if (!defined('GNUSOCIAL')) { exit(1); } /** * Action for getting OAuth token credentials (exchange an authorized @@ -44,7 +40,7 @@ require_once INSTALLDIR . '/lib/apioauth.php'; * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ -class ApiOauthAccessTokenAction extends ApiOauthAction +class ApiOAuthAccessTokenAction extends ApiOAuthAction { protected $reqToken = null; protected $verifier = null; @@ -60,7 +56,7 @@ class ApiOauthAccessTokenAction extends ApiOauthAction { parent::handle($args); - $datastore = new ApiStatusNetOAuthDataStore(); + $datastore = new ApiGNUSocialOAuthDataStore(); $server = new OAuthServer($datastore); $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 1755c36be3..baaea6fd9b 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -31,9 +31,6 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/apioauth.php'; -require_once INSTALLDIR . '/lib/info.php'; - /** * Authorize an OAuth request token * @@ -43,7 +40,7 @@ require_once INSTALLDIR . '/lib/info.php'; * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ -class ApiOauthAuthorizeAction extends Action +class ApiOAuthAuthorizeAction extends ApiOAuthAction { var $oauthTokenParam; var $reqToken; @@ -71,7 +68,7 @@ class ApiOauthAuthorizeAction extends Action $this->password = $this->arg('password'); $this->oauthTokenParam = $this->arg('oauth_token'); $this->mode = $this->arg('mode'); - $this->store = new ApiStatusNetOAuthDataStore(); + $this->store = new ApiGNUSocialOAuthDataStore(); try { $this->app = $this->store->getAppByRequestToken($this->oauthTokenParam); @@ -367,7 +364,7 @@ class ApiOauthAuthorizeAction extends Action $this->elementStart('form', array('method' => 'post', 'id' => 'form_apioauthauthorize', 'class' => 'form_settings', - 'action' => common_local_url('ApiOauthAuthorize'))); + 'action' => common_local_url('ApiOAuthAuthorize'))); $this->elementStart('fieldset'); $this->element('legend', array('id' => 'apioauthauthorize_allowdeny'), // TRANS: Fieldset legend. @@ -613,7 +610,7 @@ class ApiOauthAuthorizeAction extends Action } if ($this->reqToken->verified_callback == 'oob') { - $pin = new ApiOauthPinAction( + $pin = new ApiOAuthPinAction( $title, $msg, $this->reqToken->verifier, diff --git a/actions/apioauthpin.php b/actions/apioauthpin.php index 22fb95adb5..ad4e7947e2 100644 --- a/actions/apioauthpin.php +++ b/actions/apioauthpin.php @@ -42,7 +42,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @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 +class ApiOAuthPinAction extends InfoAction { function __construct($title, $message, $verifier, $desktopMode = false) { diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php index 31e6387462..b9346a9e87 100644 --- a/actions/apioauthrequesttoken.php +++ b/actions/apioauthrequesttoken.php @@ -31,8 +31,6 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/apioauth.php'; - /** * Issue temporary OAuth credentials (a request token) * @@ -42,7 +40,7 @@ require_once INSTALLDIR . '/lib/apioauth.php'; * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 * @link http://status.net/ */ -class ApiOauthRequestTokenAction extends ApiOauthAction +class ApiOAuthRequestTokenAction extends ApiOAuthAction { /** * Take arguments for running @@ -75,7 +73,7 @@ class ApiOauthRequestTokenAction extends ApiOauthAction { parent::handle($args); - $datastore = new ApiStatusNetOAuthDataStore(); + $datastore = new ApiGNUSocialOAuthDataStore(); $server = new OAuthServer($datastore); $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); diff --git a/actions/oauthappssettings.php b/actions/oauthappssettings.php index c93a0a1580..d6477f6be9 100644 --- a/actions/oauthappssettings.php +++ b/actions/oauthappssettings.php @@ -32,7 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { } require_once INSTALLDIR . '/lib/applicationlist.php'; -require_once INSTALLDIR . '/lib/statusnetoauthstore.php'; /** * Show a user's registered OAuth applications diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index 1ad4a6f60d..43e6b45898 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -32,7 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { } require_once INSTALLDIR . '/lib/applicationlist.php'; -require_once INSTALLDIR . '/lib/statusnetoauthstore.php'; /** * Show connected OAuth applications @@ -169,7 +168,7 @@ class OauthconnectionssettingsAction extends SettingsAction $app = Oauth_application::getKV('id', $appUser->application_id); - $datastore = new ApiStatusNetOAuthDataStore(); + $datastore = new ApiGNUSocialOAuthDataStore(); $datastore->revoke_token($appUser->token, 1); $result = $appUser->delete(); diff --git a/actions/showapplication.php b/actions/showapplication.php index b15c830ad8..acee1862f4 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -261,13 +261,13 @@ class ShowApplicationAction extends Action $this->element('dd', null, $consumer->consumer_secret); // TRANS: Field label on application page. $this->element('dt', null, _('Request token URL')); - $this->element('dd', null, common_local_url('ApiOauthRequestToken')); + $this->element('dd', null, common_local_url('ApiOAuthRequestToken')); // TRANS: Field label on application page. $this->element('dt', null, _('Access token URL')); - $this->element('dd', null, common_local_url('ApiOauthAccessToken')); + $this->element('dd', null, common_local_url('ApiOAuthAccessToken')); // TRANS: Field label on application page. $this->element('dt', null, _('Authorize URL')); - $this->element('dd', null, common_local_url('ApiOauthAuthorize')); + $this->element('dd', null, common_local_url('ApiOAuthAuthorize')); $this->elementEnd('dl'); $this->element('p', 'note', diff --git a/lib/apiauthaction.php b/lib/apiauthaction.php index 499c502480..54b05b3d52 100644 --- a/lib/apiauthaction.php +++ b/lib/apiauthaction.php @@ -57,8 +57,6 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/apioauth.php'; - /** * Actions extending this class will require auth * @@ -133,7 +131,7 @@ class ApiAuthAction extends ApiAction */ function getOAuthRequest() { - ApiOauthAction::cleanRequest(); + ApiOAuthAction::cleanRequest(); $req = OAuthRequest::from_request(); @@ -160,7 +158,7 @@ class ApiAuthAction extends ApiAction */ function checkOAuthRequest($request) { - $datastore = new ApiStatusNetOAuthDataStore(); + $datastore = new ApiGNUSocialOAuthDataStore(); $server = new OAuthServer($datastore); $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); diff --git a/lib/apignusocialoauthdatastore.php b/lib/apignusocialoauthdatastore.php new file mode 100644 index 0000000000..5a0c2a8243 --- /dev/null +++ b/lib/apignusocialoauthdatastore.php @@ -0,0 +1,506 @@ +. + */ + +if (!defined('GNUSOCIAL')) { exit(1); } + +require_once 'OAuth.php'; + +/** + * @fixme class doc + */ +class ApiGNUSocialOAuthDataStore extends OAuthDataStore +{ + function lookup_consumer($consumerKey) + { + $con = Consumer::getKV('consumer_key', $consumerKey); + + if (!$con) { + + // Create an anon consumer and anon application if one + // doesn't exist already + if ($consumerKey == 'anonymous') { + + common_debug("API OAuth - creating anonymous consumer"); + $con = new Consumer(); + $con->consumer_key = $consumerKey; + $con->consumer_secret = $consumerKey; + $con->created = common_sql_now(); + + $result = $con->insert(); + if (!$result) { + // TRANS: Server error displayed when trying to create an anynymous OAuth consumer. + $this->serverError(_('Could not create anonymous consumer.')); + } + + $app = Oauth_application::getByConsumerKey('anonymous'); + + if (!$app) { + common_debug("API OAuth - creating anonymous application"); + $app = new OAuth_application(); + $app->owner = 1; // XXX: What to do here? + $app->consumer_key = $con->consumer_key; + $app->name = 'anonymous'; + $app->icon = 'default-avatar-stream.png'; // XXX: Fix this! + $app->description = "An anonymous application"; + // XXX: allow the user to set the access type when + // authorizing? Currently we default to r+w for anonymous + // OAuth client applications + $app->access_type = 3; // read + write + $app->type = 2; // desktop + $app->created = common_sql_now(); + + $id = $app->insert(); + + if (!$id) { + // TRANS: Server error displayed when trying to create an anynymous OAuth application. + $this->serverError(_("Could not create anonymous OAuth application.")); + } + } + } else { + return null; + } + } + + return new OAuthConsumer( + $con->consumer_key, + $con->consumer_secret + ); + } + + function getAppByRequestToken($token_key) + { + // Look up the full req token + $req_token = $this->lookup_token( + null, + 'request', + $token_key + ); + + if (empty($req_token)) { + common_debug("Couldn't get request token from oauth datastore"); + return null; + } + + // Look up the full Token + $token = new Token(); + $token->tok = $req_token->key; + $result = $token->find(true); + + if (empty($result)) { + common_debug('Couldn\'t find req token in the token table.'); + return null; + } + + // Look up the app + $app = new Oauth_application(); + $app->consumer_key = $token->consumer_key; + $result = $app->find(true); + + if (!empty($result)) { + return $app; + } else { + common_debug("Couldn't find the app!"); + return null; + } + } + + function new_access_token($token, $consumer, $verifier = null) + { + common_debug( + sprintf( + "New access token from request token %s, consumer %s and verifier %s ", + $token, + $consumer, + $verifier + ), + __FILE__ + ); + + $rt = new Token(); + + $rt->consumer_key = $consumer->key; + $rt->tok = $token->key; + $rt->type = 0; // request + + $app = Oauth_application::getByConsumerKey($consumer->key); + assert(!empty($app)); + + if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized + + common_debug('Request token found.', __FILE__); + + // find the app and profile associated with this token + $tokenAssoc = Oauth_token_association::getKV('token', $rt->tok); + + if (!$tokenAssoc) { + throw new Exception( + // TRANS: Exception thrown when no token association could be found. + _('Could not find a profile and application associated with the request token.') + ); + } + + // Check to see if we have previously issued an access token for + // this application and profile; if so we can just return the + // existing access token. That seems to be the best practice. It + // makes it so users only have to authorize the app once per + // machine. + + $appUser = new Oauth_application_user(); + + $appUser->application_id = $app->id; + $appUser->profile_id = $tokenAssoc->profile_id; + + $result = $appUser->find(true); + + if (!empty($result)) { + + common_log(LOG_INFO, + sprintf( + "Existing access token found for application %s, profile %s.", + $app->id, + $tokenAssoc->profile_id + ) + ); + + $at = null; + + // Special case: we used to store request tokens in the + // Oauth_application_user record, and the access_type would + // always be 0 (no access) as a failsafe until an access + // token was issued and replaced the request token. There could + // be a few old Oauth_application_user records storing request + // tokens still around, and we don't want to accidentally + // return a useless request token instead of a new access + // token. So if we find one, we generate a new access token + // and update the existing Oauth_application_user record before + // returning the new access token. This should be rare. + + if ($appUser->access_type == 0) { + + $at = $this->generateNewAccessToken($consumer, $rt, $verifier); + $this->updateAppUser($appUser, $app, $at); + + } else { + + $at = new Token(); + + // fetch the full access token + $at->consumer_key = $consumer->key; + $at->tok = $appUser->token; + + $result = $at->find(true); + + if (!$result) { + throw new Exception( + // TRANS: Exception thrown when no access token can be issued. + _('Could not issue access token.') + ); + } + } + + // Yay, we can re-issue the access token + return new OAuthToken($at->tok, $at->secret); + + } else { + + common_log(LOG_INFO, + sprintf( + "Creating new access token for application %s, profile %s.", + $app->id, + $tokenAssoc->profile_id + ) + ); + + $at = $this->generateNewAccessToken($consumer, $rt, $verifier); + $this->newAppUser($tokenAssoc, $app, $at); + + // Okay, good + return new OAuthToken($at->tok, $at->secret); + } + + } else { + + // the token was not authorized or not verfied + common_log( + LOG_INFO, + sprintf( + "API OAuth - Attempt to exchange unauthorized or unverified request token %s for an access token.", + $rt->tok + ) + ); + return null; + } + } + + /* + * Generate a new access token and save it to the database + * + * @param Consumer $consumer the OAuth consumer + * @param Token $rt the authorized request token + * @param string $verifier the OAuth 1.0a verifier + * + * @access private + * + * @return Token $at the new access token + */ + private function generateNewAccessToken($consumer, $rt, $verifier) + { + $at = new Token(); + + $at->consumer_key = $consumer->key; + $at->tok = common_good_rand(16); + $at->secret = common_good_rand(16); + $at->type = 1; // access + $at->verifier = $verifier; + $at->verified_callback = $rt->verified_callback; // 1.0a + $at->created = common_sql_now(); + + if (!$at->insert()) { + $e = $at->_lastError; + common_debug('access token "' . $at->tok . '" not inserted: "' . $e->message . '"', __FILE__); + return null; + } else { + common_debug('access token "' . $at->tok . '" inserted', __FILE__); + // burn the old one + $orig_rt = clone($rt); + $rt->state = 2; // used + if (!$rt->update($orig_rt)) { + return null; + } + common_debug('request token "' . $rt->tok . '" updated', __FILE__); + } + + return $at; + } + + /* + * Add a new app user (Oauth_application_user) record + * + * @param Oauth_token_association $tokenAssoc token-to-app association + * @param Oauth_application $app the OAuth client app + * @param Token $at the access token + * + * @access private + * + * @return void + */ + private function newAppUser($tokenAssoc, $app, $at) + { + $appUser = new Oauth_application_user(); + + $appUser->profile_id = $tokenAssoc->profile_id; + $appUser->application_id = $app->id; + $appUser->access_type = $app->access_type; + $appUser->token = $at->tok; + $appUser->created = common_sql_now(); + + $result = $appUser->insert(); + + if (!$result) { + common_log_db_error($appUser, 'INSERT', __FILE__); + + throw new Exception( + // TRANS: Exception thrown when a database error occurs. + _('Database error inserting OAuth application user.') + ); + } + } + + /* + * Update an existing app user (Oauth_application_user) record + * + * @param Oauth_application_user $appUser existing app user rec + * @param Oauth_application $app the OAuth client app + * @param Token $at the access token + * + * @access private + * + * @return void + */ + private function updateAppUser($appUser, $app, $at) + { + $original = clone($appUser); + $appUser->access_type = $app->access_type; + $appUser->token = $at->tok; + + $result = $appUser->update($original); + + if (!$result) { + common_log_db_error($appUser, 'UPDATE', __FILE__); + throw new Exception( + // TRANS: Exception thrown when a database error occurs. + _('Database error updating OAuth application user.') + ); + } + } + + /** + * Revoke specified access token + * + * Revokes the token specified by $token_key. + * Throws exceptions in case of error. + * + * @param string $token_key the token to be revoked + * @param int $type type of token (0 = req, 1 = access) + * + * @access public + * + * @return void + */ + public function revoke_token($token_key, $type = 0) { + $rt = new Token(); + $rt->tok = $token_key; + $rt->type = $type; + $rt->state = 0; + + if (!$rt->find(true)) { + // TRANS: Exception thrown when an attempt is made to revoke an unknown token. + throw new Exception(_('Tried to revoke unknown token.')); + } + + if (!$rt->delete()) { + // TRANS: Exception thrown when an attempt is made to remove a 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 = common_sql_now(); + if (!$t->insert()) { + return null; + } else { + return new OAuthToken($t->tok, $t->secret); + } + } + + /** + * Authorize specified OAuth token + * + * Authorizes the authorization token specified by $token_key. + * Throws exceptions in case of error. + * + * @param string $token_key The token to be authorized + * + * @access public + **/ + public function authorize_token($token_key) { + $rt = new Token(); + $rt->tok = $token_key; + $rt->type = 0; + $rt->state = 0; + if (!$rt->find(true)) { + throw new Exception('Tried to authorize unknown token'); + } + $orig_rt = clone($rt); + $rt->state = 1; # Authorized but not used + if (!$rt->update($orig_rt)) { + throw new Exception('Failed to authorize token'); + } + } + + /** + * + * http://oauth.net/core/1.0/#nonce + * "The Consumer SHALL then generate a Nonce value that is unique for + * all requests with that timestamp." + * XXX: It's not clear why the token is here + * + * @param type $consumer + * @param type $token + * @param type $nonce + * @param type $timestamp + * @return type + */ + function lookup_nonce($consumer, $token, $nonce, $timestamp) + { + $n = new Nonce(); + $n->consumer_key = $consumer->key; + $n->ts = common_sql_date($timestamp); + $n->nonce = $nonce; + if ($n->find(true)) { + return true; + } else { + $n->created = DB_DataObject_Cast::dateTime(); + $n->insert(); + return false; + } + } + + /** + * + * @param type $consumer + * @param type $token_type + * @param type $token_key + * @return OAuthToken + */ + function lookup_token($consumer, $token_type, $token_key) + { + $t = new Token(); + if (!is_null($consumer)) { + $t->consumer_key = $consumer->key; + } + $t->tok = $token_key; + $t->type = ($token_type == 'access') ? 1 : 0; + if ($t->find(true)) { + return new OAuthToken($t->tok, $t->secret); + } else { + return null; + } + } + + /** + * + * @param type $token_key + * @return Token + */ + function getTokenByKey($token_key) + { + $t = new Token(); + $t->tok = $token_key; + if ($t->find(true)) { + return $t; + } else { + return null; + } + } +} diff --git a/lib/apioauth.php b/lib/apioauth.php deleted file mode 100644 index a93811cc0f..0000000000 --- a/lib/apioauth.php +++ /dev/null @@ -1,109 +0,0 @@ -. - * - * @category API - * @package StatusNet - * @author Zach Copley - * @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')) { - exit(1); -} -require_once INSTALLDIR . '/lib/apiaction.php'; -require_once INSTALLDIR . '/lib/statusnetoauthstore.php'; - -/** - * Base action for API OAuth enpoints. Clean up the - * request. Some other common functions. - * - * @category API - * @package StatusNet - * @author Zach Copley - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ -class ApiOauthAction extends ApiAction -{ - /** - * Is this a read-only action? - * - * @return boolean false - */ - function isReadOnly($args) - { - return false; - } - - function prepare($args) - { - parent::prepare($args); - return true; - } - - /** - * Handle input, produce output - * - * Switches on request method; either shows the form or handles its input. - * - * @param array $args $_REQUEST data - * - * @return void - */ - function handle($args) - { - parent::handle($args); - 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() - { - // 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 - unset($_GET['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]); - } - } - - $_SERVER['QUERY_STRING'] = implode('&', $queryArray); - } - -} diff --git a/lib/apioauthaction.php b/lib/apioauthaction.php new file mode 100644 index 0000000000..8da7e5c61c --- /dev/null +++ b/lib/apioauthaction.php @@ -0,0 +1,108 @@ +. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @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')) { + exit(1); +} +require_once INSTALLDIR . '/lib/apiaction.php'; + +/** + * Base action for API OAuth enpoints. Clean up the + * request. Some other common functions. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ +class ApiOAuthAction extends ApiAction +{ + /** + * Is this a read-only action? + * + * @return boolean false + */ + function isReadOnly($args) + { + return false; + } + + function prepare($args) + { + parent::prepare($args); + return true; + } + + /** + * Handle input, produce output + * + * Switches on request method; either shows the form or handles its input. + * + * @param array $args $_REQUEST data + * + * @return void + */ + function handle($args) + { + parent::handle($args); + 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() + { + // 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 + unset($_GET['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]); + } + } + + $_SERVER['QUERY_STRING'] = implode('&', $queryArray); + } + +} diff --git a/lib/error.php b/lib/error.php index c24f3bbf43..e7bdd05707 100644 --- a/lib/error.php +++ b/lib/error.php @@ -33,8 +33,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once INSTALLDIR . '/lib/info.php'; - /** * Base class for displaying HTTP errors * diff --git a/lib/info.php b/lib/info.php deleted file mode 100644 index f72bed59d6..0000000000 --- a/lib/info.php +++ /dev/null @@ -1,124 +0,0 @@ - - * @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 . - */ - -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 - * @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->elementStart('div', array('id' => 'aside_primary_wrapper')); - $this->elementStart('div', array('id' => 'content_wrapper')); - $this->elementStart('div', array('id' => 'site_nav_local_views_wrapper')); - $this->showContentBlock(); - $this->elementEnd('div'); - $this->elementEnd('div'); - $this->elementEnd('div'); - $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); - } - -} diff --git a/lib/infoaction.php b/lib/infoaction.php new file mode 100644 index 0000000000..f72bed59d6 --- /dev/null +++ b/lib/infoaction.php @@ -0,0 +1,124 @@ + + * @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 . + */ + +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 + * @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->elementStart('div', array('id' => 'aside_primary_wrapper')); + $this->elementStart('div', array('id' => 'content_wrapper')); + $this->elementStart('div', array('id' => 'site_nav_local_views_wrapper')); + $this->showContentBlock(); + $this->elementEnd('div'); + $this->elementEnd('div'); + $this->elementEnd('div'); + $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); + } + +} diff --git a/lib/router.php b/lib/router.php index 0180594e26..a7b59e6ab4 100644 --- a/lib/router.php +++ b/lib/router.php @@ -787,13 +787,13 @@ class Router $m->connect('api/trends.json', array('action' => 'ApiTrends')); $m->connect('api/oauth/request_token', - array('action' => 'ApiOauthRequestToken')); + array('action' => 'ApiOAuthRequestToken')); $m->connect('api/oauth/access_token', - array('action' => 'ApiOauthAccessToken')); + array('action' => 'ApiOAuthAccessToken')); $m->connect('api/oauth/authorize', - array('action' => 'ApiOauthAuthorize')); + array('action' => 'ApiOAuthAuthorize')); // Admin diff --git a/lib/statusnetoauthstore.php b/lib/statusnetoauthstore.php deleted file mode 100644 index bcf6a180aa..0000000000 --- a/lib/statusnetoauthstore.php +++ /dev/null @@ -1,506 +0,0 @@ -. - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } - -require_once 'OAuth.php'; - -/** - * @fixme class doc - */ -class ApiStatusNetOAuthDataStore extends OAuthDataStore -{ - function lookup_consumer($consumerKey) - { - $con = Consumer::getKV('consumer_key', $consumerKey); - - if (!$con) { - - // Create an anon consumer and anon application if one - // doesn't exist already - if ($consumerKey == 'anonymous') { - - common_debug("API OAuth - creating anonymous consumer"); - $con = new Consumer(); - $con->consumer_key = $consumerKey; - $con->consumer_secret = $consumerKey; - $con->created = common_sql_now(); - - $result = $con->insert(); - if (!$result) { - // TRANS: Server error displayed when trying to create an anynymous OAuth consumer. - $this->serverError(_('Could not create anonymous consumer.')); - } - - $app = Oauth_application::getByConsumerKey('anonymous'); - - if (!$app) { - common_debug("API OAuth - creating anonymous application"); - $app = new OAuth_application(); - $app->owner = 1; // XXX: What to do here? - $app->consumer_key = $con->consumer_key; - $app->name = 'anonymous'; - $app->icon = 'default-avatar-stream.png'; // XXX: Fix this! - $app->description = "An anonymous application"; - // XXX: allow the user to set the access type when - // authorizing? Currently we default to r+w for anonymous - // OAuth client applications - $app->access_type = 3; // read + write - $app->type = 2; // desktop - $app->created = common_sql_now(); - - $id = $app->insert(); - - if (!$id) { - // TRANS: Server error displayed when trying to create an anynymous OAuth application. - $this->serverError(_("Could not create anonymous OAuth application.")); - } - } - } else { - return null; - } - } - - return new OAuthConsumer( - $con->consumer_key, - $con->consumer_secret - ); - } - - function getAppByRequestToken($token_key) - { - // Look up the full req token - $req_token = $this->lookup_token( - null, - 'request', - $token_key - ); - - if (empty($req_token)) { - common_debug("Couldn't get request token from oauth datastore"); - return null; - } - - // Look up the full Token - $token = new Token(); - $token->tok = $req_token->key; - $result = $token->find(true); - - if (empty($result)) { - common_debug('Couldn\'t find req token in the token table.'); - return null; - } - - // Look up the app - $app = new Oauth_application(); - $app->consumer_key = $token->consumer_key; - $result = $app->find(true); - - if (!empty($result)) { - return $app; - } else { - common_debug("Couldn't find the app!"); - return null; - } - } - - function new_access_token($token, $consumer, $verifier) - { - common_debug( - sprintf( - "New access token from request token %s, consumer %s and verifier %s ", - $token, - $consumer, - $verifier - ), - __FILE__ - ); - - $rt = new Token(); - - $rt->consumer_key = $consumer->key; - $rt->tok = $token->key; - $rt->type = 0; // request - - $app = Oauth_application::getByConsumerKey($consumer->key); - assert(!empty($app)); - - if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized - - common_debug('Request token found.', __FILE__); - - // find the app and profile associated with this token - $tokenAssoc = Oauth_token_association::getKV('token', $rt->tok); - - if (!$tokenAssoc) { - throw new Exception( - // TRANS: Exception thrown when no token association could be found. - _('Could not find a profile and application associated with the request token.') - ); - } - - // Check to see if we have previously issued an access token for - // this application and profile; if so we can just return the - // existing access token. That seems to be the best practice. It - // makes it so users only have to authorize the app once per - // machine. - - $appUser = new Oauth_application_user(); - - $appUser->application_id = $app->id; - $appUser->profile_id = $tokenAssoc->profile_id; - - $result = $appUser->find(true); - - if (!empty($result)) { - - common_log(LOG_INFO, - sprintf( - "Existing access token found for application %s, profile %s.", - $app->id, - $tokenAssoc->profile_id - ) - ); - - $at = null; - - // Special case: we used to store request tokens in the - // Oauth_application_user record, and the access_type would - // always be 0 (no access) as a failsafe until an access - // token was issued and replaced the request token. There could - // be a few old Oauth_application_user records storing request - // tokens still around, and we don't want to accidentally - // return a useless request token instead of a new access - // token. So if we find one, we generate a new access token - // and update the existing Oauth_application_user record before - // returning the new access token. This should be rare. - - if ($appUser->access_type == 0) { - - $at = $this->generateNewAccessToken($consumer, $rt, $verifier); - $this->updateAppUser($appUser, $app, $at); - - } else { - - $at = new Token(); - - // fetch the full access token - $at->consumer_key = $consumer->key; - $at->tok = $appUser->token; - - $result = $at->find(true); - - if (!$result) { - throw new Exception( - // TRANS: Exception thrown when no access token can be issued. - _('Could not issue access token.') - ); - } - } - - // Yay, we can re-issue the access token - return new OAuthToken($at->tok, $at->secret); - - } else { - - common_log(LOG_INFO, - sprintf( - "Creating new access token for application %s, profile %s.", - $app->id, - $tokenAssoc->profile_id - ) - ); - - $at = $this->generateNewAccessToken($consumer, $rt, $verifier); - $this->newAppUser($tokenAssoc, $app, $at); - - // Okay, good - return new OAuthToken($at->tok, $at->secret); - } - - } else { - - // the token was not authorized or not verfied - common_log( - LOG_INFO, - sprintf( - "API OAuth - Attempt to exchange unauthorized or unverified request token %s for an access token.", - $rt->tok - ) - ); - return null; - } - } - - /* - * Generate a new access token and save it to the database - * - * @param Consumer $consumer the OAuth consumer - * @param Token $rt the authorized request token - * @param string $verifier the OAuth 1.0a verifier - * - * @access private - * - * @return Token $at the new access token - */ - private function generateNewAccessToken($consumer, $rt, $verifier) - { - $at = new Token(); - - $at->consumer_key = $consumer->key; - $at->tok = common_good_rand(16); - $at->secret = common_good_rand(16); - $at->type = 1; // access - $at->verifier = $verifier; - $at->verified_callback = $rt->verified_callback; // 1.0a - $at->created = common_sql_now(); - - if (!$at->insert()) { - $e = $at->_lastError; - common_debug('access token "' . $at->tok . '" not inserted: "' . $e->message . '"', __FILE__); - return null; - } else { - common_debug('access token "' . $at->tok . '" inserted', __FILE__); - // burn the old one - $orig_rt = clone($rt); - $rt->state = 2; // used - if (!$rt->update($orig_rt)) { - return null; - } - common_debug('request token "' . $rt->tok . '" updated', __FILE__); - } - - return $at; - } - - /* - * Add a new app user (Oauth_application_user) record - * - * @param Oauth_token_association $tokenAssoc token-to-app association - * @param Oauth_application $app the OAuth client app - * @param Token $at the access token - * - * @access private - * - * @return void - */ - private function newAppUser($tokenAssoc, $app, $at) - { - $appUser = new Oauth_application_user(); - - $appUser->profile_id = $tokenAssoc->profile_id; - $appUser->application_id = $app->id; - $appUser->access_type = $app->access_type; - $appUser->token = $at->tok; - $appUser->created = common_sql_now(); - - $result = $appUser->insert(); - - if (!$result) { - common_log_db_error($appUser, 'INSERT', __FILE__); - - throw new Exception( - // TRANS: Exception thrown when a database error occurs. - _('Database error inserting OAuth application user.') - ); - } - } - - /* - * Update an existing app user (Oauth_application_user) record - * - * @param Oauth_application_user $appUser existing app user rec - * @param Oauth_application $app the OAuth client app - * @param Token $at the access token - * - * @access private - * - * @return void - */ - private function updateAppUser($appUser, $app, $at) - { - $original = clone($appUser); - $appUser->access_type = $app->access_type; - $appUser->token = $at->tok; - - $result = $appUser->update($original); - - if (!$result) { - common_log_db_error($appUser, 'UPDATE', __FILE__); - throw new Exception( - // TRANS: Exception thrown when a database error occurs. - _('Database error updating OAuth application user.') - ); - } - } - - /** - * Revoke specified access token - * - * Revokes the token specified by $token_key. - * Throws exceptions in case of error. - * - * @param string $token_key the token to be revoked - * @param int $type type of token (0 = req, 1 = access) - * - * @access public - * - * @return void - */ - public function revoke_token($token_key, $type = 0) { - $rt = new Token(); - $rt->tok = $token_key; - $rt->type = $type; - $rt->state = 0; - - if (!$rt->find(true)) { - // TRANS: Exception thrown when an attempt is made to revoke an unknown token. - throw new Exception(_('Tried to revoke unknown token.')); - } - - if (!$rt->delete()) { - // TRANS: Exception thrown when an attempt is made to remove a 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); - } - } - - /** - * Authorize specified OAuth token - * - * Authorizes the authorization token specified by $token_key. - * Throws exceptions in case of error. - * - * @param string $token_key The token to be authorized - * - * @access public - **/ - public function authorize_token($token_key) { - $rt = new Token(); - $rt->tok = $token_key; - $rt->type = 0; - $rt->state = 0; - if (!$rt->find(true)) { - throw new Exception('Tried to authorize unknown token'); - } - $orig_rt = clone($rt); - $rt->state = 1; # Authorized but not used - if (!$rt->update($orig_rt)) { - throw new Exception('Failed to authorize token'); - } - } - - /** - * - * http://oauth.net/core/1.0/#nonce - * "The Consumer SHALL then generate a Nonce value that is unique for - * all requests with that timestamp." - * XXX: It's not clear why the token is here - * - * @param type $consumer - * @param type $token - * @param type $nonce - * @param type $timestamp - * @return type - */ - function lookup_nonce($consumer, $token, $nonce, $timestamp) - { - $n = new Nonce(); - $n->consumer_key = $consumer->key; - $n->ts = common_sql_date($timestamp); - $n->nonce = $nonce; - if ($n->find(true)) { - return true; - } else { - $n->created = DB_DataObject_Cast::dateTime(); - $n->insert(); - return false; - } - } - - /** - * - * @param type $consumer - * @param type $token_type - * @param type $token_key - * @return OAuthToken - */ - function lookup_token($consumer, $token_type, $token_key) - { - $t = new Token(); - if (!is_null($consumer)) { - $t->consumer_key = $consumer->key; - } - $t->tok = $token_key; - $t->type = ($token_type == 'access') ? 1 : 0; - if ($t->find(true)) { - return new OAuthToken($t->tok, $t->secret); - } else { - return null; - } - } - - /** - * - * @param type $token_key - * @return Token - */ - function getTokenByKey($token_key) - { - $t = new Token(); - $t->tok = $token_key; - if ($t->find(true)) { - return $t; - } else { - return null; - } - } -} diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php index 0e05983806..e48b616f4d 100644 --- a/plugins/OpenID/OpenIDPlugin.php +++ b/plugins/OpenID/OpenIDPlugin.php @@ -738,7 +738,7 @@ class OpenIDPlugin extends Plugin oid_assert_allowed($openid_url); $returnto = common_local_url( - 'ApiOauthAuthorize', + 'ApiOAuthAuthorize', array(), array( 'oauth_token' => $action->arg('oauth_token'),