From 52397f14741463cd518512e2f024b3ea7e18e136 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 1 Feb 2010 20:31:56 +0100 Subject: Sentence case for app statistics --- actions/showapplication.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/showapplication.php b/actions/showapplication.php index a6ff425c7..090e11882 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -201,7 +201,7 @@ class ShowApplicationAction extends OwnerDesignAction $userCnt = $appUsers->count(); $this->raw(sprintf( - _('created by %1$s - %2$s access by default - %3$d users'), + _('Created by %1$s - %2$s access by default - %3$d users'), $profile->getBestName(), $defaultAccess, $userCnt -- cgit v1.2.3-54-g00ecf From 8a0a89196043bc12e1fafea6d4638db5e61a181a Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 1 Feb 2010 20:32:18 +0100 Subject: Prevents app statistic text from wrapping around avatar --- theme/base/css/display.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 0d6395d05..2240e42af 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -632,7 +632,8 @@ margin-bottom:18px; .entity_profile .entity_url, .entity_profile .entity_note, .entity_profile .entity_tags, -.entity_profile .entity_aliases { +.entity_profile .entity_aliases, +.entity_profile .entity_statistics { margin-left:113px; margin-bottom:4px; } -- cgit v1.2.3-54-g00ecf From dc183f23cf3bd8e0fbd604ad2af4b12f77837bf2 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 1 Feb 2010 20:58:29 +0000 Subject: OAuth app names should be unique. --- actions/editapplication.php | 24 ++++++++++++++++++++++++ actions/newapplication.php | 20 ++++++++++++++++++++ classes/statusnet.ini | 3 ++- db/statusnet.sql | 2 +- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/actions/editapplication.php b/actions/editapplication.php index 9cc3e3cea..029b622e8 100644 --- a/actions/editapplication.php +++ b/actions/editapplication.php @@ -179,6 +179,9 @@ class EditApplicationAction extends OwnerDesignAction } elseif (mb_strlen($name) > 255) { $this->showForm(_('Name is too long (max 255 chars).')); return; + } else if ($this->nameExists($name)) { + $this->showForm(_('Name already in use. Try another one.')); + return; } elseif (empty($description)) { $this->showForm(_('Description is required.')); return; @@ -260,5 +263,26 @@ class EditApplicationAction extends OwnerDesignAction common_redirect(common_local_url('oauthappssettings'), 303); } + /** + * Does the app name already exist? + * + * Checks the DB to see someone has already registered and app + * with the same name. + * + * @param string $name app name to check + * + * @return boolean true if the name already exists + */ + + function nameExists($name) + { + $newapp = Oauth_application::staticGet('name', $name); + if (!$newapp) { + return false; + } else { + return $newapp->id != $this->app->id; + } + } + } diff --git a/actions/newapplication.php b/actions/newapplication.php index c499fe7c7..ba1cca5c9 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -158,6 +158,9 @@ class NewApplicationAction extends OwnerDesignAction if (empty($name)) { $this->showForm(_('Name is required.')); return; + } else if ($this->nameExists($name)) { + $this->showForm(_('Name already in use. Try another one.')); + return; } elseif (mb_strlen($name) > 255) { $this->showForm(_('Name is too long (max 255 chars).')); return; @@ -273,5 +276,22 @@ class NewApplicationAction extends OwnerDesignAction } + /** + * Does the app name already exist? + * + * Checks the DB to see someone has already registered and app + * with the same name. + * + * @param string $name app name to check + * + * @return boolean true if the name already exists + */ + + function nameExists($name) + { + $app = Oauth_application::staticGet('name', $name); + return ($app !== false); + } + } diff --git a/classes/statusnet.ini b/classes/statusnet.ini index e28424ce2..a535159e8 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -353,7 +353,7 @@ notice_id = K id = 129 owner = 129 consumer_key = 130 -name = 130 +name = 2 description = 2 icon = 130 source_url = 2 @@ -367,6 +367,7 @@ modified = 384 [oauth_application__keys] id = N +name = U [oauth_application_user] profile_id = 129 diff --git a/db/statusnet.sql b/db/statusnet.sql index 17de4fd0d..71a6e724c 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -214,7 +214,7 @@ create table oauth_application ( id integer auto_increment primary key comment 'unique identifier', owner integer not null comment 'owner of the application' references profile (id), consumer_key varchar(255) not null comment 'application consumer key' references consumer (consumer_key), - name varchar(255) not null comment 'name of the application', + name varchar(255) unique key comment 'name of the application', description varchar(255) comment 'description of the application', icon varchar(255) not null comment 'application icon', source_url varchar(255) comment 'application homepage - used for source link', -- cgit v1.2.3-54-g00ecf From e495ac356c10a6abc0e10c81892830b5e198ef60 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 2 Feb 2010 06:26:03 +0000 Subject: Allow developers to delete OAuth applications --- actions/deleteapplication.php | 176 ++++++++++++++++++++++++++++++++++++++++++ actions/showapplication.php | 19 ++++- classes/Consumer.php | 30 +++++++ classes/Oauth_application.php | 17 ++++ lib/router.php | 4 + 5 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 actions/deleteapplication.php diff --git a/actions/deleteapplication.php b/actions/deleteapplication.php new file mode 100644 index 000000000..17526e111 --- /dev/null +++ b/actions/deleteapplication.php @@ -0,0 +1,176 @@ +. + * + * @category Action + * @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') && !defined('LACONICA')) { + exit(1); +} + +/** + * Delete an OAuth appliction + * + * @category Action + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + */ + +class DeleteapplicationAction extends Action +{ + var $app = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + */ + + function prepare($args) + { + if (!parent::prepare($args)) { + return false; + } + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to delete an application.')); + return false; + } + + $id = (int)$this->arg('id'); + $this->app = Oauth_application::staticGet('id', $id); + + if (empty($this->app)) { + $this->clientError(_('Application not found.')); + return false; + } + + $cur = common_current_user(); + + if ($cur->id != $this->app->owner) { + $this->clientError(_('You are not the owner of this application.'), 401); + return false; + } + + return true; + } + + /** + * Handle request + * + * Shows a page with list of favorite notices + * + * @param array $args $_REQUEST args; handled in prepare() + * + * @return void + */ + + function handle($args) + { + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + if ($this->arg('no')) { + common_redirect(common_local_url('showapplication', + array('id' => $this->app->id)), 303); + } elseif ($this->arg('yes')) { + $this->handlePost(); + common_redirect(common_local_url('oauthappssettings'), 303); + } else { + $this->showPage(); + } + } + } + + function showContent() { + $this->areYouSureForm(); + } + + function title() { + return _('Delete application'); + } + + function showNoticeForm() { + // nop + } + + /** + * Confirm with user. + * + * Shows a confirmation form. + * + * @return void + */ + function areYouSureForm() + { + $id = $this->app->id; + $this->elementStart('form', array('id' => 'deleteapplication-' . $id, + 'method' => 'post', + 'class' => 'form_settings form_entity_block', + 'action' => common_local_url('deleteapplication', + array('id' => $this->app->id)))); + $this->elementStart('fieldset'); + $this->hidden('token', common_session_token()); + $this->element('legend', _('Delete application')); + $this->element('p', null, + _('Are you sure you want to delete this application? '. + 'This will clear all data about the application from the '. + 'database, including all existing user connections.')); + $this->submit('form_action-no', + _('No'), + 'submit form_action-primary', + 'no', + _("Do not delete this application")); + $this->submit('form_action-yes', + _('Yes'), + 'submit form_action-secondary', + 'yes', _('Delete this application')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + } + + /** + * Actually delete the app + * + * @return void + */ + + function handlePost() + { + $this->app->delete(); + } +} + diff --git a/actions/showapplication.php b/actions/showapplication.php index 090e11882..020d62480 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -222,18 +222,33 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementStart('li', 'entity_reset_keysecret'); $this->elementStart('form', array( - 'id' => 'forma_reset_key', + 'id' => 'form_reset_key', 'class' => 'form_reset_key', 'method' => 'POST', 'action' => common_local_url('showapplication', array('id' => $this->application->id)))); - $this->elementStart('fieldset'); $this->hidden('token', common_session_token()); $this->submit('reset', _('Reset key & secret')); $this->elementEnd('fieldset'); $this->elementEnd('form'); $this->elementEnd('li'); + + $this->elementStart('li', 'entity_delete'); + $this->elementStart('form', array( + 'id' => 'form_delete_application', + 'class' => 'form_delete_application', + 'method' => 'POST', + 'action' => common_local_url('deleteapplication', + array('id' => $this->application->id)))); + + $this->elementStart('fieldset'); + $this->hidden('token', common_session_token()); + $this->submit('delete', _('Delete')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + $this->elementEnd('li'); + $this->elementEnd('ul'); $this->elementEnd('div'); diff --git a/classes/Consumer.php b/classes/Consumer.php index ad64a8491..ce399f278 100644 --- a/classes/Consumer.php +++ b/classes/Consumer.php @@ -36,4 +36,34 @@ class Consumer extends Memcached_DataObject return $cons; } + /** + * Delete a Consumer and related tokens and nonces + * + * XXX: Should this happen in an OAuthDataStore instead? + * + */ + function delete() + { + // XXX: Is there any reason NOT to do this kind of cleanup? + + $this->_deleteTokens(); + $this->_deleteNonces(); + + parent::delete(); + } + + function _deleteTokens() + { + $token = new Token(); + $token->consumer_key = $this->consumer_key; + $token->delete(); + } + + function _deleteNonces() + { + $nonce = new Nonce(); + $nonce->consumer_key = $this->consumer_key; + $nonce->delete(); + } + } diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php index a6b539087..748b64220 100644 --- a/classes/Oauth_application.php +++ b/classes/Oauth_application.php @@ -137,4 +137,21 @@ class Oauth_application extends Memcached_DataObject } } + function delete() + { + $this->_deleteAppUsers(); + + $consumer = $this->getConsumer(); + $consumer->delete(); + + parent::delete(); + } + + function _deleteAppUsers() + { + $oauser = new Oauth_application_user(); + $oauser->application_id = $this->id; + $oauser->delete(); + } + } diff --git a/lib/router.php b/lib/router.php index 4b5b8d0bb..5981ef5d7 100644 --- a/lib/router.php +++ b/lib/router.php @@ -152,6 +152,10 @@ class Router array('action' => 'editapplication'), array('id' => '[0-9]+') ); + $m->connect('settings/oauthapps/delete/:id', + array('action' => 'deleteapplication'), + array('id' => '[0-9]+') + ); // search -- cgit v1.2.3-54-g00ecf From b31c79cee1565ca9bca5bcaffcbec04ddb312041 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 2 Feb 2010 07:35:54 +0000 Subject: Better token revocation --- actions/apioauthauthorize.php | 22 ++++++---------------- actions/oauthconnectionssettings.php | 24 +++++++++++++++--------- db/statusnet.sql | 2 +- lib/apioauthstore.php | 27 +++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 15c3a9dad..05d925d26 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -99,24 +99,17 @@ class ApiOauthAuthorizeAction extends ApiOauthAction } else { - // XXX: make better error messages - if (empty($this->oauth_token)) { - - common_debug("No request token found."); - - $this->clientError(_('Bad request.')); + $this->clientError(_('No oauth_token parameter provided.')); return; } if (empty($this->app)) { - common_debug('No app for that token.'); - $this->clientError(_('Bad request.')); + $this->clientError(_('Invalid token.')); return; } $name = $this->app->name; - common_debug("Requesting auth for app: " . $name); $this->showForm(); } @@ -124,8 +117,6 @@ class ApiOauthAuthorizeAction extends ApiOauthAction function handlePost() { - common_debug("handlePost()"); - // check session token for CSRF protection. $token = $this->trimmed('token'); @@ -210,13 +201,9 @@ class ApiOauthAuthorizeAction extends ApiOauthAction if (!empty($this->callback)) { - // XXX: Need better way to build this redirect url. - $target_url = $this->getCallback($this->callback, array('oauth_token' => $this->oauth_token)); - common_debug("Doing callback to $target_url"); - common_redirect($target_url, 303); } else { common_debug("callback was empty!"); @@ -236,9 +223,12 @@ class ApiOauthAuthorizeAction extends ApiOauthAction } 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."), + $this->raw(sprintf(_("The request token %s has been denied and revoked."), $this->oauth_token)); $this->elementEnd('p'); diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index c2e8d441b..b1467f0d0 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -33,6 +33,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { require_once INSTALLDIR . '/lib/connectsettingsaction.php'; require_once INSTALLDIR . '/lib/applicationlist.php'; +require_once INSTALLDIR . '/lib/apioauthstore.php'; /** * Show connected OAuth applications @@ -71,11 +72,6 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction return _('Connected applications'); } - function isReadOnly($args) - { - return true; - } - /** * Instructions for use * @@ -153,6 +149,13 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction } } + /** + * Revoke access to an authorized OAuth application + * + * @param int $appId the ID of the application + * + */ + function revokeAccess($appId) { $cur = common_current_user(); @@ -164,6 +167,8 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction return false; } + // XXX: Transaction here? + $appUser = Oauth_application_user::getByKeys($cur, $app); if (empty($appUser)) { @@ -171,12 +176,13 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction return false; } - $orig = clone($appUser); - $appUser->access_type = 0; // No access - $result = $appUser->update(); + $datastore = new ApiStatusNetOAuthDataStore(); + $datastore->revoke_token($appUser->token, 1); + + $result = $appUser->delete(); if (!$result) { - common_log_db_error($orig, 'UPDATE', __FILE__); + common_log_db_error($orig, 'DELETE', __FILE__); $this->clientError(_('Unable to revoke access for app: ' . $app->id)); return false; } diff --git a/db/statusnet.sql b/db/statusnet.sql index 71a6e724c..8946f4d7e 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -230,7 +230,7 @@ create table oauth_application ( create table oauth_application_user ( profile_id integer not null comment 'user of the application' references profile (id), application_id integer not null comment 'id of the application' references oauth_application (id), - access_type tinyint default 0 comment 'access type, bit 1 = read, bit 2 = write, bit 3 = revoked', + access_type tinyint default 0 comment 'access type, bit 1 = read, bit 2 = write', token varchar(255) comment 'request or access token', created datetime not null comment 'date this record was created', modified timestamp comment 'date this record was modified', diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index 32110d057..1bb11cbca 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -159,5 +159,32 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore } } + /** + * 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)) { + throw new Exception('Tried to revoke unknown token'); + } + if (!$rt->delete()) { + throw new Exception('Failed to delete revoked token'); + } + } + } -- cgit v1.2.3-54-g00ecf From e9ecd8062a5d8223b7c0914255a24288c317d2a1 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 2 Feb 2010 07:59:28 +0000 Subject: Suppress notice input box on OAuth authorization page --- actions/apioauthauthorize.php | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 05d925d26..2caa8d20b 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -67,8 +67,6 @@ class ApiOauthAuthorizeAction extends ApiOauthAction { parent::prepare($args); - common_debug("apioauthauthorize"); - $this->nickname = $this->trimmed('nickname'); $this->password = $this->arg('password'); $this->oauth_token = $this->arg('oauth_token'); @@ -193,8 +191,6 @@ class ApiOauthAuthorizeAction extends ApiOauthAction // A callback specified in the app setup overrides whatever // is passed in with the request. - common_debug("Req token is authorized - doing callback"); - if (!empty($this->app->callback_url)) { $this->callback = $this->app->callback_url; } @@ -295,12 +291,15 @@ class ApiOauthAuthorizeAction extends ApiOauthAction $msg = _('The application %1$s by ' . '%2$s would like the ability ' . - 'to %3$s your account data.'); + 'to %3$s your %4$s account data. ' . + 'You should only give access to your %4$s account ' . + 'to third parties you trust.'); $this->raw(sprintf($msg, $this->app->name, $this->app->organization, - $access)); + $access, + common_config('site', 'name'))); $this->elementEnd('p'); $this->elementEnd('li'); $this->elementEnd('ul'); @@ -362,6 +361,31 @@ class ApiOauthAuthorizeAction extends ApiOauthAction function showLocalNav() { + // NOP + } + + /** + * Show site notice. + * + * @return nothing + */ + + function showSiteNotice() + { + // NOP + } + + /** + * Show notice form. + * + * Show the form for posting a new notice + * + * @return nothing + */ + + function showNoticeForm() + { + // NOP } } -- cgit v1.2.3-54-g00ecf From 54171248847e0c535697c6b1e8ff0e89f42f0087 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 2 Feb 2010 08:47:14 +0000 Subject: Linkify notice source when posting from registered OAuth apps --- lib/api.php | 19 ++++++++++++++++++- lib/noticelist.php | 20 ++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/api.php b/lib/api.php index 10a2fae28..f81975216 100644 --- a/lib/api.php +++ b/lib/api.php @@ -1249,10 +1249,27 @@ class ApiAction extends Action case 'api': break; default: + + $name = null; + $url = null; + $ns = Notice_source::staticGet($source); + if ($ns) { - $source_name = '' . $ns->name . ''; + $name = $ns->name; + $url = $ns->url; + } else { + $app = Oauth_application::staticGet('name', $source); + if ($app) { + $name = $app->name; + $url = $app->source_url; + } + } + + if (!empty($name) && !empty($url)) { + $source_name = '' . $name . ''; } + break; } return $source_name; diff --git a/lib/noticelist.php b/lib/noticelist.php index 85c169716..a4a0f2651 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -486,12 +486,28 @@ class NoticeListItem extends Widget $this->out->element('span', 'device', $source_name); break; default: + + $name = null; + $url = null; + $ns = Notice_source::staticGet($this->notice->source); + if ($ns) { + $name = $ns->name; + $url = $ns->url; + } else { + $app = Oauth_application::staticGet('name', $this->notice->source); + if ($app) { + $name = $app->name; + $url = $app->source_url; + } + } + + if (!empty($name) && !empty($url)) { $this->out->elementStart('span', 'device'); - $this->out->element('a', array('href' => $ns->url, + $this->out->element('a', array('href' => $url, 'rel' => 'external'), - $ns->name); + $name); $this->out->elementEnd('span'); } else { $this->out->element('span', 'device', $source_name); -- cgit v1.2.3-54-g00ecf From 4041a59282c5ebb751e3763b5489be2bfef7f74a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 2 Feb 2010 23:16:44 +0000 Subject: Always check for an OAuth request. This allows OAuth clients to set an auth user, similar to how they can set one via http basic auth, even if one is not required. I think I finally got this right. --- lib/apiauth.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/apiauth.php b/lib/apiauth.php index 99500404f..25e2196cf 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -55,6 +55,7 @@ class ApiAuthAction extends ApiAction { var $auth_user_nickname = null; var $auth_user_password = null; + var $oauth_source = null; /** * Take arguments for running, looks for an OAuth request, @@ -73,20 +74,18 @@ class ApiAuthAction extends ApiAction // NOTE: $this->auth_user has to get set in prepare(), not handle(), // because subclasses do stuff with it in their prepares. - if ($this->requiresAuth()) { + $oauthReq = $this->getOAuthRequest(); - $oauthReq = $this->getOAuthRequest(); - - if (!$oauthReq) { + if (!$oauthReq) { + if ($this->requiresAuth()) { $this->checkBasicAuthUser(true); } else { - $this->checkOAuthRequest($oauthReq); + // Check to see if a basic auth user is there even + // if one's not required + $this->checkBasicAuthUser(false); } } else { - - // Check to see if a basic auth user is there even - // if one's not required - $this->checkBasicAuthUser(false); + $this->checkOAuthRequest($oauthReq); } // Reject API calls with the wrong access level @@ -108,7 +107,6 @@ class ApiAuthAction extends ApiAction * This is to avoid doign any unnecessary DB lookups. * * @return mixed the OAuthRequest or false - * */ function getOAuthRequest() @@ -137,7 +135,6 @@ class ApiAuthAction extends ApiAction * @param OAuthRequest $request the OAuth Request * * @return nothing - * */ function checkOAuthRequest($request) -- cgit v1.2.3-54-g00ecf From 7931875bbbfb127c0fa2f49331c137f0c6f1824a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 3 Feb 2010 01:43:59 +0000 Subject: Confirm dialog for reset OAuth consumer key and secret button --- actions/editapplication.php | 2 +- actions/newapplication.php | 2 +- actions/showapplication.php | 54 ++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/actions/editapplication.php b/actions/editapplication.php index 029b622e8..ca5dba1e4 100644 --- a/actions/editapplication.php +++ b/actions/editapplication.php @@ -266,7 +266,7 @@ class EditApplicationAction extends OwnerDesignAction /** * Does the app name already exist? * - * Checks the DB to see someone has already registered and app + * Checks the DB to see someone has already registered an app * with the same name. * * @param string $name app name to check diff --git a/actions/newapplication.php b/actions/newapplication.php index ba1cca5c9..c0c520797 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -279,7 +279,7 @@ class NewApplicationAction extends OwnerDesignAction /** * Does the app name already exist? * - * Checks the DB to see someone has already registered and app + * Checks the DB to see someone has already registered an app * with the same name. * * @param string $name app name to check diff --git a/actions/showapplication.php b/actions/showapplication.php index 020d62480..fa4484481 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -149,7 +149,6 @@ class ShowApplicationAction extends OwnerDesignAction function showContent() { - $cur = common_current_user(); $consumer = $this->application->getConsumer(); @@ -229,7 +228,13 @@ class ShowApplicationAction extends OwnerDesignAction array('id' => $this->application->id)))); $this->elementStart('fieldset'); $this->hidden('token', common_session_token()); - $this->submit('reset', _('Reset key & secret')); + + $this->element('input', array('type' => 'submit', + 'id' => 'reset', + 'name' => 'reset', + 'class' => 'submit', + 'value' => _('Reset key & secret'), + 'onClick' => 'return confirmReset()')); $this->elementEnd('fieldset'); $this->elementEnd('form'); $this->elementEnd('li'); @@ -291,14 +296,53 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementEnd('p'); } + /** + * Add a confirm script for Consumer key/secret reset + * + * @return void + */ + + function showScripts() + { + parent::showScripts(); + + $msg = _('Are you sure you want to reset your consumer key and secret?'); + + $js = 'function confirmReset() { '; + $js .= ' var agree = confirm("' . $msg . '"); '; + $js .= ' return agree;'; + $js .= '}'; + + $this->inlineScript($js); + } + + /** + * Reset an application's Consumer key and secret + * + * XXX: Should this be moved to its own page with a confirm? + * + */ + function resetKey() { $this->application->query('BEGIN'); + $oauser = new Oauth_application_user(); + $oauser->application_id = $this->application->id; + $result = $oauser->delete(); + + if ($result === false) { + common_log_db_error($oauser, 'DELETE', __FILE__); + $this->success = false; + $this->msg = ('Unable to reset consumer key and secret.'); + $this->showPage(); + return; + } + $consumer = $this->application->getConsumer(); $result = $consumer->delete(); - if (!$result) { + if ($result === false) { common_log_db_error($consumer, 'DELETE', __FILE__); $this->success = false; $this->msg = ('Unable to reset consumer key and secret.'); @@ -310,7 +354,7 @@ class ShowApplicationAction extends OwnerDesignAction $result = $consumer->insert(); - if (!$result) { + if (empty($result)) { common_log_db_error($consumer, 'INSERT', __FILE__); $this->application->query('ROLLBACK'); $this->success = false; @@ -323,7 +367,7 @@ class ShowApplicationAction extends OwnerDesignAction $this->application->consumer_key = $consumer->consumer_key; $result = $this->application->update($orig); - if (!$result) { + if ($result === false) { common_log_db_error($application, 'UPDATE', __FILE__); $this->application->query('ROLLBACK'); $this->success = false; -- cgit v1.2.3-54-g00ecf From 586d8e8524236c2682287f6a3b45fb572b3e3181 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 3 Feb 2010 18:13:21 +0100 Subject: Added right margin for notice text. Helps Conversation notices look better. --- theme/base/css/display.css | 1 + 1 file changed, 1 insertion(+) diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 2240e42af..ed8853e57 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -1024,6 +1024,7 @@ float:none; } #content .notice .entry-title { margin-left:59px; +margin-right:7px; } .vcard .url { -- cgit v1.2.3-54-g00ecf From af9f23c2d9db2966284e5146026ec05d4bb37367 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 4 Feb 2010 01:53:08 +0000 Subject: - Fix cache handling in TwitterStatusFetcher - Other stability fixes --- .../TwitterBridge/daemons/twitterstatusfetcher.php | 53 ++++++++++++++++++---- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/plugins/TwitterBridge/daemons/twitterstatusfetcher.php b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php index 36732ce46..bff657eb6 100755 --- a/plugins/TwitterBridge/daemons/twitterstatusfetcher.php +++ b/plugins/TwitterBridge/daemons/twitterstatusfetcher.php @@ -2,7 +2,7 @@ is_local = Notice::GATEWAY; if (Event::handle('StartNoticeSave', array(&$notice))) { - $id = $notice->insert(); + $notice->insert(); Event::handle('EndNoticeSave', array($notice)); } @@ -270,17 +270,41 @@ class TwitterStatusFetcher extends ParallelizingDaemon Inbox::insertNotice($flink->user_id, $notice->id); - $notice->blowCaches(); + $notice->blowOnInsert(); return $notice; } + /** + * Look up a Profile by profileurl field. Profile::staticGet() was + * not working consistently. + * + * @param string $url the profile url + * + * @return mixed the first profile with that url, or null + */ + + function getProfileByUrl($nickname, $profileurl) + { + $profile = new Profile(); + $profile->nickname = $nickname; + $profile->profileurl = $profileurl; + $profile->limit(1); + + if ($profile->find()) { + $profile->fetch(); + return $profile; + } + + return null; + } + function ensureProfile($user) { // check to see if there's already a profile for this user $profileurl = 'http://twitter.com/' . $user->screen_name; - $profile = Profile::staticGet('profileurl', $profileurl); + $profile = $this->getProfileByUrl($user->screen_name, $profileurl); if (!empty($profile)) { common_debug($this->name() . @@ -292,6 +316,7 @@ class TwitterStatusFetcher extends ParallelizingDaemon return $profile->id; } else { + common_debug($this->name() . ' - Adding profile and remote profile ' . "for Twitter user: $profileurl."); @@ -306,7 +331,11 @@ class TwitterStatusFetcher extends ParallelizingDaemon $profile->profileurl = $profileurl; $profile->created = common_sql_now(); - $id = $profile->insert(); + try { + $id = $profile->insert(); + } catch(Exception $e) { + common_log(LOG_WARNING, $this->name . ' Couldn\'t insert profile - ' . $e->getMessage()); + } if (empty($id)) { common_log_db_error($profile, 'INSERT', __FILE__); @@ -326,7 +355,11 @@ class TwitterStatusFetcher extends ParallelizingDaemon $remote_pro->uri = $profileurl; $remote_pro->created = common_sql_now(); - $rid = $remote_pro->insert(); + try { + $rid = $remote_pro->insert(); + } catch (Exception $e) { + common_log(LOG_WARNING, $this->name() . ' Couldn\'t save remote profile - ' . $e->getMessage()); + } if (empty($rid)) { common_log_db_error($profile, 'INSERT', __FILE__); @@ -446,7 +479,7 @@ class TwitterStatusFetcher extends ParallelizingDaemon if ($this->fetchAvatar($url, $filename)) { $this->newAvatar($id, $size, $mediatype, $filename); } else { - common_log(LOG_WARNING, $this->id() . + common_log(LOG_WARNING, $id() . " - Problem fetching Avatar: $url"); } } @@ -507,7 +540,11 @@ class TwitterStatusFetcher extends ParallelizingDaemon $avatar->created = common_sql_now(); - $id = $avatar->insert(); + try { + $id = $avatar->insert(); + } catch (Exception $e) { + common_log(LOG_WARNING, $this->name() . ' Couldn\'t insert avatar - ' . $e->getMessage()); + } if (empty($id)) { common_log_db_error($avatar, 'INSERT', __FILE__); -- cgit v1.2.3-54-g00ecf From 4379027432b4d35b60649624466a4c0e2abb5271 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 5 Feb 2010 01:13:23 +0000 Subject: Fix issue with OAuth request parameters being parsed/stored twice when calling /api/account/verify_credentials.:format --- actions/apiaccountverifycredentials.php | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/actions/apiaccountverifycredentials.php b/actions/apiaccountverifycredentials.php index 1095d5162..ea61a3205 100644 --- a/actions/apiaccountverifycredentials.php +++ b/actions/apiaccountverifycredentials.php @@ -66,18 +66,21 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction { parent::handle($args); - switch ($this->format) { - case 'xml': - case 'json': - $args['id'] = $this->auth_user->id; - $action_obj = new ApiUserShowAction(); - if ($action_obj->prepare($args)) { - $action_obj->handle($args); - } - break; - default: - header('Content-Type: text/html; charset=utf-8'); - print 'Authorized'; + if (!in_array($this->format, array('xml', 'json'))) { + $this->clientError(_('API method not found.'), $code = 404); + return; + } + + $twitter_user = $this->twitterUserArray($this->auth_user->getProfile(), true); + + if ($this->format == 'xml') { + $this->initDocument('xml'); + $this->showTwitterXmlUser($twitter_user); + $this->endDocument('xml'); + } elseif ($this->format == 'json') { + $this->initDocument('json'); + $this->showJsonObjects($twitter_user); + $this->endDocument('json'); } } @@ -86,14 +89,14 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction * Is this action read only? * * @param array $args other arguments - * + * * @return boolean true * **/ - + function isReadOnly($args) { return true; } - + } -- cgit v1.2.3-54-g00ecf From 208eec6511b13635b5feb8f100078f401cb0ce20 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 5 Feb 2010 01:24:21 +0000 Subject: OAuth app name should not be null --- classes/statusnet.ini | 2 +- db/statusnet.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/statusnet.ini b/classes/statusnet.ini index a535159e8..5f8da7cf5 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -353,7 +353,7 @@ notice_id = K id = 129 owner = 129 consumer_key = 130 -name = 2 +name = 130 description = 2 icon = 130 source_url = 2 diff --git a/db/statusnet.sql b/db/statusnet.sql index 8946f4d7e..343464801 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -214,7 +214,7 @@ create table oauth_application ( id integer auto_increment primary key comment 'unique identifier', owner integer not null comment 'owner of the application' references profile (id), consumer_key varchar(255) not null comment 'application consumer key' references consumer (consumer_key), - name varchar(255) unique key comment 'name of the application', + name varchar(255) not null unique key comment 'name of the application', description varchar(255) comment 'description of the application', icon varchar(255) not null comment 'application icon', source_url varchar(255) comment 'application homepage - used for source link', -- cgit v1.2.3-54-g00ecf From 857494c9c61d872b7decf69de226bba6cd250d99 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 5 Feb 2010 01:38:29 +0000 Subject: Actually store the timestamp on each nonce --- lib/oauthstore.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/oauthstore.php b/lib/oauthstore.php index b30fb49d5..eabe37f9f 100644 --- a/lib/oauthstore.php +++ b/lib/oauthstore.php @@ -65,7 +65,7 @@ class StatusNetOAuthDataStore extends OAuthDataStore { $n = new Nonce(); $n->consumer_key = $consumer->key; - $n->ts = $timestamp; + $n->ts = common_sql_date($timestamp); $n->nonce = $nonce; if ($n->find(true)) { return true; @@ -362,7 +362,6 @@ class StatusNetOAuthDataStore extends OAuthDataStore array('is_local' => Notice::REMOTE_OMB, 'uri' => $omb_notice->getIdentifierURI())); - } /** -- cgit v1.2.3-54-g00ecf From 21c0e75a2e52d63eb46de6f5938b00c4c9ba8323 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 5 Feb 2010 21:39:29 -0800 Subject: Store Twitter screen_name, not name, for foreign_user.nickname when saving Twitter user. --- plugins/TwitterBridge/twitterauthorization.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/TwitterBridge/twitterauthorization.php b/plugins/TwitterBridge/twitterauthorization.php index b2657ff61..dbef438a4 100644 --- a/plugins/TwitterBridge/twitterauthorization.php +++ b/plugins/TwitterBridge/twitterauthorization.php @@ -219,7 +219,7 @@ class TwitterauthorizationAction extends Action $user = common_current_user(); $this->saveForeignLink($user->id, $twitter_user->id, $atok); - save_twitter_user($twitter_user->id, $twitter_user->name); + save_twitter_user($twitter_user->id, $twitter_user->screen_name); } else { -- cgit v1.2.3-54-g00ecf From c83d0b5e98fc6e59632a0fa1335b3586996929e2 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sat, 6 Feb 2010 06:46:00 +0000 Subject: Delete old Twitter user record when user changes screen name instead of updating. Simpler. --- plugins/TwitterBridge/twitter.php | 54 ++++++--------------------------------- 1 file changed, 8 insertions(+), 46 deletions(-) diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index 33dfb788b..de30d9ebf 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -26,38 +26,6 @@ define('TWITTER_SERVICE', 1); // Twitter is foreign_service ID 1 require_once INSTALLDIR . '/plugins/TwitterBridge/twitterbasicauthclient.php'; require_once INSTALLDIR . '/plugins/TwitterBridge/twitteroauthclient.php'; -function updateTwitter_user($twitter_id, $screen_name) -{ - $uri = 'http://twitter.com/' . $screen_name; - $fuser = new Foreign_user(); - - $fuser->query('BEGIN'); - - // Dropping down to SQL because regular DB_DataObject udpate stuff doesn't seem - // to work so good with tables that have multiple column primary keys - - // Any time we update the uri for a forein user we have to make sure there - // are no dupe entries first -- unique constraint on the uri column - - $qry = 'UPDATE foreign_user set uri = \'\' WHERE uri = '; - $qry .= '\'' . $uri . '\'' . ' AND service = ' . TWITTER_SERVICE; - - $fuser->query($qry); - - // Update the user - - $qry = 'UPDATE foreign_user SET nickname = '; - $qry .= '\'' . $screen_name . '\'' . ', uri = \'' . $uri . '\' '; - $qry .= 'WHERE id = ' . $twitter_id . ' AND service = ' . TWITTER_SERVICE; - - $fuser->query('COMMIT'); - - $fuser->free(); - unset($fuser); - - return true; -} - function add_twitter_user($twitter_id, $screen_name) { @@ -105,7 +73,6 @@ function add_twitter_user($twitter_id, $screen_name) // Creates or Updates a Twitter user function save_twitter_user($twitter_id, $screen_name) { - // Check to see whether the Twitter user is already in the system, // and update its screen name and uri if so. @@ -115,25 +82,20 @@ function save_twitter_user($twitter_id, $screen_name) $result = true; - // Only update if Twitter screen name has changed + // Delete old record if Twitter user changed screen name if ($fuser->nickname != $screen_name) { - $result = updateTwitter_user($twitter_id, $screen_name); - - common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' . - "$fuser->id to $screen_name, was $fuser->nickname"); + $oldname = $fuser->nickname; + $fuser->delete(); + common_log(LOG_INFO, sprintf('Twitter bridge - Updated nickname (and URI) ' . + 'for Twitter user %1$d - %2$s, was %3$s.', + $fuser->id, + $screen_name, + $oldname)); } - return $result; - - } else { return add_twitter_user($twitter_id, $screen_name); } - - $fuser->free(); - unset($fuser); - - return true; } function is_twitter_bound($notice, $flink) { -- cgit v1.2.3-54-g00ecf From 9cac8eaae5315f64e024d22119bc627e9bdd6141 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 1 Feb 2010 13:44:06 -0500 Subject: readme and version for beta5 --- README | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- lib/common.php | 2 +- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/README b/README index 4e576dcdd..9b4147645 100644 --- a/README +++ b/README @@ -2,8 +2,8 @@ README ------ -StatusNet 0.9.0 ("Stand") Beta 4 -27 Jan 2010 +StatusNet 0.9.0 ("Stand") Beta 5 +1 Feb 2010 This is the README file for StatusNet (formerly Laconica), the Open Source microblogging platform. It includes installation instructions, @@ -78,6 +78,11 @@ New this version ================ This is a major feature release since version 0.8.2, released Nov 1 2009. +It is also a security release since 0.9.0beta4 January 27 2010. Beta +users are strongly encouraged to upgrade to deal with a security alert. + +http://status.net/wiki/Security_alert_0000002 + Notable changes this version: - Records of deleted notices are stored without the notice content. @@ -198,6 +203,77 @@ Notable changes this version: - Major refactoring of queue handlers to manage very large hosting site (like status.net) - SubscriptionThrottle plugin to prevent subscription spamming +- Don't enqueue into plugin or SMS queues when disabled (breaks unqueuehandler if SMS queue isn't attached) +- Improve name validation checks on local File references +- fix local file include vulnerability in doc.php +- Reusing fixed selector name for 'processing' in util.js +- Removed hAtom pattern from registration page. +- restructuring of User::registerNew() lost password munging +- Add a script to clear the cache for a given key +- buggy fetch for site owner +- Added missing concat of in Realtime response +- Updated XHR binded events to work better in jQuery 1.4.1. Using .live() for event delegation instead of jQuery.data() and checking to see if an element was previously binded. +- Updated jQuery Form Plugin from v2.17 to v2.36 +- Updated jQuery JavaScript Library from v1.3.2 to v1.4.1 +- move schema.type.php to typeschema.php like other files +- Add Really Simple Discovery (RSD) support +- Add a robots.txt URL to the site root +- error clearing tags for profiles from memcached +- on exceptions, stomp logs the error and reenqueues +- add lat, lon, location and remove closing tag from geocode.php +- Use passed-in lat long in geocode.php +- better handling of null responses from geonames.org +- Globalized form notice data geo values +- Using jQuery chaining in FormNoticeXHR +- Using form object instead of form_id and find(). Slightly faster and easier to read. +- removed describeTable from base class, and fixed it up in pgsql +- getTableDef() mostly working in postgres +- move the schema DDL sql off into seperate files for each db we support +- plugin to limit number of registered users +- add hooks for user registration +- live fast, die young in bash scripts +- for single-user mode, retrieve either site owner or defined nickname +- method to get the site owner +- define a constant for the 'owner' role of a site +- add simple cache getter/setter static functions to Memcached_DataObject +- Adds notice author's name to @title in Realtime response +- Hides .author from XHR response in showstream +- Hides .author from XHR response in showstream +- Fix more fatal errors in queue edge cases +- Don't attempt to resend XMPP messages that can't be broadcast due to the profile being deleted. +- Wrap each bit of distrib queue handler's saving operation in a try/catch; log exceptions but let everything else continue. +- Log exceptions from queuedaemon.php if they're not already caught +- Move sessions settings to its own panel +- Fixes for status_network db object .ini and tag setter script +- Add a script to set tags for sites +- Adjust API authentication to also check for OAuth protocol params in the HTTP Authorization header, as defined in OAuth HTTP Authorization Scheme. +- Last-chance distribution if enqueueing fails +- Manual failover for stomp queues. +- lost config in index.php made all traffic go to master +- "Revert "move RW setup above user get in index.php so remember_me works"" +- Revert "move RW setup above user get in index.php so remember_me works" +- move RW setup above user get in index.php so remember_me works +- hide most DB_DataObject errors +- always set up database_rw, regardless, so cached sessions work +- update mysqltimestamps on insert and update +- additional debugging data for Sessions +- 'Sign in with Twitter' button img +- Update to biz theme +- Remove redundant session token field from form (was already being added by base class). +- 'Sign in with Twitter' button img +- Can now set $config['queue']['stomp_persistent'] = false; to explicitly disable persistence when we queue items +- Showing processing indicator for form_repeat on submit instead of form +- Removed avatar from repeat of username (matches noticelist) +- Removed unused variable assignment for avatar URL and added missing fn +- Don't preemptively close existing DB connections for web views (needed to keep # of conns from going insane on multi-site queue daemons, so just doing for CLI) May, or may not, help with mystery session problems +- dropping the setcookie() call from common_ensure_session() since we're pretty sure it's unnecessary +- append '/' on cookie path for now (may still need some refactoring) +- set session cookie correctly +- Fix for Mapstraction plugin's zoomed map links +- debug log line for control channel sub +- Move faceboookapp.js to the Facebook plugin +- fix for fix for bad realtime JS load +- default 24-hour expiry on Memcached objects where not specified. Prerequisites ============= diff --git a/lib/common.php b/lib/common.php index b482464aa..b95cd1175 100644 --- a/lib/common.php +++ b/lib/common.php @@ -22,7 +22,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } //exit with 200 response, if this is checking fancy from the installer if (isset($_REQUEST['p']) && $_REQUEST['p'] == 'check-fancy') { exit; } -define('STATUSNET_VERSION', '0.9.0beta4'); +define('STATUSNET_VERSION', '0.9.0beta5'); define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility define('STATUSNET_CODENAME', 'Stand'); -- cgit v1.2.3-54-g00ecf From 96ef4435b61570dbbf15d921a42543bfb13786c0 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Feb 2010 15:32:20 -0800 Subject: Allow scripts/decache.php to blow out cache for objects that don't exist (anymore). May miss keys other than the given or primary key, but should work for a lot of common cases where a bad entry's been removed from DB but lingers in cache. --- scripts/decache.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/decache.php b/scripts/decache.php index 7cabd78ad..094bdb5aa 100644 --- a/scripts/decache.php +++ b/scripts/decache.php @@ -24,6 +24,8 @@ $helptext = << [] Clears the cache for the object in table with id If is specified, use that instead of 'id' + + ENDOFHELP; require_once INSTALLDIR.'/scripts/commandline.inc'; @@ -43,8 +45,10 @@ if (count($args) > 2) { $object = Memcached_DataObject::staticGet($table, $column, $id); if (!$object) { - print "No such '$table' with $column = '$id'.\n"; - exit(1); + print "No such '$table' with $column = '$id'; it's possible some cache keys won't be cleared properly.\n"; + $class = ucfirst($table); + $object = new $class(); + $object->column = $id; } $result = $object->decache(); -- cgit v1.2.3-54-g00ecf