From 0f3658d3da788b071da408839195526a10da5e2e Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 11:29:05 +0000 Subject: Updated path to farbtastic stylesheet --- actions/designadminpanel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actions') diff --git a/actions/designadminpanel.php b/actions/designadminpanel.php index f862aff0e..72ad6ade2 100644 --- a/actions/designadminpanel.php +++ b/actions/designadminpanel.php @@ -289,7 +289,7 @@ class DesignadminpanelAction extends AdminPanelAction function showStylesheets() { parent::showStylesheets(); - $this->cssLink('css/farbtastic.css','base','screen, projection, tv'); + $this->cssLink('js/farbtastic/farbtastic.css',null,'screen, projection, tv'); } /** -- cgit v1.2.3-54-g00ecf From c3ee1af7bee3b7268fc4cfd40ef45d9ca512fe97 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 18 Jan 2010 17:17:02 +0000 Subject: Missing null className for incoming email form legend --- actions/emailsettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actions') diff --git a/actions/emailsettings.php b/actions/emailsettings.php index bfef2970d..08608348c 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -130,7 +130,7 @@ class EmailsettingsAction extends AccountSettingsAction if (common_config('emailpost', 'enabled') && $user->email) { $this->elementStart('fieldset', array('id' => 'settings_email_incoming')); - $this->element('legend',_('Incoming email')); + $this->element('legend', null, _('Incoming email')); if ($user->incomingemail) { $this->elementStart('p'); $this->element('span', 'address', $user->incomingemail); -- cgit v1.2.3-54-g00ecf From ae46bc5fff21120ef867fb8ab59833a8f26adf47 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 12 Nov 2009 19:42:18 -0800 Subject: Started work on interface for displaying connected OAuth apps --- actions/applicationsettings.php | 135 ++++++++++++++++++++++++++++++++++++++++ actions/oauthclients.php | 108 ++++++++++++++++++++++++++++++++ classes/Profile.php | 23 +++++++ lib/applicationlist.php | 111 +++++++++++++++++++++++++++++++++ lib/connectsettingsaction.php | 4 +- lib/router.php | 4 +- 6 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 actions/applicationsettings.php create mode 100644 actions/oauthclients.php create mode 100644 lib/applicationlist.php (limited to 'actions') diff --git a/actions/applicationsettings.php b/actions/applicationsettings.php new file mode 100644 index 000000000..16c571fee --- /dev/null +++ b/actions/applicationsettings.php @@ -0,0 +1,135 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/connectsettingsaction.php'; +require_once INSTALLDIR . '/lib/applicationlist.php'; + +/** + * Show connected OAuth applications + * + * @category Settings + * @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/ + * + * @see SettingsAction + */ + +class ApplicationSettingsAction extends ConnectSettingsAction +{ + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('Connected Applications'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('You have allowed the following applications to access you account.'); + } + + /** + * Content area of the page + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + $profile = $user->getProfile(); + + $offset = ($this->page - 1) * APPS_PER_PAGE; + $limit = APPS_PER_PAGE + 1; + + $application = $profile->getApplications($offset, $limit); + + if ($application) { + $al = new ApplicationList($application, $this->user, $this); + $cnt = $al->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } + } + + $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, + $this->page, 'applicationsettings', + array('nickname' => $this->user->nickname)); + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + } + + function showEmptyListMessage() + { + $message = sprintf(_('You have not authorized any applications to use your account.')); + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + +} diff --git a/actions/oauthclients.php b/actions/oauthclients.php new file mode 100644 index 000000000..9a29e158e --- /dev/null +++ b/actions/oauthclients.php @@ -0,0 +1,108 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/connectsettingsaction.php'; + +/** + * Show a user's registered OAuth applications + * + * @category Settings + * @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/ + * + * @see SettingsAction + */ + +class OauthClientsAction extends ConnectSettingsAction +{ + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('Applications using %%site_name%%'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('Applications you have registered'); + } + + /** + * Content area of the page + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + } + +} diff --git a/classes/Profile.php b/classes/Profile.php index 25d908dbf..687215b11 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -352,6 +352,29 @@ class Profile extends Memcached_DataObject return $profile; } + function getApplications($offset = 0, $limit = null) + { + $qry = + 'SELECT oauth_application_user.* ' . + 'FROM oauth_application_user ' . + 'WHERE profile_id = %d ' . + 'ORDER BY created DESC '; + + if ($offset > 0) { + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } + } + + $application = new Oauth_application(); + + $cnt = $application->query(sprintf($qry, $this->id)); + + return $application; + } + function subscriptionCount() { $c = common_memcache(); diff --git a/lib/applicationlist.php b/lib/applicationlist.php new file mode 100644 index 000000000..fed784bb6 --- /dev/null +++ b/lib/applicationlist.php @@ -0,0 +1,111 @@ +. + * + * @category Public + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/widget.php'; + +define('APPS_PER_PAGE', 20); + +/** + * Widget to show a list of OAuth applications + * + * @category Public + * @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 ApplicationList extends Widget +{ + /** Current application, application query */ + var $application = null; + + /** Owner of this list */ + var $owner = null; + + /** Action object using us. */ + var $action = null; + + function __construct($application, $owner=null, $action=null) + { + parent::__construct($action); + + $this->application = $application; + $this->owner = $owner; + $this->action = $action; + } + + function show() + { + $this->out->elementStart('ul', 'applications xoxo'); + + $cnt = 0; + + while ($this->application->fetch()) { + $cnt++; + if($cnt > APPS_PER_PAGE) { + break; + } + $this->showapplication(); + } + + $this->out->elementEnd('ul'); + + return $cnt; + } + + function showApplication() + { + $this->out->elementStart('li', array('class' => 'application', + 'id' => 'oauthclient-' . $this->application->id)); + + $user = common_current_user(); + + $this->out->raw($this->application->name); + + $this->out->elementEnd('li'); + } + + /* Override this in subclasses. */ + + function showOwnerControls() + { + return; + } + + function highlight($text) + { + return htmlspecialchars($text); + } +} diff --git a/lib/connectsettingsaction.php b/lib/connectsettingsaction.php index e5fb8727b..4b5059540 100644 --- a/lib/connectsettingsaction.php +++ b/lib/connectsettingsaction.php @@ -116,6 +116,9 @@ class ConnectSettingsNav extends Widget _('Updates by SMS')); } + $menu['applicationsettings'] = array(_('Applications'), + _('OAuth connected applications')); + foreach ($menu as $menuaction => $menudesc) { $this->action->menuItem(common_local_url($menuaction), $menudesc[0], @@ -131,4 +134,3 @@ class ConnectSettingsNav extends Widget } - diff --git a/lib/router.php b/lib/router.php index 6b87ed27f..9b2aa025e 100644 --- a/lib/router.php +++ b/lib/router.php @@ -140,11 +140,13 @@ class Router // settings - foreach (array('profile', 'avatar', 'password', 'im', + foreach (array('profile', 'avatar', 'password', 'im', 'application', 'email', 'sms', 'userdesign', 'other') as $s) { $m->connect('settings/'.$s, array('action' => $s.'settings')); } + $m->connect('settings/oauthclients', array('action' => 'oauthclients')); + // search foreach (array('group', 'people', 'notice') as $s) { -- cgit v1.2.3-54-g00ecf From 9d958fd539ecb74356013c11a2fd2c4c8b6a0ed1 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 13 Nov 2009 19:02:18 -0800 Subject: Reorganized the OAuth app URLs and more work on the register app workflow --- actions/applicationsettings.php | 135 ---------------------- actions/apps.php | 108 ++++++++++++++++++ actions/newapplication.php | 202 ++++++++++++++++++++++++++++++++ actions/oauthclients.php | 108 ------------------ actions/oauthconnectionssettings.php | 135 ++++++++++++++++++++++ lib/applicationeditform.php | 215 +++++++++++++++++++++++++++++++++++ lib/connectsettingsaction.php | 8 +- lib/router.php | 15 ++- 8 files changed, 675 insertions(+), 251 deletions(-) delete mode 100644 actions/applicationsettings.php create mode 100644 actions/apps.php create mode 100644 actions/newapplication.php delete mode 100644 actions/oauthclients.php create mode 100644 actions/oauthconnectionssettings.php create mode 100644 lib/applicationeditform.php (limited to 'actions') diff --git a/actions/applicationsettings.php b/actions/applicationsettings.php deleted file mode 100644 index 16c571fee..000000000 --- a/actions/applicationsettings.php +++ /dev/null @@ -1,135 +0,0 @@ -. - * - * @category Settings - * @package StatusNet - * @author Zach Copley - * @copyright 2008-2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/connectsettingsaction.php'; -require_once INSTALLDIR . '/lib/applicationlist.php'; - -/** - * Show connected OAuth applications - * - * @category Settings - * @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/ - * - * @see SettingsAction - */ - -class ApplicationSettingsAction extends ConnectSettingsAction -{ - /** - * Title of the page - * - * @return string Title of the page - */ - - function title() - { - return _('Connected Applications'); - } - - /** - * Instructions for use - * - * @return instructions for use - */ - - function getInstructions() - { - return _('You have allowed the following applications to access you account.'); - } - - /** - * Content area of the page - * - * @return void - */ - - function showContent() - { - $user = common_current_user(); - $profile = $user->getProfile(); - - $offset = ($this->page - 1) * APPS_PER_PAGE; - $limit = APPS_PER_PAGE + 1; - - $application = $profile->getApplications($offset, $limit); - - if ($application) { - $al = new ApplicationList($application, $this->user, $this); - $cnt = $al->show(); - if (0 == $cnt) { - $this->showEmptyListMessage(); - } - } - - $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, - $this->page, 'applicationsettings', - array('nickname' => $this->user->nickname)); - } - - /** - * Handle posts to this form - * - * Based on the button that was pressed, muxes out to other functions - * to do the actual task requested. - * - * All sub-functions reload the form with a message -- success or failure. - * - * @return void - */ - - function handlePost() - { - // CSRF protection - - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->showForm(_('There was a problem with your session token. '. - 'Try again, please.')); - return; - } - - } - - function showEmptyListMessage() - { - $message = sprintf(_('You have not authorized any applications to use your account.')); - - $this->elementStart('div', 'guide'); - $this->raw(common_markup_to_html($message)); - $this->elementEnd('div'); - } - -} diff --git a/actions/apps.php b/actions/apps.php new file mode 100644 index 000000000..d4cea1e3e --- /dev/null +++ b/actions/apps.php @@ -0,0 +1,108 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/connectsettingsaction.php'; + +/** + * Show a user's registered OAuth applications + * + * @category Settings + * @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/ + * + * @see SettingsAction + */ + +class AppsAction extends ConnectSettingsAction +{ + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('OAuth applications'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('Applications you have registered'); + } + + /** + * Content area of the page + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + } + +} diff --git a/actions/newapplication.php b/actions/newapplication.php new file mode 100644 index 000000000..a78a856b1 --- /dev/null +++ b/actions/newapplication.php @@ -0,0 +1,202 @@ +. + * + * @category Applications + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Add a new application + * + * This is the form for adding a new application + * + * @category Application + * @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 NewApplicationAction extends Action +{ + var $msg; + + function title() + { + return _('New Application'); + } + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to create a group.')); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On GET, show the form. On POST, try to save the group. + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->trySave(); + } else { + $this->showForm(); + } + } + + function showForm($msg=null) + { + $this->msg = $msg; + $this->showPage(); + } + + function showContent() + { + $form = new ApplicationEditForm($this); + $form->show(); + } + + function showPageNotice() + { + if ($this->msg) { + $this->element('p', 'error', $this->msg); + } else { + $this->element('p', 'instructions', + _('Use this form to register a new application.')); + } + } + + function trySave() + { + $name = $this->trimmed('name'); + $description = $this->trimmed('description'); + $source_url = $this->trimmed('source_url'); + $organization = $this->trimmed('organization'); + $homepage = $this->trimmed('application'); + $callback_url = $this->trimmed('callback_url'); + $this->type = $this->trimmed('type'); + $this->access_type = $this->trimmed('access_type'); + + if (!is_null($name) && mb_strlen($name) > 255) { + $this->showForm(_('Name is too long (max 255 chars).')); + return; + } else if (User_group::descriptionTooLong($description)) { + $this->showForm(sprintf( + _('description is too long (max %d chars).'), + Oauth_application::maxDescription())); + return; + } elseif (!is_null($source_url) + && (strlen($source_url) > 0) + && !Validate::uri( + $source_url, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Source URL is not valid.')); + return; + } elseif (!is_null($homepage) + && (strlen($homepage) > 0) + && !Validate::uri( + $homepage, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Homepage is not a valid URL.')); + return; + } elseif (!is_null($callback_url) + && (strlen($callback_url) > 0) + && !Validate::uri( + $source_url, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Callback URL is not valid.')); + return; + } + + $cur = common_current_user(); + + // Checked in prepare() above + + assert(!is_null($cur)); + + $app = new Oauth_application(); + + $app->query('BEGIN'); + + $app->name = $name; + $app->owner = $cur->id; + $app->description = $description; + $app->source_url = $souce_url; + $app->organization = $organization; + $app->homepage = $homepage; + $app->callback_url = $callback_url; + $app->type = $type; + $app->access_type = $access_type; + + // generate consumer key and secret + + $app->created = common_sql_now(); + + $result = $app->insert(); + + if (!$result) { + common_log_db_error($group, 'INSERT', __FILE__); + $this->serverError(_('Could not create application.')); + } + + $group->query('COMMIT'); + + common_redirect($group->homeUrl(), 303); + + } + +} + diff --git a/actions/oauthclients.php b/actions/oauthclients.php deleted file mode 100644 index 9a29e158e..000000000 --- a/actions/oauthclients.php +++ /dev/null @@ -1,108 +0,0 @@ -. - * - * @category Settings - * @package StatusNet - * @author Zach Copley - * @copyright 2008-2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/connectsettingsaction.php'; - -/** - * Show a user's registered OAuth applications - * - * @category Settings - * @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/ - * - * @see SettingsAction - */ - -class OauthClientsAction extends ConnectSettingsAction -{ - /** - * Title of the page - * - * @return string Title of the page - */ - - function title() - { - return _('Applications using %%site_name%%'); - } - - /** - * Instructions for use - * - * @return instructions for use - */ - - function getInstructions() - { - return _('Applications you have registered'); - } - - /** - * Content area of the page - * - * @return void - */ - - function showContent() - { - $user = common_current_user(); - - } - - /** - * Handle posts to this form - * - * Based on the button that was pressed, muxes out to other functions - * to do the actual task requested. - * - * All sub-functions reload the form with a message -- success or failure. - * - * @return void - */ - - function handlePost() - { - // CSRF protection - - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->showForm(_('There was a problem with your session token. '. - 'Try again, please.')); - return; - } - - } - -} diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php new file mode 100644 index 000000000..6ec9f7027 --- /dev/null +++ b/actions/oauthconnectionssettings.php @@ -0,0 +1,135 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/connectsettingsaction.php'; +require_once INSTALLDIR . '/lib/applicationlist.php'; + +/** + * Show connected OAuth applications + * + * @category Settings + * @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/ + * + * @see SettingsAction + */ + +class OauthconnectionssettingsAction extends ConnectSettingsAction +{ + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('Connected Applications'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('You have allowed the following applications to access you account.'); + } + + /** + * Content area of the page + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + $profile = $user->getProfile(); + + $offset = ($this->page - 1) * APPS_PER_PAGE; + $limit = APPS_PER_PAGE + 1; + + $application = $profile->getApplications($offset, $limit); + + if ($application) { + $al = new ApplicationList($application, $this->user, $this); + $cnt = $al->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } + } + + $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, + $this->page, 'connectionssettings', + array('nickname' => $this->user->nickname)); + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + } + + function showEmptyListMessage() + { + $message = sprintf(_('You have not authorized any applications to use your account.')); + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + +} diff --git a/lib/applicationeditform.php b/lib/applicationeditform.php new file mode 100644 index 000000000..3fd45876a --- /dev/null +++ b/lib/applicationeditform.php @@ -0,0 +1,215 @@ +. + * + * @category Form + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/form.php'; + +/** + * Form for editing an application + * + * @category Form + * @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 ApplicationEditForm extends Form +{ + /** + * group for user to join + */ + + var $application = null; + + /** + * Constructor + * + * @param Action $out output channel + * @param User_group $group group to join + */ + + function __construct($out=null, $application=null) + { + parent::__construct($out); + + $this->application = $application; + } + + /** + * ID of the form + * + * @return string ID of the form + */ + + function id() + { + if ($this->application) { + return 'form_application_edit-' . $this->application->id; + } else { + return 'form_application_add'; + } + } + + /** + * class of the form + * + * @return string of the form class + */ + + function formClass() + { + return 'form_settings'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + if ($this->application) { + return common_local_url('editapplication', + array('id' => $this->application->id)); + } else { + return common_local_url('newapplication'); + } + } + + /** + * Name of the form + * + * @return void + */ + + function formLegend() + { + $this->out->element('legend', null, _('Register a new application')); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + if ($this->application) { + $id = $this->application->id; + $name = $this->application->name; + $description = $this->application->description; + $source_url = $this->application->source_url; + $organization = $this->application->organization; + $homepage = $this->application->homepage; + $callback_url = $this->application->callback_url; + $this->type = $this->application->type; + $this->access_type = $this->application->access_type; + } else { + $id = ''; + $name = ''; + $description = ''; + $source_url = ''; + $organization = ''; + $homepage = ''; + $callback_url = ''; + $this->type = ''; + $this->access_type = ''; + } + + $this->out->elementStart('ul', 'form_data'); + $this->out->elementStart('li'); + + $this->out->hidden('application_id', $id); + $this->out->input('name', _('Name'), + ($this->out->arg('name')) ? $this->out->arg('name') : $name); + + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); + $this->out->input('description', _('Description'), + ($this->out->arg('Description')) ? $this->out->arg('discription') : $description); + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); + $this->out->input('source_url', _('Source URL'), + ($this->out->arg('source_url')) ? $this->out->arg('source_url') : $source_url, + _('URL of the homepage of this application')); + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); + $this->out->input('Organization', _('Organization'), + ($this->out->arg('organization')) ? $this->out->arg('organization') : $orgranization, + _('Organization responsible for this application')); + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); + $this->out->input('homepage', _('Homepage'), + ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage, + _('URL of the homepage of the organization')); + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); + $this->out->input('callback_url', ('Callback URL'), + ($this->out->arg('callback_url')) ? $this->out->arg('callback_url') : $callback_url, + _('URL to redirect to after authentication')); + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); + $this->out->input('type', _('Application type'), + ($this->out->arg('type')) ? $this->out->arg('type') : $type, + _('Type of application, browser or desktop')); + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); + $this->out->input('access_type', _('Default access'), + ($this->out->arg('access_type')) ? $this->out->arg('access_type') : $access_type, + _('Default access for this application: read-write, or read-only')); + $this->out->elementEnd('li'); + + $this->out->elementEnd('ul'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Save')); + } +} diff --git a/lib/connectsettingsaction.php b/lib/connectsettingsaction.php index 4b5059540..b9c14799e 100644 --- a/lib/connectsettingsaction.php +++ b/lib/connectsettingsaction.php @@ -115,9 +115,11 @@ class ConnectSettingsNav extends Widget array(_('SMS'), _('Updates by SMS')); } - - $menu['applicationsettings'] = array(_('Applications'), - _('OAuth connected applications')); + + $menu['oauthconnectionssettings'] = array( + _('Connections'), + _('Authorized connected applications') + ); foreach ($menu as $menuaction => $menudesc) { $this->action->menuItem(common_local_url($menuaction), diff --git a/lib/router.php b/lib/router.php index 9b2aa025e..7b65ae215 100644 --- a/lib/router.php +++ b/lib/router.php @@ -140,13 +140,11 @@ class Router // settings - foreach (array('profile', 'avatar', 'password', 'im', 'application', + foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections', 'email', 'sms', 'userdesign', 'other') as $s) { $m->connect('settings/'.$s, array('action' => $s.'settings')); } - - $m->connect('settings/oauthclients', array('action' => 'oauthclients')); - + // search foreach (array('group', 'people', 'notice') as $s) { @@ -636,12 +634,19 @@ class Router // user stuff foreach (array('subscriptions', 'subscribers', - 'nudge', 'all', 'foaf', 'xrds', + 'nudge', 'all', 'foaf', 'xrds', 'apps', 'replies', 'inbox', 'outbox', 'microsummary') as $a) { $m->connect(':nickname/'.$a, array('action' => $a), array('nickname' => '[a-zA-Z0-9]{1,64}')); } + + $m->connect('apps/new', array('action' => 'newapplication')); + + $m->connect(':nickname/apps/edit', + array('action' => 'editapplication'), + array('nickname' => '['.NICKNAME_FMT.']{1,64}') + ); foreach (array('subscriptions', 'subscribers') as $a) { $m->connect(':nickname/'.$a.'/:tag', -- cgit v1.2.3-54-g00ecf From 3c2b05d222a55cd1e148f3f887bf55e924898f1b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 16 Nov 2009 16:58:49 -0800 Subject: Workflow for registering new OAuth apps pretty much done. --- actions/apps.php | 63 +++++++- actions/editapplication.php | 246 ++++++++++++++++++++++++++++ actions/newapplication.php | 133 ++++++++++----- actions/oauthconnectionssettings.php | 13 ++ actions/showapplication.php | 306 +++++++++++++++++++++++++++++++++++ classes/Consumer.php | 16 +- classes/Oauth_application.php | 44 +++++ db/statusnet.sql | 2 +- lib/applicationeditform.php | 135 +++++++++++++--- lib/applicationlist.php | 46 ++++-- lib/default.php | 2 + lib/router.php | 25 ++- 12 files changed, 949 insertions(+), 82 deletions(-) create mode 100644 actions/editapplication.php create mode 100644 actions/showapplication.php (limited to 'actions') diff --git a/actions/apps.php b/actions/apps.php index d4cea1e3e..e6500599f 100644 --- a/actions/apps.php +++ b/actions/apps.php @@ -31,7 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -require_once INSTALLDIR . '/lib/connectsettingsaction.php'; +require_once INSTALLDIR . '/lib/settingsaction.php'; +require_once INSTALLDIR . '/lib/applicationlist.php'; /** * Show a user's registered OAuth applications @@ -45,8 +46,23 @@ require_once INSTALLDIR . '/lib/connectsettingsaction.php'; * @see SettingsAction */ -class AppsAction extends ConnectSettingsAction +class AppsAction extends SettingsAction { + var $page = 0; + + function prepare($args) + { + parent::prepare($args); + $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to list your applications.')); + return false; + } + + return true; + } + /** * Title of the page * @@ -79,6 +95,49 @@ class AppsAction extends ConnectSettingsAction { $user = common_current_user(); + $offset = ($this->page - 1) * APPS_PER_PAGE; + $limit = APPS_PER_PAGE + 1; + + $application = new Oauth_application(); + $application->owner = $user->id; + $application->limit($offset, $limit); + $application->orderBy('created DESC'); + $application->find(); + + $cnt = 0; + + if ($application) { + $al = new ApplicationList($application, $user, $this); + $cnt = $al->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } + } + + $this->element('a', + array('href' => common_local_url( + 'newapplication', + array('nickname' => $user->nickname) + ) + ), + 'Register a new application ยป'); + + $this->pagination( + $this->page > 1, + $cnt > APPS_PER_PAGE, + $this->page, + 'apps', + array('nickname' => $user->nickname) + ); + } + + function showEmptyListMessage() + { + $message = sprintf(_('You have not registered any applications yet.')); + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); } /** diff --git a/actions/editapplication.php b/actions/editapplication.php new file mode 100644 index 000000000..3af482844 --- /dev/null +++ b/actions/editapplication.php @@ -0,0 +1,246 @@ +. + * + * @category Applications + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Edit the details of an OAuth application + * + * This is the form for editing an application + * + * @category Application + * @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 EditApplicationAction extends OwnerDesignAction +{ + var $msg = null; + + var $app = null; + + function title() + { + return _('Edit Application'); + } + + /** + * Prepare to run + */ + + function prepare($args) + { + parent::prepare($args); + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to edit an application.')); + return false; + } + + $id = (int)$this->arg('id'); + $this->app = Oauth_application::staticGet($id); + + if (!$this->app) { + $this->clientError(_('No such application.')); + return false; + } + + return true; + } + + /** + * Handle the request + * + * On GET, show the form. On POST, try to save the group. + * + * @param array $args unused + * + * @return void + */ + + function handle($args) + { + parent::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; + } + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('showapplication', + array( + 'nickname' => $cur->nickname, + 'id' => $this->app->id) + ), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } + } else { + $this->showForm(); + } + } + + function showForm($msg=null) + { + $this->msg = $msg; + $this->showPage(); + } + + function showContent() + { + $form = new ApplicationEditForm($this, $this->app); + $form->show(); + } + + function showPageNotice() + { + if (!empty($this->msg)) { + $this->element('p', 'error', $this->msg); + } else { + $this->element('p', 'instructions', + _('Use this form to edit your application.')); + } + } + + function trySave() + { + $name = $this->trimmed('name'); + $description = $this->trimmed('description'); + $source_url = $this->trimmed('source_url'); + $organization = $this->trimmed('organization'); + $homepage = $this->trimmed('homepage'); + $callback_url = $this->trimmed('callback_url'); + $type = $this->arg('app_type'); + $access_type = $this->arg('access_type'); + + if (empty($name)) { + $this->showForm(_('Name is required.')); + return; + } elseif (mb_strlen($name) > 255) { + $this->showForm(_('Name is too long (max 255 chars).')); + return; + } elseif (empty($description)) { + $this->showForm(_('Description is required.')); + return; + } elseif (Oauth_application::descriptionTooLong($description)) { + $this->showForm(sprintf( + _('Description is too long (max %d chars).'), + Oauth_application::maxDescription())); + return; + } elseif (empty($source_url)) { + $this->showForm(_('Source URL is required.')); + return; + } elseif ((strlen($source_url) > 0) + && !Validate::uri( + $source_url, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Source URL is not valid.')); + return; + } elseif (empty($organization)) { + $this->showForm(_('Organization is required.')); + return; + } elseif (mb_strlen($organization) > 255) { + $this->showForm(_('Organization is too long (max 255 chars).')); + return; + } elseif (empty($homepage)) { + $this->showForm(_('Organization homepage is required.')); + return; + } elseif ((strlen($homepage) > 0) + && !Validate::uri( + $homepage, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Homepage is not a valid URL.')); + return; + } elseif (empty($callback_url)) { + $this->showForm(_('Callback is required.')); + return; + } elseif (strlen($callback_url) > 0 + && !Validate::uri( + $source_url, + array('allowed_schemes' => array('http', 'https')) + ) + ) + { + $this->showForm(_('Callback URL is not valid.')); + return; + } + + $cur = common_current_user(); + + // Checked in prepare() above + + assert(!is_null($cur)); + + $orig = clone($this->app); + + $this->app->name = $name; + $this->app->description = $description; + $this->app->source_url = $source_url; + $this->app->organization = $organization; + $this->app->homepage = $homepage; + $this->app->callback_url = $callback_url; + $this->app->type = $type; + + if ($access_type == 'r') { + $this->app->setAccessFlags(true, false); + } else { + $this->app->setAccessFlags(true, true); + } + + $result = $this->app->update($orig); + + if (!$result) { + common_log_db_error($app, 'UPDATE', __FILE__); + $this->serverError(_('Could not update application.')); + } + + common_redirect(common_local_url('apps', + array('nickname' => $cur->nickname)), 303); + } + +} + diff --git a/actions/newapplication.php b/actions/newapplication.php index a78a856b1..9d8635270 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -43,7 +43,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @link http://status.net/ */ -class NewApplicationAction extends Action +class NewApplicationAction extends OwnerDesignAction { var $msg; @@ -61,7 +61,7 @@ class NewApplicationAction extends Action parent::prepare($args); if (!common_logged_in()) { - $this->clientError(_('You must be logged in to create a group.')); + $this->clientError(_('You must be logged in to register an application.')); return false; } @@ -81,8 +81,19 @@ class NewApplicationAction extends Action function handle($args) { parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->trySave(); + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('apps', + array('nickname' => $cur->nickname)), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } } else { $this->showForm(); } @@ -112,55 +123,73 @@ class NewApplicationAction extends Action function trySave() { - $name = $this->trimmed('name'); - $description = $this->trimmed('description'); - $source_url = $this->trimmed('source_url'); - $organization = $this->trimmed('organization'); - $homepage = $this->trimmed('application'); - $callback_url = $this->trimmed('callback_url'); - $this->type = $this->trimmed('type'); - $this->access_type = $this->trimmed('access_type'); - - if (!is_null($name) && mb_strlen($name) > 255) { + $name = $this->trimmed('name'); + $description = $this->trimmed('description'); + $source_url = $this->trimmed('source_url'); + $organization = $this->trimmed('organization'); + $homepage = $this->trimmed('homepage'); + $callback_url = $this->trimmed('callback_url'); + $type = $this->arg('app_type'); + $access_type = $this->arg('access_type'); + + if (empty($name)) { + $this->showForm(_('Name is required.')); + return; + } elseif (mb_strlen($name) > 255) { $this->showForm(_('Name is too long (max 255 chars).')); return; - } else if (User_group::descriptionTooLong($description)) { + } elseif (empty($description)) { + $this->showForm(_('Description is required.')); + return; + } elseif (Oauth_application::descriptionTooLong($description)) { $this->showForm(sprintf( - _('description is too long (max %d chars).'), + _('Description is too long (max %d chars).'), Oauth_application::maxDescription())); return; - } elseif (!is_null($source_url) - && (strlen($source_url) > 0) + } elseif (empty($source_url)) { + $this->showForm(_('Source URL is required.')); + return; + } elseif ((strlen($source_url) > 0) && !Validate::uri( $source_url, array('allowed_schemes' => array('http', 'https')) ) - ) + ) { $this->showForm(_('Source URL is not valid.')); return; - } elseif (!is_null($homepage) - && (strlen($homepage) > 0) + } elseif (empty($organization)) { + $this->showForm(_('Organization is required.')); + return; + } elseif (mb_strlen($organization) > 255) { + $this->showForm(_('Organization is too long (max 255 chars).')); + return; + } elseif (empty($homepage)) { + $this->showForm(_('Organization homepage is required.')); + return; + } elseif ((strlen($homepage) > 0) && !Validate::uri( $homepage, array('allowed_schemes' => array('http', 'https')) ) - ) + ) { $this->showForm(_('Homepage is not a valid URL.')); - return; - } elseif (!is_null($callback_url) - && (strlen($callback_url) > 0) + return; + } elseif (empty($callback_url)) { + $this->showForm(_('Callback is required.')); + return; + } elseif (strlen($callback_url) > 0 && !Validate::uri( $source_url, array('allowed_schemes' => array('http', 'https')) ) - ) + ) { $this->showForm(_('Callback URL is not valid.')); return; } - + $cur = common_current_user(); // Checked in prepare() above @@ -171,31 +200,53 @@ class NewApplicationAction extends Action $app->query('BEGIN'); - $app->name = $name; - $app->owner = $cur->id; - $app->description = $description; - $app->source_url = $souce_url; + $app->name = $name; + $app->owner = $cur->id; + $app->description = $description; + $app->source_url = $source_url; $app->organization = $organization; - $app->homepage = $homepage; + $app->homepage = $homepage; $app->callback_url = $callback_url; - $app->type = $type; - $app->access_type = $access_type; - + $app->type = $type; + + // Yeah, I dunno why I chose bit flags. I guess so I could + // copy this value directly to Oauth_application_user + // access_type which I think does need bit flags -- Z + + if ($access_type == 'r') { + $app->setAccessFlags(true, false); + } else { + $app->setAccessFlags(true, true); + } + + $app->created = common_sql_now(); + // generate consumer key and secret - - $app->created = common_sql_now(); + + $consumer = Consumer::generateNew(); + + $result = $consumer->insert(); + + if (!$result) { + common_log_db_error($consumer, 'INSERT', __FILE__); + $this->serverError(_('Could not create application.')); + } + + $app->consumer_key = $consumer->consumer_key; $result = $app->insert(); if (!$result) { - common_log_db_error($group, 'INSERT', __FILE__); + common_log_db_error($app, 'INSERT', __FILE__); $this->serverError(_('Could not create application.')); + $app->query('ROLLBACK'); } - - $group->query('COMMIT'); - common_redirect($group->homeUrl(), 303); - + $app->query('COMMIT'); + + common_redirect(common_local_url('apps', + array('nickname' => $cur->nickname)), 303); + } } diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index 6ec9f7027..e4b5af158 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -132,4 +132,17 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction $this->elementEnd('div'); } + function showSections() + { + $cur = common_current_user(); + + $this->element('h2', null, 'Developers'); + $this->elementStart('p'); + $this->raw(_('Developers can edit the registration settings for their applications ')); + $this->element('a', + array('href' => common_local_url('apps', array('nickname' => $cur->nickname))), + 'here.'); + $this->elementEnd('p'); + } + } diff --git a/actions/showapplication.php b/actions/showapplication.php new file mode 100644 index 000000000..6b8eff4a6 --- /dev/null +++ b/actions/showapplication.php @@ -0,0 +1,306 @@ +. + * + * @category Application + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * Show an OAuth application + * + * @category Application + * @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 ShowApplicationAction extends OwnerDesignAction +{ + /** + * Application to show + */ + + var $application = null; + + /** + * User who owns the app + */ + + var $owner = null; + + + var $msg = null; + + var $success = null; + + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + + $id = (int)$this->arg('id'); + + $this->application = Oauth_application::staticGet($id); + $this->owner = User::staticGet($this->application->owner); + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to view an application.')); + return false; + } + + if (empty($this->application)) { + $this->clientError(_('No such application.'), 404); + return false; + } + + $cur = common_current_user(); + + if ($cur->id != $this->owner->id) { + $this->clientError(_('You are not the owner of this application.'), 401); + } + + return true; + } + + /** + * Handle the request + * + * Shows info about the app + * + * @return void + */ + + function handle($args) + { + parent::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('reset')) { + $this->resetKey(); + } + } else { + $this->showPage(); + } + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + if (!empty($this->application->name)) { + return 'Application: ' . $this->application->name; + } + } + + function showPageNotice() + { + if (!empty($this->msg)) { + $this->element('div', ($this->success) ? 'success' : 'error', $this->msg); + } + } + + function showContent() + { + + $cur = common_current_user(); + + $this->elementStart('div', 'entity_actions'); + + $this->element('a', + array('href' => + common_local_url( + 'editapplication', + array( + 'nickname' => $this->owner->nickname, + 'id' => $this->application->id + ) + ) + ), 'Edit application'); + + $this->elementStart('form', array( + 'id' => 'forma_reset_key', + 'class' => 'form_reset_key', + 'method' => 'POST', + 'action' => common_local_url('showapplication', + array('nickname' => $cur->nickname, + 'id' => $this->application->id)))); + + $this->elementStart('fieldset'); + $this->hidden('token', common_session_token()); + $this->submit('reset', _('Reset Consumer key/secret')); + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + + $this->elementEnd('div'); + + $consumer = $this->application->getConsumer(); + + $this->elementStart('div', 'entity-application'); + + $this->elementStart('ul', 'entity_application_details'); + + $this->elementStart('li', 'entity_application_name'); + $this->element('span', array('class' => 'big'), $this->application->name); + $this->raw(sprintf(_(' by %1$s'), $this->application->organization)); + $this->elementEnd('li'); + + $this->element('li', 'entity_application_description', $this->application->description); + + $this->elementStart('li', 'entity_application_statistics'); + + $defaultAccess = ($this->application->access_type & Oauth_application::$writeAccess) + ? 'read-write' : 'read-only'; + $profile = Profile::staticGet($this->application->owner); + $userCnt = 0; // XXX: count how many users use the app + + $this->raw(sprintf( + _('Created by %1$s - %2$s access by default - %3$d users.'), + $profile->getBestName(), + $defaultAccess, + $userCnt + )); + + $this->elementEnd('li'); + + $this->elementEnd('ul'); + + $this->elementStart('dl', 'entity_consumer_key'); + $this->element('dt', null, _('Consumer key')); + $this->element('dd', 'label', $consumer->consumer_key); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_consumer_secret'); + $this->element('dt', null, _('Consumer secret')); + $this->element('dd', 'label', $consumer->consumer_secret); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_request_token_url'); + $this->element('dt', null, _('Request token URL')); + $this->element('dd', 'label', common_local_url('oauthrequesttoken')); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_access_token_url'); + $this->element('dt', null, _('Access token URL')); + $this->element('dd', 'label', common_local_url('oauthaccesstoken')); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_authorize_url'); + $this->element('dt', null, _('Authorize URL')); + $this->element('dd', 'label', common_local_url('oauthauthorize')); + $this->elementEnd('dl'); + + $this->element('p', 'oauth-signature-note', + '*We support hmac-sha1 signatures. We do not support the plaintext signature method.'); + + $this->elementEnd('div'); + + $this->elementStart('div', 'entity-list-apps'); + $this->element('a', + array( + 'href' => common_local_url( + 'apps', + array('nickname' => $this->owner->nickname) + ) + ), + 'View your applications'); + $this->elementEnd('div'); + } + + function resetKey() + { + $this->application->query('BEGIN'); + + $consumer = $this->application->getConsumer(); + $result = $consumer->delete(); + + if (!$result) { + common_log_db_error($consumer, 'DELETE', __FILE__); + $this->success = false; + $this->msg = ('Unable to reset consumer key and secret.'); + $this->showPage(); + return; + } + + $consumer = Consumer::generateNew(); + + $result = $consumer->insert(); + + if (!$result) { + common_log_db_error($consumer, 'INSERT', __FILE__); + $this->application->query('ROLLBACK'); + $this->success = false; + $this->msg = ('Unable to reset consumer key and secret.'); + $this->showPage(); + return; + } + + $orig = clone($this->application); + $this->application->consumer_key = $consumer->consumer_key; + $result = $this->application->update($orig); + + if (!$result) { + common_log_db_error($application, 'UPDATE', __FILE__); + $this->application->query('ROLLBACK'); + $this->success = false; + $this->msg = ('Unable to reset consumer key and secret.'); + $this->showPage(); + return; + } + + $this->application->query('COMMIT'); + + $this->success = true; + $this->msg = ('Consumer key and secret reset.'); + $this->showPage(); + } + +} + diff --git a/classes/Consumer.php b/classes/Consumer.php index d17f183a8..ad64a8491 100644 --- a/classes/Consumer.php +++ b/classes/Consumer.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Consumer extends Memcached_DataObject +class Consumer extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -22,4 +22,18 @@ class Consumer extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + static function generateNew() + { + $cons = new Consumer(); + $rand = common_good_rand(16); + + $cons->seed = $rand; + $cons->consumer_key = md5(time() + $rand); + $cons->consumer_secret = md5(md5(time() + time() + $rand)); + $cons->created = common_sql_now(); + + return $cons; + } + } diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php index e2862bf97..ef1bbf6d9 100644 --- a/classes/Oauth_application.php +++ b/classes/Oauth_application.php @@ -31,4 +31,48 @@ class Oauth_application extends Memcached_DataObject } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + // Bit flags + public static $readAccess = 1; + public static $writeAccess = 2; + + public static $browser = 1; + public static $desktop = 2; + + function getConsumer() + { + return Consumer::staticGet('consumer_key', $this->consumer_key); + } + + static function maxDesc() + { + $desclimit = common_config('application', 'desclimit'); + // null => use global limit (distinct from 0!) + if (is_null($desclimit)) { + $desclimit = common_config('site', 'textlimit'); + } + return $desclimit; + } + + static function descriptionTooLong($desc) + { + $desclimit = self::maxDesc(); + return ($desclimit > 0 && !empty($desc) && (mb_strlen($desc) > $desclimit)); + } + + function setAccessFlags($read, $write) + { + if ($read) { + $this->access_type |= self::$readAccess; + } else { + $this->access_type &= ~self::$readAccess; + } + + if ($write) { + $this->access_type |= self::$writeAccess; + } else { + $this->access_type &= ~self::$writeAccess; + } + } + } diff --git a/db/statusnet.sql b/db/statusnet.sql index 03e6115e5..a2740b60c 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -219,7 +219,7 @@ create table oauth_application ( organization varchar(255) comment 'name of the organization running the application', homepage varchar(255) comment 'homepage for the organization', callback_url varchar(255) not null comment 'url to redirect to after authentication', - type tinyint default 0 comment 'type of app, 0 = browser, 1 = desktop', + type tinyint default 0 comment 'type of app, 1 = browser, 2 = desktop', access_type tinyint default 0 comment 'default access type, bit 1 = read, bit 2 = write', created datetime not null comment 'date this record was created', modified timestamp comment 'date this record was modified' diff --git a/lib/applicationeditform.php b/lib/applicationeditform.php index 3fd45876a..ed187ba0b 100644 --- a/lib/applicationeditform.php +++ b/lib/applicationeditform.php @@ -100,11 +100,16 @@ class ApplicationEditForm extends Form function action() { - if ($this->application) { + $cur = common_current_user(); + + if (!empty($this->application)) { return common_local_url('editapplication', - array('id' => $this->application->id)); + array('id' => $this->application->id, + 'nickname' => $cur->nickname) + ); } else { - return common_local_url('newapplication'); + return common_local_url('newapplication', + array('nickname' => $cur->nickname)); } } @@ -116,7 +121,7 @@ class ApplicationEditForm extends Form function formLegend() { - $this->out->element('legend', null, _('Register a new application')); + $this->out->element('legend', null, _('Edit application')); } /** @@ -130,7 +135,7 @@ class ApplicationEditForm extends Form if ($this->application) { $id = $this->application->id; $name = $this->application->name; - $description = $this->application->description; + $description = $this->application->description; $source_url = $this->application->source_url; $organization = $this->application->organization; $homepage = $this->application->homepage; @@ -151,34 +156,46 @@ class ApplicationEditForm extends Form $this->out->elementStart('ul', 'form_data'); $this->out->elementStart('li'); - + $this->out->hidden('application_id', $id); + $this->out->hidden('token', common_session_token()); + $this->out->input('name', _('Name'), ($this->out->arg('name')) ? $this->out->arg('name') : $name); - + $this->out->elementEnd('li'); - + $this->out->elementStart('li'); - $this->out->input('description', _('Description'), - ($this->out->arg('Description')) ? $this->out->arg('discription') : $description); + + $maxDesc = Oauth_application::maxDesc(); + if ($maxDesc > 0) { + $descInstr = sprintf(_('Describe your application in %d chars'), + $maxDesc); + } else { + $descInstr = _('Describe your application'); + } + $this->out->textarea('description', _('Description'), + ($this->out->arg('description')) ? $this->out->arg('description') : $description, + $descInstr); + $this->out->elementEnd('li'); - + $this->out->elementStart('li'); $this->out->input('source_url', _('Source URL'), ($this->out->arg('source_url')) ? $this->out->arg('source_url') : $source_url, _('URL of the homepage of this application')); - $this->out->elementEnd('li'); + $this->out->elementEnd('li'); $this->out->elementStart('li'); - $this->out->input('Organization', _('Organization'), - ($this->out->arg('organization')) ? $this->out->arg('organization') : $orgranization, + $this->out->input('organization', _('Organization'), + ($this->out->arg('organization')) ? $this->out->arg('organization') : $organization, _('Organization responsible for this application')); $this->out->elementEnd('li'); $this->out->elementStart('li'); $this->out->input('homepage', _('Homepage'), ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage, - _('URL of the homepage of the organization')); + _('URL for the homepage of the organization')); $this->out->elementEnd('li'); $this->out->elementStart('li'); @@ -188,17 +205,86 @@ class ApplicationEditForm extends Form $this->out->elementEnd('li'); $this->out->elementStart('li'); - $this->out->input('type', _('Application type'), - ($this->out->arg('type')) ? $this->out->arg('type') : $type, - _('Type of application, browser or desktop')); + + $attrs = array('name' => 'app_type', + 'type' => 'radio', + 'id' => 'app_type-browser', + 'class' => 'radio', + 'value' => Oauth_application::$browser); + + // Default to Browser + + if ($this->application->type == Oauth_application::$browser + || empty($this->applicaiton->type)) { + $attrs['checked'] = 'checked'; + } + + $this->out->element('input', $attrs); + + $this->out->element('label', array('for' => 'app_type-browser', + 'class' => 'radio'), + _('Browser')); + + $attrs = array('name' => 'app_type', + 'type' => 'radio', + 'id' => 'app_type-dekstop', + 'class' => 'radio', + 'value' => Oauth_application::$desktop); + + if ($this->application->type == Oauth_application::$desktop) { + $attrs['checked'] = 'checked'; + } + + $this->out->element('input', $attrs); + + $this->out->element('label', array('for' => 'app_type-desktop', + 'class' => 'radio'), + _('Desktop')); + $this->out->element('p', 'form_guide', _('Type of application, browser or desktop')); $this->out->elementEnd('li'); - + $this->out->elementStart('li'); - $this->out->input('access_type', _('Default access'), - ($this->out->arg('access_type')) ? $this->out->arg('access_type') : $access_type, - _('Default access for this application: read-write, or read-only')); + + $attrs = array('name' => 'default_access_type', + 'type' => 'radio', + 'id' => 'default_access_type-r', + 'class' => 'radio', + 'value' => 'r'); + + // default to read-only access + + if ($this->application->access_type & Oauth_application::$readAccess + || empty($this->application->access_type)) { + $attrs['checked'] = 'checked'; + } + + $this->out->element('input', $attrs); + + $this->out->element('label', array('for' => 'default_access_type-ro', + 'class' => 'radio'), + _('Read-only')); + + $attrs = array('name' => 'default_access_type', + 'type' => 'radio', + 'id' => 'default_access_type-rw', + 'class' => 'radio', + 'value' => 'rw'); + + if ($this->application->access_type & Oauth_application::$readAccess + && $this->application->access_type & Oauth_application::$writeAccess + ) { + $attrs['checked'] = 'checked'; + } + + $this->out->element('input', $attrs); + + $this->out->element('label', array('for' => 'default_access_type-rw', + 'class' => 'radio'), + _('Read-write')); + $this->out->element('p', 'form_guide', _('Default access for this application: read-only, or read-write')); + $this->out->elementEnd('li'); - + $this->out->elementEnd('ul'); } @@ -210,6 +296,7 @@ class ApplicationEditForm extends Form function formActions() { - $this->out->submit('submit', _('Save')); + $this->out->submit('save', _('Save')); + $this->out->submit('cancel', _('Cancel')); } } diff --git a/lib/applicationlist.php b/lib/applicationlist.php index fed784bb6..3141ea974 100644 --- a/lib/applicationlist.php +++ b/lib/applicationlist.php @@ -20,7 +20,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * - * @category Public + * @category Application * @package StatusNet * @author Zach Copley * @copyright 2008-2009 StatusNet, Inc. @@ -39,7 +39,7 @@ define('APPS_PER_PAGE', 20); /** * Widget to show a list of OAuth applications * - * @category Public + * @category Application * @package StatusNet * @author Zach Copley * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 @@ -50,10 +50,10 @@ class ApplicationList extends Widget { /** Current application, application query */ var $application = null; - + /** Owner of this list */ var $owner = null; - + /** Action object using us. */ var $action = null; @@ -87,14 +87,42 @@ class ApplicationList extends Widget function showApplication() { - $this->out->elementStart('li', array('class' => 'application', - 'id' => 'oauthclient-' . $this->application->id)); $user = common_current_user(); - $this->out->raw($this->application->name); - - $this->out->elementEnd('li'); + $this->out->elementStart('li', array('class' => 'application', + 'id' => 'oauthclient-' . $this->application->id)); + + $this->out->elementStart('a', + array('href' => common_local_url( + 'showapplication', + array( + 'nickname' => $user->nickname, + 'id' => $this->application->id + ) + ), + 'class' => 'url') + ); + + $this->out->raw($this->application->name); + $this->out->elementEnd('a'); + + $this->out->raw(' by '); + + $this->out->elementStart('a', + array( + 'href' => $this->application->homepage, + 'class' => 'url' + ) + ); + $this->out->raw($this->application->organization); + $this->out->elementEnd('a'); + + $this->out->elementStart('p', 'note'); + $this->out->raw($this->application->description); + $this->out->elementEnd('p'); + + $this->out->elementEnd('li'); } /* Override this in subclasses. */ diff --git a/lib/default.php b/lib/default.php index e3a043de1..b6ee72279 100644 --- a/lib/default.php +++ b/lib/default.php @@ -211,6 +211,8 @@ $default = 'uploads' => true, 'filecommand' => '/usr/bin/file', ), + 'application' => + array('desclimit' => null), 'group' => array('maxaliases' => 3, 'desclimit' => null), diff --git a/lib/router.php b/lib/router.php index 7b65ae215..a8dbbf6d0 100644 --- a/lib/router.php +++ b/lib/router.php @@ -641,13 +641,30 @@ class Router array('nickname' => '[a-zA-Z0-9]{1,64}')); } - $m->connect('apps/new', array('action' => 'newapplication')); - - $m->connect(':nickname/apps/edit', + $m->connect(':nickname/apps', + array('action' => 'apps'), + array('nickname' => '['.NICKNAME_FMT.']{1,64}')); + $m->connect(':nickname/apps/show/:id', + array('action' => 'showapplication'), + array('nickname' => '['.NICKNAME_FMT.']{1,64}', + 'id' => '[0-9]+') + ); + $m->connect(':nickname/apps/new', + array('action' => 'newapplication'), + array('nickname' => '['.NICKNAME_FMT.']{1,64}')); + $m->connect(':nickname/apps/edit/:id', array('action' => 'editapplication'), - array('nickname' => '['.NICKNAME_FMT.']{1,64}') + array('nickname' => '['.NICKNAME_FMT.']{1,64}', + 'id' => '[0-9]+') ); + $m->connect('oauth/request_token', + array('action' => 'oauthrequesttoken')); + $m->connect('oauth/access_token', + array('action' => 'oauthaccesstoken')); + $m->connect('oauth/authorize', + array('action' => 'oauthauthorize')); + foreach (array('subscriptions', 'subscribers') as $a) { $m->connect(':nickname/'.$a.'/:tag', array('action' => $a), -- cgit v1.2.3-54-g00ecf From 1e5b2a497e3c70e4af5f93e2326c93beed15fed1 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 16 Nov 2009 18:12:39 -0800 Subject: Added session token checking. --- actions/newapplication.php | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'actions') diff --git a/actions/newapplication.php b/actions/newapplication.php index 9d8635270..ec0f2e7af 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -84,6 +84,13 @@ class NewApplicationAction extends OwnerDesignAction 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; + } + $cur = common_current_user(); if ($this->arg('cancel')) { -- cgit v1.2.3-54-g00ecf From 48e5f2b3c5164aa9e47289e5b243e2e1189b71ef Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 7 Jan 2010 01:55:57 -0800 Subject: Add icons/icon upload to Oauth apps --- actions/editapplication.php | 79 +++++++++++++++++++++------------ actions/newapplication.php | 100 ++++++++++++++++++++++++++++++++---------- actions/showapplication.php | 9 +++- classes/Oauth_application.php | 13 ++++++ lib/applicationeditform.php | 43 ++++++++++++++++-- lib/applicationlist.php | 4 ++ 6 files changed, 192 insertions(+), 56 deletions(-) (limited to 'actions') diff --git a/actions/editapplication.php b/actions/editapplication.php index 3af482844..6b8dd501c 100644 --- a/actions/editapplication.php +++ b/actions/editapplication.php @@ -81,7 +81,7 @@ class EditApplicationAction extends OwnerDesignAction /** * Handle the request * - * On GET, show the form. On POST, try to save the group. + * On GET, show the form. On POST, try to save the app. * * @param array $args unused * @@ -91,31 +91,49 @@ class EditApplicationAction extends OwnerDesignAction function handle($args) { parent::handle($args); + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->handlePost($args); + } else { + $this->showForm(); + } + } - // CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->clientError(_('There was a problem with your session token.')); - return; - } - - $cur = common_current_user(); - - if ($this->arg('cancel')) { - common_redirect(common_local_url('showapplication', - array( - 'nickname' => $cur->nickname, - 'id' => $this->app->id) - ), 303); - } elseif ($this->arg('save')) { - $this->trySave(); - } else { - $this->clientError(_('Unexpected form submission.')); - } - } else { - $this->showForm(); + function handlePost($args) + { + // Workaround for PHP returning empty $_POST and $_FILES when POST + // length > post_max_size in php.ini + + if (empty($_FILES) + && empty($_POST) + && ($_SERVER['CONTENT_LENGTH'] > 0) + ) { + $msg = _('The server was unable to handle that much POST ' . + 'data (%s bytes) due to its current configuration.'); + $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); + return; } + + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('showapplication', + array( + 'nickname' => $cur->nickname, + 'id' => $this->app->id) + ), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } } function showForm($msg=null) @@ -149,7 +167,7 @@ class EditApplicationAction extends OwnerDesignAction $homepage = $this->trimmed('homepage'); $callback_url = $this->trimmed('callback_url'); $type = $this->arg('app_type'); - $access_type = $this->arg('access_type'); + $access_type = $this->arg('default_access_type'); if (empty($name)) { $this->showForm(_('Name is required.')); @@ -214,6 +232,7 @@ class EditApplicationAction extends OwnerDesignAction // Checked in prepare() above assert(!is_null($cur)); + assert(!is_null($this->app)); $orig = clone($this->app); @@ -225,16 +244,18 @@ class EditApplicationAction extends OwnerDesignAction $this->app->callback_url = $callback_url; $this->app->type = $type; + $result = $this->app->update($orig); + + common_debug("access_type = $access_type"); + if ($access_type == 'r') { - $this->app->setAccessFlags(true, false); + $this->app->access_type = 1; } else { - $this->app->setAccessFlags(true, true); + $this->app->access_type = 3; } - $result = $this->app->update($orig); - if (!$result) { - common_log_db_error($app, 'UPDATE', __FILE__); + common_log_db_error($this->app, 'UPDATE', __FILE__); $this->serverError(_('Could not update application.')); } diff --git a/actions/newapplication.php b/actions/newapplication.php index ec0f2e7af..a0e61d288 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -71,7 +71,7 @@ class NewApplicationAction extends OwnerDesignAction /** * Handle the request * - * On GET, show the form. On POST, try to save the group. + * On GET, show the form. On POST, try to save the app. * * @param array $args unused * @@ -83,29 +83,46 @@ class NewApplicationAction extends OwnerDesignAction parent::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; - } - - $cur = common_current_user(); - - if ($this->arg('cancel')) { - common_redirect(common_local_url('apps', - array('nickname' => $cur->nickname)), 303); - } elseif ($this->arg('save')) { - $this->trySave(); - } else { - $this->clientError(_('Unexpected form submission.')); - } + $this->handlePost($args); } else { $this->showForm(); } } + function handlePost($args) + { + // Workaround for PHP returning empty $_POST and $_FILES when POST + // length > post_max_size in php.ini + + if (empty($_FILES) + && empty($_POST) + && ($_SERVER['CONTENT_LENGTH'] > 0) + ) { + $msg = _('The server was unable to handle that much POST ' . + 'data (%s bytes) due to its current configuration.'); + $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); + return; + } + + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('apps', + array('nickname' => $cur->nickname)), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } + } + function showForm($msg=null) { $this->msg = $msg; @@ -130,14 +147,14 @@ class NewApplicationAction extends OwnerDesignAction function trySave() { - $name = $this->trimmed('name'); + $name = $this->trimmed('name'); $description = $this->trimmed('description'); $source_url = $this->trimmed('source_url'); $organization = $this->trimmed('organization'); $homepage = $this->trimmed('homepage'); $callback_url = $this->trimmed('callback_url'); $type = $this->arg('app_type'); - $access_type = $this->arg('access_type'); + $access_type = $this->arg('default_access_type'); if (empty($name)) { $this->showForm(_('Name is required.')); @@ -241,14 +258,16 @@ class NewApplicationAction extends OwnerDesignAction $app->consumer_key = $consumer->consumer_key; - $result = $app->insert(); + $this->app_id = $app->insert(); - if (!$result) { + if (!$this->app_id) { common_log_db_error($app, 'INSERT', __FILE__); $this->serverError(_('Could not create application.')); $app->query('ROLLBACK'); } + $this->uploadLogo($app); + $app->query('COMMIT'); common_redirect(common_local_url('apps', @@ -256,5 +275,40 @@ class NewApplicationAction extends OwnerDesignAction } + /** + * Handle an image upload + * + * Does all the magic for handling an image upload, and crops the + * image by default. + * + * @return void + */ + + function uploadLogo($app) + { + if ($_FILES['app_icon']['error'] == + UPLOAD_ERR_OK) { + + try { + $imagefile = ImageFile::fromUpload('app_icon'); + } catch (Exception $e) { + common_debug("damn that sucks"); + $this->showForm($e->getMessage()); + return; + } + + $filename = Avatar::filename($app->id, + image_type_to_extension($imagefile->type), + null, + 'oauth-app-icon-'.common_timestamp()); + + $filepath = Avatar::path($filename); + + move_uploaded_file($imagefile->filepath, $filepath); + + $app->setOriginal($filename); + } + } + } diff --git a/actions/showapplication.php b/actions/showapplication.php index 6b8eff4a6..6d19b9561 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -55,7 +55,6 @@ class ShowApplicationAction extends OwnerDesignAction var $owner = null; - var $msg = null; var $success = null; @@ -187,6 +186,14 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementStart('ul', 'entity_application_details'); + $this->elementStart('li', 'entity_application-icon'); + + if (!empty($this->application->icon)) { + $this->element('img', array('src' => $this->application->icon)); + } + + $this->elementEnd('li'); + $this->elementStart('li', 'entity_application_name'); $this->element('span', array('class' => 'big'), $this->application->name); $this->raw(sprintf(_(' by %1$s'), $this->application->organization)); diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php index ef1bbf6d9..d4de6d82e 100644 --- a/classes/Oauth_application.php +++ b/classes/Oauth_application.php @@ -75,4 +75,17 @@ class Oauth_application extends Memcached_DataObject } } + function setOriginal($filename) + { + $imagefile = new ImageFile($this->id, Avatar::path($filename)); + + // XXX: Do we want to have a bunch of different size icons? homepage, stream, mini? + // or just one and control size via CSS? --Zach + + $orig = clone($this); + $this->icon = Avatar::url($filename); + common_debug(common_log_objstring($this)); + return $this->update($orig); + } + } diff --git a/lib/applicationeditform.php b/lib/applicationeditform.php index ed187ba0b..4d3bb06e7 100644 --- a/lib/applicationeditform.php +++ b/lib/applicationeditform.php @@ -81,6 +81,21 @@ class ApplicationEditForm extends Form } } + /** + * HTTP method used to submit the form + * + * For image data we need to send multipart/form-data + * so we set that here too + * + * @return string the method to use for submitting + */ + + function method() + { + $this->enctype = 'multipart/form-data'; + return 'post'; + } + /** * class of the form * @@ -134,6 +149,7 @@ class ApplicationEditForm extends Form { if ($this->application) { $id = $this->application->id; + $icon = $this->application->icon; $name = $this->application->name; $description = $this->application->description; $source_url = $this->application->source_url; @@ -144,6 +160,7 @@ class ApplicationEditForm extends Form $this->access_type = $this->application->access_type; } else { $id = ''; + $icon = ''; $name = ''; $description = ''; $source_url = ''; @@ -154,11 +171,31 @@ class ApplicationEditForm extends Form $this->access_type = ''; } + $this->out->hidden('token', common_session_token()); + $this->out->elementStart('ul', 'form_data'); - $this->out->elementStart('li'); + + $this->out->elementStart('li'); + + if (!empty($icon)) { + $this->out->element('img', array('src' => $icon)); + } + + $this->out->element('label', array('for' => 'app_icon'), + _('Icon')); + $this->out->element('input', array('name' => 'app_icon', + 'type' => 'file', + 'id' => 'app_icon')); + $this->out->element('p', 'form_guide', _('Icon for this application')); + $this->out->element('input', array('name' => 'MAX_FILE_SIZE', + 'type' => 'hidden', + 'id' => 'MAX_FILE_SIZE', + 'value' => ImageFile::maxFileSizeInt())); + $this->out->elementEnd('li'); + + $this->out->elementStart('li'); $this->out->hidden('application_id', $id); - $this->out->hidden('token', common_session_token()); $this->out->input('name', _('Name'), ($this->out->arg('name')) ? $this->out->arg('name') : $name); @@ -215,7 +252,7 @@ class ApplicationEditForm extends Form // Default to Browser if ($this->application->type == Oauth_application::$browser - || empty($this->applicaiton->type)) { + || empty($this->application->type)) { $attrs['checked'] = 'checked'; } diff --git a/lib/applicationlist.php b/lib/applicationlist.php index 3141ea974..5392ddab8 100644 --- a/lib/applicationlist.php +++ b/lib/applicationlist.php @@ -93,6 +93,10 @@ class ApplicationList extends Widget $this->out->elementStart('li', array('class' => 'application', 'id' => 'oauthclient-' . $this->application->id)); + if (!empty($this->application->icon)) { + $this->out->element('img', array('src' => $this->application->icon)); + } + $this->out->elementStart('a', array('href' => common_local_url( 'showapplication', -- cgit v1.2.3-54-g00ecf From 6472331be51bc6d0e670603b2a89fb66022f6b51 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 7 Jan 2010 13:19:21 -0800 Subject: Stubs for API OAuth token exchange stuff --- actions/apioauthaccesstoken.php | 49 ++++++++++++++++++++++++++++++++++++++++ actions/apioauthauthorize.php | 49 ++++++++++++++++++++++++++++++++++++++++ actions/apioauthrequesttoken.php | 49 ++++++++++++++++++++++++++++++++++++++++ lib/router.php | 6 ++--- 4 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 actions/apioauthaccesstoken.php create mode 100644 actions/apioauthauthorize.php create mode 100644 actions/apioauthrequesttoken.php (limited to 'actions') diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php new file mode 100644 index 000000000..db82f656a --- /dev/null +++ b/actions/apioauthaccesstoken.php @@ -0,0 +1,49 @@ +. + * + * @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/api.php'; + +/** + * Exchange an authorized OAuth request token for an access token + * + * @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 ApiOauthAccessTokenAction extends ApiAction +{ + +} diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php new file mode 100644 index 000000000..8839d9571 --- /dev/null +++ b/actions/apioauthauthorize.php @@ -0,0 +1,49 @@ +. + * + * @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/api.php'; + +/** + * Authorize an OAuth request token + * + * @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 ApiOauthAuthorizeAction extends Action +{ + +} diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php new file mode 100644 index 000000000..c1ccd4b7d --- /dev/null +++ b/actions/apioauthrequesttoken.php @@ -0,0 +1,49 @@ +. + * + * @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/api.php'; + +/** + * Get an OAuth request token + * + * @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 ApiOauthRequestTokenAction extends ApiAction +{ + +} diff --git a/lib/router.php b/lib/router.php index a8dbbf6d0..0703d7597 100644 --- a/lib/router.php +++ b/lib/router.php @@ -659,11 +659,11 @@ class Router ); $m->connect('oauth/request_token', - array('action' => 'oauthrequesttoken')); + array('action' => 'apioauthrequesttoken')); $m->connect('oauth/access_token', - array('action' => 'oauthaccesstoken')); + array('action' => 'apioauthaccesstoken')); $m->connect('oauth/authorize', - array('action' => 'oauthauthorize')); + array('action' => 'apioauthauthorize')); foreach (array('subscriptions', 'subscribers') as $a) { $m->connect(':nickname/'.$a.'/:tag', -- cgit v1.2.3-54-g00ecf From fa81a580bb9eea76e7739f37010b35e4b919f410 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 7 Jan 2010 18:33:17 -0800 Subject: Action for issuing a request token --- actions/apioauthrequesttoken.php | 41 +++++++++++++++++- lib/apioauthstore.php | 90 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 lib/apioauthstore.php (limited to 'actions') diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php index c1ccd4b7d..1bbd7d295 100644 --- a/actions/apioauthrequesttoken.php +++ b/actions/apioauthrequesttoken.php @@ -32,6 +32,7 @@ if (!defined('STATUSNET')) { } require_once INSTALLDIR . '/lib/api.php'; +require_once INSTALLDIR . '/lib/apioauthstore.php'; /** * Get an OAuth request token @@ -43,7 +44,45 @@ require_once INSTALLDIR . '/lib/api.php'; * @link http://status.net/ */ -class ApiOauthRequestTokenAction extends ApiAction +class ApiOauthRequestTokenAction extends Action { + /** + * Is read only? + * + * @return boolean false + */ + function isReadOnly() + { + return false; + } + + /** + * Class handler. + * + * @param array $args array of arguments + * + * @return void + */ + function handle($args) + { + parent::handle($args); + + $datastore = new ApiStatusNetOAuthDataStore(); + $server = new OAuthServer($datastore); + $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); + $server->add_signature_method($hmac_method); + + try { + $req = OAuthRequest::from_request(); + $token = $server->fetch_request_token($req); + print $token; + } catch (OAuthException $e) { + common_log(LOG_WARN, $e->getMessage()); + common_debug(var_export($req, true)); + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/html; charset=utf-8'); + print $e->getMessage() . "\n"; + } + } } diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php new file mode 100644 index 000000000..a92a4d6e4 --- /dev/null +++ b/lib/apioauthstore.php @@ -0,0 +1,90 @@ +. + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } + +require_once INSTALLDIR . '/lib/oauthstore.php'; + +class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore +{ + + function lookup_consumer($consumer_key) + { + $con = Consumer::staticGet('consumer_key', $consumer_key); + + if (!$con) { + return null; + } + + return new OAuthConsumer($con->consumer_key, + $con->consumer_secret); + } + + function new_access_token($token, $consumer) + { + common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); + $rt = new Token(); + $rt->consumer_key = $consumer->key; + $rt->tok = $token->key; + $rt->type = 0; // request + if ($rt->find(true) && $rt->state == 1) { // authorized + common_debug('request token found.', __FILE__); + $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->created = DB_DataObject_Cast::dateTime(); + 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__); + // Update subscription + // XXX: mixing levels here + $sub = Subscription::staticGet('token', $rt->tok); + if (!$sub) { + return null; + } + common_debug('subscription for request token found', __FILE__); + $orig_sub = clone($sub); + $sub->token = $at->tok; + $sub->secret = $at->secret; + if (!$sub->update($orig_sub)) { + return null; + } else { + common_debug('subscription updated to use access token', __FILE__); + return new OAuthToken($at->tok, $at->secret); + } + } + } else { + return null; + } + } + +} + -- cgit v1.2.3-54-g00ecf From e9e448bcee69b0c39badf353faedb4c29af3f502 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sun, 10 Jan 2010 21:35:46 -0800 Subject: Workflow for request tokens and authorizing request tokens --- actions/apioauthauthorize.php | 326 ++++++++++++++++++++++++++++++++++++++- actions/apioauthrequesttoken.php | 5 +- actions/showapplication.php | 6 +- lib/router.php | 19 +-- 4 files changed, 338 insertions(+), 18 deletions(-) (limited to 'actions') diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 8839d9571..895a0c6e5 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -31,7 +31,7 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/api.php'; +require_once INSTALLDIR . '/lib/apioauthstore.php'; /** * Authorize an OAuth request token @@ -45,5 +45,329 @@ require_once INSTALLDIR . '/lib/api.php'; class ApiOauthAuthorizeAction extends Action { + var $oauth_token; + var $callback; + var $app; + var $nickname; + var $password; + var $store; + + /** + * Is this a read-only action? + * + * @return boolean false + */ + + function isReadOnly($args) + { + return false; + } + + function prepare($args) + { + parent::prepare($args); + + common_debug(var_export($_REQUEST, true)); + + $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(); + + return true; + } + + function getApp() + { + // Look up the full req token + + $req_token = $this->store->lookup_token(null, + 'request', + $this->oauth_token); + + if (empty($req_token)) { + + common_debug("Couldn't find request token!"); + + $this->clientError(_('Bad request.')); + return; + } + + // Look up the app + + $app = new Oauth_application(); + $app->consumer_key = $req_token->consumer_key; + $result = $app->find(true); + + if (!empty($result)) { + $this->app = $app; + return true; + + } else { + common_debug("couldn't find the app!"); + return false; + } + } + + /** + * 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); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + /* Use a session token for CSRF protection. */ + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + $this->handlePost(); + + } else { + + common_debug('ApiOauthAuthorize::handle()'); + + if (empty($this->oauth_token)) { + + common_debug("No request token found."); + + $this->clientError(_('Bad request.')); + return; + } + + if (!$this->getApp()) { + $this->clientError(_('Bad request.')); + return; + } + + common_debug("Requesting auth for app: $app->name."); + + $this->showForm(); + } + } + + function handlePost() + { + /* Use a session token for CSRF protection. */ + + $token = $this->trimmed('token'); + + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + if (!$this->getApp()) { + $this->clientError(_('Bad request.')); + return; + } + + // is the user already logged in? + + // check creds + + if (!common_logged_in()) { + $user = common_check_user($this->nickname, $this->password); + if (empty($user)) { + $this->showForm(_("Invalid nickname / password!")); + return; + } + } + + if ($this->arg('allow')) { + + $this->store->authorize_token($this->oauth_token); + + // if we have a callback redirect and provide the token + + if (!empty($this->callback)) { + $target_url = $this->callback . '?oauth_token=' . $this->oauth_token; + common_redirect($target_url, 303); + } + + // otherwise inform the user that the rt was authorized + + $this->elementStart('p'); + + // XXX: Do verifier code? + + $this->raw(sprintf(_("The request token %s has been authorized. " . + 'Please exchange it for an access token.'), + $this->oauth_token)); + + $this->elementEnd('p'); + + } else if ($this->arg('deny')) { + + $this->elementStart('p'); + + $this->raw(sprintf(_("The request token %s has been denied."), + $this->oauth_token)); + + $this->elementEnd('p'); + } else { + $this->clientError(_('Unexpected form submission.')); + return; + } + } + + function showForm($error=null) + { + $this->error = $error; + $this->showPage(); + } + + function showScripts() + { + parent::showScripts(); + // $this->autofocus('nickname'); + } + + /** + * Title of the page + * + * @return string title of the page + */ + + function title() + { + return _('An application would like to connect to your account'); + } + + /** + * Show page notice + * + * Display a notice for how to use the page, or the + * error if it exists. + * + * @return void + */ + + function showPageNotice() + { + if ($this->error) { + $this->element('p', 'error', $this->error); + } else { + $instr = $this->getInstructions(); + $output = common_markup_to_html($instr); + + $this->raw($output); + } + } + + /** + * Shows the authorization form. + * + * @return void + */ + + function showContent() + { + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_login', + 'class' => 'form_settings', + 'action' => common_local_url('apioauthauthorize'))); + + $this->hidden('token', common_session_token()); + $this->hidden('oauth_token', $this->oauth_token); + $this->hidden('oauth_callback', $this->callback); + + $this->elementStart('fieldset'); + + $this->elementStart('ul'); + $this->elementStart('li'); + if (!empty($this->app->icon)) { + $this->element('img', array('src' => $this->app->icon)); + } + $this->elementEnd('li'); + $this->elementStart('li'); + + $access = ($this->app->access_type & Oauth_application::$writeAccess) ? + 'access and update' : 'access'; + + $msg = _("The application %s by %s would like " . + "the ability to %s your account data."); + + $this->raw(sprintf($msg, + $this->app->name, + $this->app->organization, + $access)); + + $this->elementEnd('li'); + $this->elementEnd('ul'); + + $this->elementEnd('fieldset'); + + if (!common_logged_in()) { + + $this->elementStart('fieldset'); + $this->element('legend', null, _('Login')); + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + $this->input('nickname', _('Nickname')); + $this->elementEnd('li'); + $this->elementStart('li'); + $this->password('password', _('Password')); + $this->elementEnd('li'); + $this->elementEnd('ul'); + + $this->elementEnd('fieldset'); + + } + + $this->element('input', array('id' => 'deny_submit', + 'class' => 'submit', + 'name' => 'deny', + 'type' => 'submit', + 'value' => _('Deny'))); + + $this->element('input', array('id' => 'allow_submit', + 'class' => 'submit', + 'name' => 'allow', + 'type' => 'submit', + 'value' => _('Allow'))); + + $this->elementEnd('form'); + } + + /** + * Instructions for using the form + * + * For "remembered" logins, we make the user re-login when they + * try to change settings. Different instructions for this case. + * + * @return void + */ + + function getInstructions() + { + return _('Allow or deny access to your account information.'); + + } + + /** + * A local menu + * + * Shows different login/register actions. + * + * @return void + */ + + function showLocalNav() + { + } } diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php index 1bbd7d295..53aca6b96 100644 --- a/actions/apioauthrequesttoken.php +++ b/actions/apioauthrequesttoken.php @@ -31,7 +31,6 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/api.php'; require_once INSTALLDIR . '/lib/apioauthstore.php'; /** @@ -70,6 +69,7 @@ class ApiOauthRequestTokenAction extends Action $datastore = new ApiStatusNetOAuthDataStore(); $server = new OAuthServer($datastore); $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); + $server->add_signature_method($hmac_method); try { @@ -77,8 +77,7 @@ class ApiOauthRequestTokenAction extends Action $token = $server->fetch_request_token($req); print $token; } catch (OAuthException $e) { - common_log(LOG_WARN, $e->getMessage()); - common_debug(var_export($req, true)); + common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); header('HTTP/1.1 401 Unauthorized'); header('Content-Type: text/html; charset=utf-8'); print $e->getMessage() . "\n"; diff --git a/actions/showapplication.php b/actions/showapplication.php index 6d19b9561..5156fa6f0 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -231,17 +231,17 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementStart('dl', 'entity_request_token_url'); $this->element('dt', null, _('Request token URL')); - $this->element('dd', 'label', common_local_url('oauthrequesttoken')); + $this->element('dd', 'label', common_local_url('apioauthrequesttoken')); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_access_token_url'); $this->element('dt', null, _('Access token URL')); - $this->element('dd', 'label', common_local_url('oauthaccesstoken')); + $this->element('dd', 'label', common_local_url('apioauthaccesstoken')); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_authorize_url'); $this->element('dt', null, _('Authorize URL')); - $this->element('dd', 'label', common_local_url('oauthauthorize')); + $this->element('dd', 'label', common_local_url('apioauthauthorize')); $this->elementEnd('dl'); $this->element('p', 'oauth-signature-note', diff --git a/lib/router.php b/lib/router.php index 0703d7597..420f5a0a1 100644 --- a/lib/router.php +++ b/lib/router.php @@ -50,7 +50,8 @@ class Router var $m = null; static $inst = null; static $bare = array('requesttoken', 'accesstoken', 'userauthorization', - 'postnotice', 'updateprofile', 'finishremotesubscribe'); + 'postnotice', 'updateprofile', 'finishremotesubscribe', + 'apioauthrequesttoken', 'apioauthaccesstoken'); static function get() { @@ -144,7 +145,7 @@ class Router 'email', 'sms', 'userdesign', 'other') as $s) { $m->connect('settings/'.$s, array('action' => $s.'settings')); } - + // search foreach (array('group', 'people', 'notice') as $s) { @@ -640,11 +641,11 @@ class Router array('action' => $a), array('nickname' => '[a-zA-Z0-9]{1,64}')); } - - $m->connect(':nickname/apps', + + $m->connect(':nickname/apps', array('action' => 'apps'), array('nickname' => '['.NICKNAME_FMT.']{1,64}')); - $m->connect(':nickname/apps/show/:id', + $m->connect(':nickname/apps/show/:id', array('action' => 'showapplication'), array('nickname' => '['.NICKNAME_FMT.']{1,64}', 'id' => '[0-9]+') @@ -652,18 +653,14 @@ class Router $m->connect(':nickname/apps/new', array('action' => 'newapplication'), array('nickname' => '['.NICKNAME_FMT.']{1,64}')); - $m->connect(':nickname/apps/edit/:id', + $m->connect(':nickname/apps/edit/:id', array('action' => 'editapplication'), array('nickname' => '['.NICKNAME_FMT.']{1,64}', 'id' => '[0-9]+') ); - $m->connect('oauth/request_token', - array('action' => 'apioauthrequesttoken')); - $m->connect('oauth/access_token', - array('action' => 'apioauthaccesstoken')); $m->connect('oauth/authorize', - array('action' => 'apioauthauthorize')); + array('action' => 'apioauthauthorize')); foreach (array('subscriptions', 'subscribers') as $a) { $m->connect(':nickname/'.$a.'/:tag', -- cgit v1.2.3-54-g00ecf From c473a39a7da07fbe5b80fec4c08111a554691c3a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sun, 10 Jan 2010 23:03:30 -0800 Subject: Associate request tokens with OAuth apps and app users --- actions/apioauthauthorize.php | 64 +++++++++++++++++++++++++++++--------- classes/Oauth_application_user.php | 24 +++++++++++++- classes/statusnet.ini | 4 +++ db/statusnet.sql | 5 ++- 4 files changed, 81 insertions(+), 16 deletions(-) (limited to 'actions') diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 895a0c6e5..48d5087ef 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -125,19 +125,12 @@ class ApiOauthAuthorizeAction extends Action parent::handle($args); if ($_SERVER['REQUEST_METHOD'] == 'POST') { - /* Use a session token for CSRF protection. */ - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->showForm(_('There was a problem with your session token. '. - 'Try again, please.')); - return; - } $this->handlePost(); } else { - common_debug('ApiOauthAuthorize::handle()'); + // XXX: make better error messages if (empty($this->oauth_token)) { @@ -160,7 +153,7 @@ class ApiOauthAuthorizeAction extends Action function handlePost() { - /* Use a session token for CSRF protection. */ + // check session token for CSRF protection. $token = $this->trimmed('token'); @@ -175,25 +168,66 @@ class ApiOauthAuthorizeAction extends Action return; } - // is the user already logged in? - // check creds + $user = null; + if (!common_logged_in()) { $user = common_check_user($this->nickname, $this->password); if (empty($user)) { $this->showForm(_("Invalid nickname / password!")); return; } - } + } else { + $user = common_current_user(); + } if ($this->arg('allow')) { + // mark the req token as authorized + $this->store->authorize_token($this->oauth_token); + // Check to see if there was a previous token associated + // with this user/app and kill it. If you're doing this you + // probably don't want any old tokens anyway. + + $appUser = Oauth_application_user::getByKeys($user, $this->app); + + if (!empty($appUser)) { + $result = $appUser->delete(); + + if (!$result) { + common_log_db_error($appUser, 'DELETE', __FILE__); + throw new ServerException(_('DB error deleting OAuth app user.')); + return; + } + } + + // associated the new req token with the user and the app + + $appUser = new Oauth_application_user(); + + $appUser->profile_id = $user->id; + $appUser->application_id = $this->app->id; + $appUser->access_type = $this->app->access_type; + $appUser->token = $this->oauth_token; + $appUser->created = common_sql_now(); + + $result = $appUser->insert(); + + if (!$result) { + common_log_db_error($appUser, 'INSERT', __FILE__); + throw new ServerException(_('DB error inserting OAuth app user.')); + return; + } + // if we have a callback redirect and provide the token if (!empty($this->callback)) { + + // XXX: Need better way to build this redirect url. + $target_url = $this->callback . '?oauth_token=' . $this->oauth_token; common_redirect($target_url, 303); } @@ -202,7 +236,7 @@ class ApiOauthAuthorizeAction extends Action $this->elementStart('p'); - // XXX: Do verifier code? + // XXX: Do OAuth 1.0a verifier code? $this->raw(sprintf(_("The request token %s has been authorized. " . 'Please exchange it for an access token.'), @@ -233,7 +267,9 @@ class ApiOauthAuthorizeAction extends Action function showScripts() { parent::showScripts(); - // $this->autofocus('nickname'); + if (!common_logged_in()) { + $this->autofocus('nickname'); + } } /** diff --git a/classes/Oauth_application_user.php b/classes/Oauth_application_user.php index 9e45ece25..e4c018f21 100644 --- a/classes/Oauth_application_user.php +++ b/classes/Oauth_application_user.php @@ -13,12 +13,34 @@ class Oauth_application_user extends Memcached_DataObject public $profile_id; // int(4) primary_key not_null public $application_id; // int(4) primary_key not_null public $access_type; // tinyint(1) + public $token; // varchar(255) + public $secret; // varchar(255) + public $verifier; // varchar(255) public $created; // datetime not_null + public $modified; // timestamp not_null default_CURRENT_TIMESTAMP /* Static get */ function staticGet($k,$v=NULL) { - return Memcached_DataObject::staticGet('Oauth_application_user',$k,$v); + return Memcached_DataObject::staticGet('Oauth_application_user',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE + + static function getByKeys($user, $app) + { + if (empty($user) || empty($app)) { + return null; + } + + $oau = new Oauth_application_user(); + + $oau->profile_id = $user->id; + $oau->application_id = $app->id; + $oau->limit(1); + + $result = $oau->find(true); + + return empty($result) ? null : $oau; + } + } diff --git a/classes/statusnet.ini b/classes/statusnet.ini index 0cbe60a5a..43f6c4466 100644 --- a/classes/statusnet.ini +++ b/classes/statusnet.ini @@ -372,7 +372,11 @@ id = N profile_id = 129 application_id = 129 access_type = 17 +token = 2 +secret = 2 +verifier = 2 created = 142 +modified = 384 [oauth_application_user__keys] profile_id = K diff --git a/db/statusnet.sql b/db/statusnet.sql index a2740b60c..eb4706067 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -229,8 +229,11 @@ 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', + token varchar(255) comment 'authorization token', + secret varchar(255) comment 'token secret', + verifier varchar(255) not null comment 'verification code', created datetime not null comment 'date this record was created', - + modified timestamp comment 'date this record was modified', constraint primary key (profile_id, application_id) ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; -- cgit v1.2.3-54-g00ecf From a0b84387737b016168eb3b9a1c6dee1980724f66 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 11 Jan 2010 01:11:50 -0800 Subject: Exchanging authorized request tokens for access tokens working --- actions/apioauthaccesstoken.php | 60 ++++++++++++++++++++++++++++++++++-- classes/Oauth_application.php | 14 +++++++++ lib/apioauthstore.php | 68 ++++++++++++++++++++++++++++------------- 3 files changed, 119 insertions(+), 23 deletions(-) (limited to 'actions') diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php index db82f656a..9b99724d0 100644 --- a/actions/apioauthaccesstoken.php +++ b/actions/apioauthaccesstoken.php @@ -31,7 +31,7 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/api.php'; +require_once INSTALLDIR . '/lib/apioauthstore.php'; /** * Exchange an authorized OAuth request token for an access token @@ -43,7 +43,63 @@ require_once INSTALLDIR . '/lib/api.php'; * @link http://status.net/ */ -class ApiOauthAccessTokenAction extends ApiAction +class ApiOauthAccessTokenAction extends Action { + /** + * Is read only? + * + * @return boolean false + */ + function isReadOnly() + { + return false; + } + + /** + * Class handler. + * + * @param array $args array of arguments + * + * @return void + */ + function handle($args) + { + parent::handle($args); + + $datastore = new ApiStatusNetOAuthDataStore(); + $server = new OAuthServer($datastore); + $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); + + $server->add_signature_method($hmac_method); + + $atok = null; + + try { + $req = OAuthRequest::from_request(); + $atok = $server->fetch_access_token($req); + + } catch (OAuthException $e) { + common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); + common_debug(var_export($req, true)); + $this->outputError($e->getMessage()); + return; + } + + if (empty($atok)) { + common_debug('couldn\'t get access token.'); + $this->outputError("Badness."); + return; + } + + print $atok; + } + + function outputError($msg) + { + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/html; charset=utf-8'); + print $msg . "\n"; + } } + diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php index d4de6d82e..5df8b9459 100644 --- a/classes/Oauth_application.php +++ b/classes/Oauth_application.php @@ -88,4 +88,18 @@ class Oauth_application extends Memcached_DataObject return $this->update($orig); } + static function getByConsumerKey($key) + { + if (empty($key)) { + return null; + } + + $app = new Oauth_application(); + $app->consumer_key = $key; + $app->limit(1); + $result = $app->find(true); + + return empty($result) ? null : $app; + } + } diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index a92a4d6e4..290ce8973 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -39,19 +39,45 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore function new_access_token($token, $consumer) { common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); - $rt = new Token(); + + $rt = new Token(); $rt->consumer_key = $consumer->key; $rt->tok = $token->key; $rt->type = 0; // request - if ($rt->find(true) && $rt->state == 1) { // authorized + + $app = Oauth_application::getByConsumerKey($consumer->key); + + if (empty($app)) { + common_debug("empty app!"); + } + + if ($rt->find(true) && $rt->state == 1) { // authorized common_debug('request token found.', __FILE__); - $at = new Token(); + + // find the associated user of the app + + $appUser = new Oauth_application_user(); + $appUser->application_id = $app->id; + $appUser->token = $rt->tok; + $result = $appUser->find(true); + + if (!empty($result)) { + common_debug("Oath app user found."); + } else { + common_debug("Oauth app user not found."); + return null; + } + + // go ahead and make the access token + + $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->created = DB_DataObject_Cast::dateTime(); - if (!$at->insert()) { + + if (!$at->insert()) { $e = $at->_lastError; common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__); return null; @@ -64,23 +90,23 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore return null; } common_debug('request token "'.$rt->tok.'" updated', __FILE__); - // Update subscription - // XXX: mixing levels here - $sub = Subscription::staticGet('token', $rt->tok); - if (!$sub) { - return null; - } - common_debug('subscription for request token found', __FILE__); - $orig_sub = clone($sub); - $sub->token = $at->tok; - $sub->secret = $at->secret; - if (!$sub->update($orig_sub)) { - return null; - } else { - common_debug('subscription updated to use access token', __FILE__); - return new OAuthToken($at->tok, $at->secret); - } - } + + // update the token from req to access for the user + + $orig = clone($appUser); + $appUser->token = $at->tok; + $result = $appUser->update($orig); + + if (empty($result)) { + common_debug('couldn\'t update OAuth app user.'); + return null; + } + + // Okay, good + + return new OAuthToken($at->tok, $at->secret); + } + } else { return null; } -- cgit v1.2.3-54-g00ecf From 11bd98025c1e41921359b634461772d22a1c059f Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 11 Jan 2010 12:52:56 -0800 Subject: Issue a warning when someone tries to exchange an unauthorized or otherwise bad req token for an access token. --- actions/apioauthaccesstoken.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'actions') diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php index 9b99724d0..67359d765 100644 --- a/actions/apioauthaccesstoken.php +++ b/actions/apioauthaccesstoken.php @@ -88,11 +88,10 @@ class ApiOauthAccessTokenAction extends Action if (empty($atok)) { common_debug('couldn\'t get access token.'); - $this->outputError("Badness."); - return; + print "Token exchange failed. Has the request token been authorized?\n"; + } else { + print $atok; } - - print $atok; } function outputError($msg) -- cgit v1.2.3-54-g00ecf From c78937537ed17eabb665ec6e4344b564799cbccc Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 11 Jan 2010 14:11:43 -0800 Subject: Better detial in connected OAuth applications list --- actions/oauthconnectionssettings.php | 32 +++++++++++++---- classes/Profile.php | 9 ++--- lib/applicationlist.php | 68 ++++++++++++++++++++++++++---------- 3 files changed, 79 insertions(+), 30 deletions(-) (limited to 'actions') diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index e4b5af158..56e7b02fb 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -48,6 +48,16 @@ require_once INSTALLDIR . '/lib/applicationlist.php'; class OauthconnectionssettingsAction extends ConnectSettingsAction { + + var $page = null; + + function prepare($args) + { + parent::prepare($args); + $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; + return true; + } + /** * Title of the page * @@ -59,6 +69,11 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction return _('Connected Applications'); } + function isReadOnly($args) + { + return true; + } + /** * Instructions for use * @@ -86,13 +101,16 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction $application = $profile->getApplications($offset, $limit); - if ($application) { - $al = new ApplicationList($application, $this->user, $this); - $cnt = $al->show(); - if (0 == $cnt) { - $this->showEmptyListMessage(); - } - } + $cnt == 0; + + if (!empty($application)) { + $al = new ApplicationList($application, $user, $this, true); + $cnt = $al->show(); + } + + if ($cnt == 0) { + $this->showEmptyListMessage(); + } $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, $this->page, 'connectionssettings', diff --git a/classes/Profile.php b/classes/Profile.php index 687215b11..fef2a2171 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -355,10 +355,11 @@ class Profile extends Memcached_DataObject function getApplications($offset = 0, $limit = null) { $qry = - 'SELECT oauth_application_user.* ' . - 'FROM oauth_application_user ' . - 'WHERE profile_id = %d ' . - 'ORDER BY created DESC '; + 'SELECT a.* ' . + 'FROM oauth_application_user u, oauth_application a ' . + 'WHERE u.profile_id = %d ' . + 'AND a.id = u.application_id ' . + 'ORDER BY u.created DESC '; if ($offset > 0) { if (common_config('db','type') == 'pgsql') { diff --git a/lib/applicationlist.php b/lib/applicationlist.php index 5392ddab8..e305437f4 100644 --- a/lib/applicationlist.php +++ b/lib/applicationlist.php @@ -57,13 +57,14 @@ class ApplicationList extends Widget /** Action object using us. */ var $action = null; - function __construct($application, $owner=null, $action=null) + function __construct($application, $owner=null, $action=null, $connections = false) { parent::__construct($action); $this->application = $application; $this->owner = $owner; $this->action = $action; + $this->connections = $connections; } function show() @@ -97,36 +98,65 @@ class ApplicationList extends Widget $this->out->element('img', array('src' => $this->application->icon)); } - $this->out->elementStart('a', - array('href' => common_local_url( - 'showapplication', - array( - 'nickname' => $user->nickname, - 'id' => $this->application->id - ) - ), - 'class' => 'url') - ); + if (!$this->connections) { + + $this->out->elementStart('a', + array('href' => + common_local_url('showapplication', + array('nickname' => $user->nickname, + 'id' => $this->application->id)), + 'class' => 'url') + ); $this->out->raw($this->application->name); $this->out->elementEnd('a'); + } else { + $this->out->elementStart('a', + array('href' => $this->application->source_url, + 'class' => 'url')); - $this->out->raw(' by '); + $this->out->raw($this->application->name); + $this->out->elementEnd('a'); + } - $this->out->elementStart('a', + $this->out->raw(' by '); + + $this->out->elementStart('a', array( - 'href' => $this->application->homepage, - 'class' => 'url' + 'href' => $this->application->homepage, + 'class' => 'url' ) - ); - $this->out->raw($this->application->organization); - $this->out->elementEnd('a'); + ); + $this->out->raw($this->application->organization); + $this->out->elementEnd('a'); - $this->out->elementStart('p', 'note'); + $this->out->elementStart('p', 'note'); $this->out->raw($this->application->description); $this->out->elementEnd('p'); + $this->out->elementEnd('li'); + + if ($this->connections) { + + $appUser = Oauth_application_user::getByKeys($this->owner, $this->application); + + if (empty($appUser)) { + common_debug("empty appUser!"); + } + + $this->out->elementStart('li'); + + $access = ($this->application->access_type & Oauth_application::$writeAccess) + ? 'read-write' : 'read-only'; + + $txt = 'Approved ' . common_exact_date($appUser->modified) . + " $access for access."; + + $this->out->raw($txt); $this->out->elementEnd('li'); + + // XXX: Add revoke access button + } } /* Override this in subclasses. */ -- cgit v1.2.3-54-g00ecf From 61f71a4a597bb2eab2b56a80a71e867c1539739d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 11 Jan 2010 22:54:46 +0000 Subject: Updated markup for application registration and view links --- actions/apps.php | 7 +++++-- actions/showapplication.php | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'actions') diff --git a/actions/apps.php b/actions/apps.php index e6500599f..7c7b24570 100644 --- a/actions/apps.php +++ b/actions/apps.php @@ -114,13 +114,16 @@ class AppsAction extends SettingsAction } } + $this->elementStart('p', array('id' => 'application_register')); $this->element('a', array('href' => common_local_url( 'newapplication', array('nickname' => $user->nickname) - ) + ), + 'class' => 'more' ), - 'Register a new application ยป'); + 'Register a new application'); + $this->elementEnd('p'); $this->pagination( $this->page > 1, diff --git a/actions/showapplication.php b/actions/showapplication.php index 5156fa6f0..3e191148a 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -249,16 +249,16 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementEnd('div'); - $this->elementStart('div', 'entity-list-apps'); + $this->elementStart('p', array('id' => 'application_action')); $this->element('a', array( 'href' => common_local_url( 'apps', - array('nickname' => $this->owner->nickname) - ) + array('nickname' => $this->owner->nickname)), + 'class' => 'more' ), 'View your applications'); - $this->elementEnd('div'); + $this->elementEnd('p'); } function resetKey() -- cgit v1.2.3-54-g00ecf From c8a4d0d6c2cafe1d3d4285c4b634f1dc52e91d8b Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 11 Jan 2010 23:51:12 +0000 Subject: Updated markup for application details --- actions/showapplication.php | 70 ++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 30 deletions(-) (limited to 'actions') diff --git a/actions/showapplication.php b/actions/showapplication.php index 3e191148a..33bc51f93 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -152,7 +152,8 @@ class ShowApplicationAction extends OwnerDesignAction $cur = common_current_user(); $this->elementStart('div', 'entity_actions'); - + $this->elementStart('ul'); + $this->elementStart('li'); $this->element('a', array('href' => common_local_url( @@ -163,7 +164,9 @@ class ShowApplicationAction extends OwnerDesignAction ) ) ), 'Edit application'); + $this->elementEnd('li'); + $this->elementStart('li'); $this->elementStart('form', array( 'id' => 'forma_reset_key', 'class' => 'form_reset_key', @@ -177,32 +180,39 @@ class ShowApplicationAction extends OwnerDesignAction $this->submit('reset', _('Reset Consumer key/secret')); $this->elementEnd('fieldset'); $this->elementEnd('form'); - + $this->elementEnd('li'); + $this->elementEnd('ul'); $this->elementEnd('div'); $consumer = $this->application->getConsumer(); - $this->elementStart('div', 'entity-application'); - - $this->elementStart('ul', 'entity_application_details'); - - $this->elementStart('li', 'entity_application-icon'); - - if (!empty($this->application->icon)) { - $this->element('img', array('src' => $this->application->icon)); - } - - $this->elementEnd('li'); + $this->elementStart('div', 'entity_application'); + $this->element('h2', null, _('Application profile')); + $this->elementStart('dl', 'entity_depiction'); + $this->element('dt', null, _('Icon')); + $this->elementStart('dd'); + if (!empty($this->application->icon)) { + $this->element('img', array('src' => $this->application->icon)); + } + $this->elementEnd('dd'); + $this->elementEnd('dl'); - $this->elementStart('li', 'entity_application_name'); - $this->element('span', array('class' => 'big'), $this->application->name); + $this->elementStart('dl', 'entity_fn'); + $this->element('dt', null, _('Name')); + $this->elementStart('dd'); + $this->element('span', null, $this->application->name); $this->raw(sprintf(_(' by %1$s'), $this->application->organization)); - $this->elementEnd('li'); - - $this->element('li', 'entity_application_description', $this->application->description); + $this->elementEnd('dd'); + $this->elementEnd('dl'); - $this->elementStart('li', 'entity_application_statistics'); + $this->elementStart('dl', 'entity_note'); + $this->element('dt', null, _('Description')); + $this->element('dd', null, $this->application->description); + $this->elementEnd('dl'); + $this->elementStart('dl', 'entity_statistics'); + $this->element('dt', null, _('Statistics')); + $this->elementStart('dd'); $defaultAccess = ($this->application->access_type & Oauth_application::$writeAccess) ? 'read-write' : 'read-only'; $profile = Profile::staticGet($this->application->owner); @@ -214,39 +224,39 @@ class ShowApplicationAction extends OwnerDesignAction $defaultAccess, $userCnt )); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + $this->elementEnd('div'); - $this->elementEnd('li'); - - $this->elementEnd('ul'); - + $this->elementStart('div', array('id' => 'entity_data')); + $this->element('h2', null, _('Application info')); $this->elementStart('dl', 'entity_consumer_key'); $this->element('dt', null, _('Consumer key')); - $this->element('dd', 'label', $consumer->consumer_key); + $this->element('dd', null, $consumer->consumer_key); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_consumer_secret'); $this->element('dt', null, _('Consumer secret')); - $this->element('dd', 'label', $consumer->consumer_secret); + $this->element('dd', null, $consumer->consumer_secret); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_request_token_url'); $this->element('dt', null, _('Request token URL')); - $this->element('dd', 'label', common_local_url('apioauthrequesttoken')); + $this->element('dd', null, common_local_url('apioauthrequesttoken')); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_access_token_url'); $this->element('dt', null, _('Access token URL')); - $this->element('dd', 'label', common_local_url('apioauthaccesstoken')); + $this->element('dd', null, common_local_url('apioauthaccesstoken')); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_authorize_url'); $this->element('dt', null, _('Authorize URL')); - $this->element('dd', 'label', common_local_url('apioauthauthorize')); + $this->element('dd', null, common_local_url('apioauthauthorize')); $this->elementEnd('dl'); $this->element('p', 'oauth-signature-note', - '*We support hmac-sha1 signatures. We do not support the plaintext signature method.'); - + '* We support hmac-sha1 signatures. We do not support the plaintext signature method.'); $this->elementEnd('div'); $this->elementStart('p', array('id' => 'application_action')); -- cgit v1.2.3-54-g00ecf From c2ffd6612887675e55a9d9398517e23ee95c9117 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Tue, 12 Jan 2010 01:02:25 +0000 Subject: Updated markup for application details page. Similar to user/group profile page. --- actions/showapplication.php | 90 +++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 43 deletions(-) (limited to 'actions') diff --git a/actions/showapplication.php b/actions/showapplication.php index 33bc51f93..db28395c2 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -151,63 +151,33 @@ class ShowApplicationAction extends OwnerDesignAction $cur = common_current_user(); - $this->elementStart('div', 'entity_actions'); - $this->elementStart('ul'); - $this->elementStart('li'); - $this->element('a', - array('href' => - common_local_url( - 'editapplication', - array( - 'nickname' => $this->owner->nickname, - 'id' => $this->application->id - ) - ) - ), 'Edit application'); - $this->elementEnd('li'); - - $this->elementStart('li'); - $this->elementStart('form', array( - 'id' => 'forma_reset_key', - 'class' => 'form_reset_key', - 'method' => 'POST', - 'action' => common_local_url('showapplication', - array('nickname' => $cur->nickname, - 'id' => $this->application->id)))); - - $this->elementStart('fieldset'); - $this->hidden('token', common_session_token()); - $this->submit('reset', _('Reset Consumer key/secret')); - $this->elementEnd('fieldset'); - $this->elementEnd('form'); - $this->elementEnd('li'); - $this->elementEnd('ul'); - $this->elementEnd('div'); - $consumer = $this->application->getConsumer(); - $this->elementStart('div', 'entity_application'); + $this->elementStart('div', 'entity_profile vcard'); $this->element('h2', null, _('Application profile')); $this->elementStart('dl', 'entity_depiction'); $this->element('dt', null, _('Icon')); $this->elementStart('dd'); if (!empty($this->application->icon)) { - $this->element('img', array('src' => $this->application->icon)); + $this->element('img', array('src' => $this->application->icon, + 'class' => 'photo logo')); } $this->elementEnd('dd'); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_fn'); $this->element('dt', null, _('Name')); - $this->elementStart('dd'); - $this->element('span', null, $this->application->name); - $this->raw(sprintf(_(' by %1$s'), $this->application->organization)); - $this->elementEnd('dd'); + $this->element('dd', 'fn', $this->application->name); + $this->elementEnd('dl'); + + $this->elementStart('dl', 'entity_org'); + $this->element('dt', null, _('Organization')); + $this->element('dd', 'org', $this->application->organization); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_note'); $this->element('dt', null, _('Description')); - $this->element('dd', null, $this->application->description); + $this->element('dd', 'note', $this->application->description); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_statistics'); @@ -228,7 +198,41 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementEnd('dl'); $this->elementEnd('div'); - $this->elementStart('div', array('id' => 'entity_data')); + $this->elementStart('div', 'entity_actions'); + $this->element('h2', null, _('Application actions')); + $this->elementStart('ul'); + $this->elementStart('li', 'entity_edit'); + $this->element('a', + array('href' => + common_local_url( + 'editapplication', + array( + 'nickname' => $this->owner->nickname, + 'id' => $this->application->id + ) + ) + ), 'Edit'); + $this->elementEnd('li'); + + $this->elementStart('li', 'entity_reset_keysecret'); + $this->elementStart('form', array( + 'id' => 'forma_reset_key', + 'class' => 'form_reset_key', + 'method' => 'POST', + 'action' => common_local_url('showapplication', + array('nickname' => $cur->nickname, + '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->elementEnd('ul'); + $this->elementEnd('div'); + + $this->elementStart('div', 'entity_data'); $this->element('h2', null, _('Application info')); $this->elementStart('dl', 'entity_consumer_key'); $this->element('dt', null, _('Consumer key')); @@ -255,8 +259,8 @@ class ShowApplicationAction extends OwnerDesignAction $this->element('dd', null, common_local_url('apioauthauthorize')); $this->elementEnd('dl'); - $this->element('p', 'oauth-signature-note', - '* We support hmac-sha1 signatures. We do not support the plaintext signature method.'); + $this->element('p', 'note', + _('Note: We support hmac-sha1 signatures. We do not support the plaintext signature method.')); $this->elementEnd('div'); $this->elementStart('p', array('id' => 'application_action')); -- cgit v1.2.3-54-g00ecf From ba0c82b391ad3ec71bb7efe60b03f4f95f9ecaf5 Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Tue, 12 Jan 2010 01:13:36 +0000 Subject: Added anchors to application source and homepage --- actions/showapplication.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/showapplication.php b/actions/showapplication.php index db28395c2..f2ff8b900 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -167,12 +167,20 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementStart('dl', 'entity_fn'); $this->element('dt', null, _('Name')); - $this->element('dd', 'fn', $this->application->name); + $this->elementStart('dd'); + $this->element('a', array('href' => $this->application->source_url, + 'class' => 'url fn'), + $this->application->name); + $this->elementEnd('dd'); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_org'); $this->element('dt', null, _('Organization')); - $this->element('dd', 'org', $this->application->organization); + $this->elementStart('dd'); + $this->element('a', array('href' => $this->application->homepage, + 'class' => 'url'), + $this->application->organization); + $this->elementEnd('dd'); $this->elementEnd('dl'); $this->elementStart('dl', 'entity_note'); -- cgit v1.2.3-54-g00ecf From adfca0180847571b9474db76a0c4daa407acf22b Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 01:22:37 +0000 Subject: Can now edit/change application icon --- actions/editapplication.php | 128 ++++++++++++++++++++---------------------- actions/newapplication.php | 85 +++++++++------------------- classes/Oauth_application.php | 53 ++++++++++++++--- 3 files changed, 130 insertions(+), 136 deletions(-) (limited to 'actions') diff --git a/actions/editapplication.php b/actions/editapplication.php index 6b8dd501c..a0ed3117a 100644 --- a/actions/editapplication.php +++ b/actions/editapplication.php @@ -93,47 +93,47 @@ class EditApplicationAction extends OwnerDesignAction parent::handle($args); if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->handlePost($args); - } else { - $this->showForm(); - } + $this->handlePost($args); + } else { + $this->showForm(); + } } function handlePost($args) { - // Workaround for PHP returning empty $_POST and $_FILES when POST + // Workaround for PHP returning empty $_POST and $_FILES when POST // length > post_max_size in php.ini if (empty($_FILES) && empty($_POST) && ($_SERVER['CONTENT_LENGTH'] > 0) - ) { + ) { $msg = _('The server was unable to handle that much POST ' . - 'data (%s bytes) due to its current configuration.'); + 'data (%s bytes) due to its current configuration.'); $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); return; } - // CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->clientError(_('There was a problem with your session token.')); - return; - } - - $cur = common_current_user(); - - if ($this->arg('cancel')) { - common_redirect(common_local_url('showapplication', - array( - 'nickname' => $cur->nickname, - 'id' => $this->app->id) - ), 303); - } elseif ($this->arg('save')) { - $this->trySave(); - } else { - $this->clientError(_('Unexpected form submission.')); - } + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('showapplication', + array( + 'nickname' => $cur->nickname, + 'id' => $this->app->id) + ), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } } function showForm($msg=null) @@ -170,8 +170,8 @@ class EditApplicationAction extends OwnerDesignAction $access_type = $this->arg('default_access_type'); if (empty($name)) { - $this->showForm(_('Name is required.')); - return; + $this->showForm(_('Name is required.')); + return; } elseif (mb_strlen($name) > 255) { $this->showForm(_('Name is too long (max 255 chars).')); return; @@ -181,20 +181,17 @@ class EditApplicationAction extends OwnerDesignAction } elseif (Oauth_application::descriptionTooLong($description)) { $this->showForm(sprintf( _('Description is too long (max %d chars).'), - Oauth_application::maxDescription())); + Oauth_application::maxDescription())); return; - } elseif (empty($source_url)) { - $this->showForm(_('Source URL is required.')); - return; - } elseif ((strlen($source_url) > 0) - && !Validate::uri( - $source_url, - array('allowed_schemes' => array('http', 'https')) - ) - ) - { - $this->showForm(_('Source URL is not valid.')); + } elseif (mb_strlen($source_url) > 255) { + $this->showForm(_('Source URL is too long.')); return; + } elseif ((mb_strlen($source_url) > 0) + && !Validate::uri($source_url, + array('allowed_schemes' => array('http', 'https')))) + { + $this->showForm(_('Source URL is not valid.')); + return; } elseif (empty($organization)) { $this->showForm(_('Organization is required.')); return; @@ -204,35 +201,30 @@ class EditApplicationAction extends OwnerDesignAction } elseif (empty($homepage)) { $this->showForm(_('Organization homepage is required.')); return; - } elseif ((strlen($homepage) > 0) - && !Validate::uri( - $homepage, - array('allowed_schemes' => array('http', 'https')) - ) - ) - { - $this->showForm(_('Homepage is not a valid URL.')); - return; - } elseif (empty($callback_url)) { - $this->showForm(_('Callback is required.')); - return; - } elseif (strlen($callback_url) > 0 - && !Validate::uri( - $source_url, - array('allowed_schemes' => array('http', 'https')) - ) - ) - { - $this->showForm(_('Callback URL is not valid.')); - return; - } + } elseif ((mb_strlen($homepage) > 0) + && !Validate::uri($homepage, + array('allowed_schemes' => array('http', 'https')))) + { + $this->showForm(_('Homepage is not a valid URL.')); + return; + } elseif (mb_strlen($callback_url) > 255) { + $this->showForm(_('Callback is too long.')); + return; + } elseif (mb_strlen($callback_url) > 0 + && !Validate::uri($source_url, + array('allowed_schemes' => array('http', 'https')) + )) + { + $this->showForm(_('Callback URL is not valid.')); + return; + } $cur = common_current_user(); // Checked in prepare() above assert(!is_null($cur)); - assert(!is_null($this->app)); + assert(!is_null($this->app)); $orig = clone($this->app); @@ -244,9 +236,7 @@ class EditApplicationAction extends OwnerDesignAction $this->app->callback_url = $callback_url; $this->app->type = $type; - $result = $this->app->update($orig); - - common_debug("access_type = $access_type"); + common_debug("access_type = $access_type"); if ($access_type == 'r') { $this->app->access_type = 1; @@ -254,11 +244,15 @@ class EditApplicationAction extends OwnerDesignAction $this->app->access_type = 3; } + $result = $this->app->update($orig); + if (!$result) { common_log_db_error($this->app, 'UPDATE', __FILE__); $this->serverError(_('Could not update application.')); } + $this->app->uploadLogo(); + common_redirect(common_local_url('apps', array('nickname' => $cur->nickname)), 303); } diff --git a/actions/newapplication.php b/actions/newapplication.php index a0e61d288..3d42b657b 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -83,7 +83,7 @@ class NewApplicationAction extends OwnerDesignAction parent::handle($args); if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->handlePost($args); + $this->handlePost($args); } else { $this->showForm(); } @@ -91,36 +91,36 @@ class NewApplicationAction extends OwnerDesignAction function handlePost($args) { - // Workaround for PHP returning empty $_POST and $_FILES when POST + // Workaround for PHP returning empty $_POST and $_FILES when POST // length > post_max_size in php.ini if (empty($_FILES) && empty($_POST) && ($_SERVER['CONTENT_LENGTH'] > 0) - ) { + ) { $msg = _('The server was unable to handle that much POST ' . - 'data (%s bytes) due to its current configuration.'); + 'data (%s bytes) due to its current configuration.'); $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); return; } - // CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->clientError(_('There was a problem with your session token.')); - return; - } - - $cur = common_current_user(); - - if ($this->arg('cancel')) { - common_redirect(common_local_url('apps', - array('nickname' => $cur->nickname)), 303); - } elseif ($this->arg('save')) { - $this->trySave(); - } else { - $this->clientError(_('Unexpected form submission.')); - } + // CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->clientError(_('There was a problem with your session token.')); + return; + } + + $cur = common_current_user(); + + if ($this->arg('cancel')) { + common_redirect(common_local_url('apps', + array('nickname' => $cur->nickname)), 303); + } elseif ($this->arg('save')) { + $this->trySave(); + } else { + $this->clientError(_('Unexpected form submission.')); + } } function showForm($msg=null) @@ -147,7 +147,7 @@ class NewApplicationAction extends OwnerDesignAction function trySave() { - $name = $this->trimmed('name'); + $name = $this->trimmed('name'); $description = $this->trimmed('description'); $source_url = $this->trimmed('source_url'); $organization = $this->trimmed('organization'); @@ -200,8 +200,8 @@ class NewApplicationAction extends OwnerDesignAction { $this->showForm(_('Homepage is not a valid URL.')); return; - } elseif (empty($callback_url)) { - $this->showForm(_('Callback is required.')); + } elseif (mb_strlen($callback_url) > 255) { + $this->showForm(_('Callback is too long.')); return; } elseif (strlen($callback_url) > 0 && !Validate::uri( @@ -266,7 +266,7 @@ class NewApplicationAction extends OwnerDesignAction $app->query('ROLLBACK'); } - $this->uploadLogo($app); + $this->app->uploadLogo(); $app->query('COMMIT'); @@ -275,40 +275,5 @@ class NewApplicationAction extends OwnerDesignAction } - /** - * Handle an image upload - * - * Does all the magic for handling an image upload, and crops the - * image by default. - * - * @return void - */ - - function uploadLogo($app) - { - if ($_FILES['app_icon']['error'] == - UPLOAD_ERR_OK) { - - try { - $imagefile = ImageFile::fromUpload('app_icon'); - } catch (Exception $e) { - common_debug("damn that sucks"); - $this->showForm($e->getMessage()); - return; - } - - $filename = Avatar::filename($app->id, - image_type_to_extension($imagefile->type), - null, - 'oauth-app-icon-'.common_timestamp()); - - $filepath = Avatar::path($filename); - - move_uploaded_file($imagefile->filepath, $filepath); - - $app->setOriginal($filename); - } - } - } diff --git a/classes/Oauth_application.php b/classes/Oauth_application.php index 5df8b9459..a6b539087 100644 --- a/classes/Oauth_application.php +++ b/classes/Oauth_application.php @@ -27,7 +27,7 @@ class Oauth_application extends Memcached_DataObject /* Static get */ function staticGet($k,$v=NULL) { - return Memcached_DataObject::staticGet('Oauth_application',$k,$v); + return Memcached_DataObject::staticGet('Oauth_application',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE @@ -90,16 +90,51 @@ class Oauth_application extends Memcached_DataObject static function getByConsumerKey($key) { - if (empty($key)) { - return null; - } + if (empty($key)) { + return null; + } + + $app = new Oauth_application(); + $app->consumer_key = $key; + $app->limit(1); + $result = $app->find(true); + + return empty($result) ? null : $app; + } + + /** + * Handle an image upload + * + * Does all the magic for handling an image upload, and crops the + * image by default. + * + * @return void + */ - $app = new Oauth_application(); - $app->consumer_key = $key; - $app->limit(1); - $result = $app->find(true); + function uploadLogo() + { + if ($_FILES['app_icon']['error'] == + UPLOAD_ERR_OK) { - return empty($result) ? null : $app; + try { + $imagefile = ImageFile::fromUpload('app_icon'); + } catch (Exception $e) { + common_debug("damn that sucks"); + $this->showForm($e->getMessage()); + return; + } + + $filename = Avatar::filename($this->id, + image_type_to_extension($imagefile->type), + null, + 'oauth-app-icon-'.common_timestamp()); + + $filepath = Avatar::path($filename); + + move_uploaded_file($imagefile->filepath, $filepath); + + $this->setOriginal($filename); + } } } -- cgit v1.2.3-54-g00ecf From 8da5e98cba12c32f0b75a90d1ff0007b73f0fc8d Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 05:06:35 +0000 Subject: OAuth 1.0 working now --- actions/apioauthaccesstoken.php | 40 +++++------- actions/apioauthauthorize.php | 111 +++++++++++++++++++------------ actions/apioauthrequesttoken.php | 24 +++++-- lib/apiauth.php | 138 +++++++++++++++++---------------------- lib/apioauth.php | 122 ++++++++++++++++++++++++++++++++++ lib/apioauthstore.php | 69 +++++++++++--------- lib/router.php | 11 +++- 7 files changed, 330 insertions(+), 185 deletions(-) create mode 100644 lib/apioauth.php (limited to 'actions') diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php index 67359d765..085ef6f0b 100644 --- a/actions/apioauthaccesstoken.php +++ b/actions/apioauthaccesstoken.php @@ -31,7 +31,7 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/apioauthstore.php'; +require_once INSTALLDIR . '/lib/apioauth.php'; /** * Exchange an authorized OAuth request token for an access token @@ -43,19 +43,9 @@ require_once INSTALLDIR . '/lib/apioauthstore.php'; * @link http://status.net/ */ -class ApiOauthAccessTokenAction extends Action +class ApiOauthAccessTokenAction extends ApiOauthAction { - /** - * Is read only? - * - * @return boolean false - */ - function isReadOnly() - { - return false; - } - /** * Class handler. * @@ -73,7 +63,7 @@ class ApiOauthAccessTokenAction extends Action $server->add_signature_method($hmac_method); - $atok = null; + $atok = null; try { $req = OAuthRequest::from_request(); @@ -81,24 +71,24 @@ class ApiOauthAccessTokenAction extends Action } catch (OAuthException $e) { common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); - common_debug(var_export($req, true)); - $this->outputError($e->getMessage()); - return; + common_debug(var_export($req, true)); + $this->outputError($e->getMessage()); + return; } - if (empty($atok)) { - common_debug('couldn\'t get access token.'); - print "Token exchange failed. Has the request token been authorized?\n"; - } else { - print $atok; - } + if (empty($atok)) { + common_debug('couldn\'t get access token.'); + print "Token exchange failed. Has the request token been authorized?\n"; + } else { + print $atok; + } } function outputError($msg) { - header('HTTP/1.1 401 Unauthorized'); - header('Content-Type: text/html; charset=utf-8'); - print $msg . "\n"; + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/html; charset=utf-8'); + print $msg . "\n"; } } diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 48d5087ef..cdf9cb7df 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -31,7 +31,7 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/apioauthstore.php'; +require_once INSTALLDIR . '/lib/apioauth.php'; /** * Authorize an OAuth request token @@ -43,7 +43,7 @@ require_once INSTALLDIR . '/lib/apioauthstore.php'; * @link http://status.net/ */ -class ApiOauthAuthorizeAction extends Action +class ApiOauthAuthorizeAction extends ApiOauthAction { var $oauth_token; var $callback; @@ -67,7 +67,7 @@ class ApiOauthAuthorizeAction extends Action { parent::prepare($args); - common_debug(var_export($_REQUEST, true)); + common_debug("apioauthauthorize"); $this->nickname = $this->trimmed('nickname'); $this->password = $this->arg('password'); @@ -130,7 +130,7 @@ class ApiOauthAuthorizeAction extends Action } else { - // XXX: make better error messages + // XXX: make better error messages if (empty($this->oauth_token)) { @@ -145,7 +145,8 @@ class ApiOauthAuthorizeAction extends Action return; } - common_debug("Requesting auth for app: $app->name."); + $name = $this->app->name; + common_debug("Requesting auth for app: " . $name); $this->showForm(); } @@ -153,6 +154,8 @@ class ApiOauthAuthorizeAction extends Action function handlePost() { + common_debug("handlePost()"); + // check session token for CSRF protection. $token = $this->trimmed('token'); @@ -170,7 +173,7 @@ class ApiOauthAuthorizeAction extends Action // check creds - $user = null; + $user = null; if (!common_logged_in()) { $user = common_check_user($this->nickname, $this->password); @@ -179,64 +182,86 @@ class ApiOauthAuthorizeAction extends Action return; } } else { - $user = common_current_user(); - } + $user = common_current_user(); + } if ($this->arg('allow')) { - // mark the req token as authorized + // mark the req token as authorized $this->store->authorize_token($this->oauth_token); - // Check to see if there was a previous token associated - // with this user/app and kill it. If you're doing this you - // probably don't want any old tokens anyway. + // Check to see if there was a previous token associated + // with this user/app and kill it. If the user is doing this she + // probably doesn't want any old tokens anyway. + + $appUser = Oauth_application_user::getByKeys($user, $this->app); + + if (!empty($appUser)) { + $result = $appUser->delete(); - $appUser = Oauth_application_user::getByKeys($user, $this->app); + if (!$result) { + common_log_db_error($appUser, 'DELETE', __FILE__); + throw new ServerException(_('DB error deleting OAuth app user.')); + return; + } + } - if (!empty($appUser)) { - $result = $appUser->delete(); + // associated the authorized req token with the user and the app - if (!$result) { - common_log_db_error($appUser, 'DELETE', __FILE__); - throw new ServerException(_('DB error deleting OAuth app user.')); - return; - } - } + $appUser = new Oauth_application_user(); - // associated the new req token with the user and the app + $appUser->profile_id = $user->id; + $appUser->application_id = $this->app->id; - $appUser = new Oauth_application_user(); + // Note: do not copy the access type from the application. + // The access type should always be 0 when the OAuth app + // user record has a request token associated with it. + // Access type gets assigned once an access token has been + // granted. The OAuth app user record then gets updated + // with the new access token and access type. - $appUser->profile_id = $user->id; - $appUser->application_id = $this->app->id; - $appUser->access_type = $this->app->access_type; - $appUser->token = $this->oauth_token; - $appUser->created = common_sql_now(); + $appUser->token = $this->oauth_token; + $appUser->created = common_sql_now(); - $result = $appUser->insert(); + $result = $appUser->insert(); - if (!$result) { - common_log_db_error($appUser, 'INSERT', __FILE__); - throw new ServerException(_('DB error inserting OAuth app user.')); - return; - } + if (!$result) { + common_log_db_error($appUser, 'INSERT', __FILE__); + throw new ServerException(_('DB error inserting OAuth app user.')); + return; + } // if we have a callback redirect and provide the token + // 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; + } + if (!empty($this->callback)) { - // XXX: Need better way to build this redirect url. + // 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"); - $target_url = $this->callback . '?oauth_token=' . $this->oauth_token; common_redirect($target_url, 303); + } else { + common_debug("callback was empty!"); } // otherwise inform the user that the rt was authorized $this->elementStart('p'); - // XXX: Do OAuth 1.0a verifier code? + // XXX: Do OAuth 1.0a verifier code $this->raw(sprintf(_("The request token %s has been authorized. " . 'Please exchange it for an access token.'), @@ -267,9 +292,9 @@ class ApiOauthAuthorizeAction extends Action function showScripts() { parent::showScripts(); - if (!common_logged_in()) { - $this->autofocus('nickname'); - } + if (!common_logged_in()) { + $this->autofocus('nickname'); + } } /** @@ -313,9 +338,9 @@ class ApiOauthAuthorizeAction extends Action function showContent() { $this->elementStart('form', array('method' => 'post', - 'id' => 'form_login', - 'class' => 'form_settings', - 'action' => common_local_url('apioauthauthorize'))); + 'id' => 'form_login', + 'class' => 'form_settings', + 'action' => common_local_url('apioauthauthorize'))); $this->hidden('token', common_session_token()); $this->hidden('oauth_token', $this->oauth_token); diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php index 53aca6b96..467640b9a 100644 --- a/actions/apioauthrequesttoken.php +++ b/actions/apioauthrequesttoken.php @@ -31,7 +31,7 @@ if (!defined('STATUSNET')) { exit(1); } -require_once INSTALLDIR . '/lib/apioauthstore.php'; +require_once INSTALLDIR . '/lib/apioauth.php'; /** * Get an OAuth request token @@ -43,16 +43,28 @@ require_once INSTALLDIR . '/lib/apioauthstore.php'; * @link http://status.net/ */ -class ApiOauthRequestTokenAction extends Action +class ApiOauthRequestTokenAction extends ApiOauthAction { /** - * Is read only? + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag * - * @return boolean false */ - function isReadOnly() + + function prepare($args) { - return false; + parent::prepare($args); + + $this->callback = $this->arg('oauth_callback'); + + if (!empty($this->callback)) { + common_debug("callback: $this->callback"); + } + + return true; } /** diff --git a/lib/apiauth.php b/lib/apiauth.php index 3229ab19f..431f3ac4f 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -39,7 +39,7 @@ if (!defined('STATUSNET')) { } require_once INSTALLDIR . '/lib/api.php'; -require_once INSTALLDIR . '/lib/apioauthstore.php'; +require_once INSTALLDIR . '/lib/apioauth.php'; /** * Actions extending this class will require auth @@ -71,14 +71,14 @@ class ApiAuthAction extends ApiAction if ($this->requiresAuth()) { - $this->consumer_key = $this->arg('oauth_consumer_key'); - $this->access_token = $this->arg('oauth_token'); + $this->consumer_key = $this->arg('oauth_consumer_key'); + $this->access_token = $this->arg('oauth_token'); - if (!empty($this->access_token)) { - $this->checkOAuthRequest(); - } else { - $this->checkBasicAuthUser(); - } + if (!empty($this->access_token)) { + $this->checkOAuthRequest(); + } else { + $this->checkBasicAuthUser(); + } } return true; @@ -86,101 +86,83 @@ class ApiAuthAction extends ApiAction function checkOAuthRequest() { - common_debug("We have an OAuth request."); + common_debug("We have an OAuth request."); - $datastore = new ApiStatusNetOAuthDataStore(); - $server = new OAuthServer($datastore); - $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); + $datastore = new ApiStatusNetOAuthDataStore(); + $server = new OAuthServer($datastore); + $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); - $server->add_signature_method($hmac_method); + $server->add_signature_method($hmac_method); - $this->cleanRequest(); + ApiOauthAction::cleanRequest(); - try { + try { - $req = OAuthRequest::from_request(); - $server->verify_request($req); + $req = OAuthRequest::from_request(); + $server->verify_request($req); - common_debug("Good OAuth request!"); + common_debug("Good OAuth request!"); - $app = Oauth_application::getByConsumerKey($this->consumer_key); + $app = Oauth_application::getByConsumerKey($this->consumer_key); - if (empty($app)) { + if (empty($app)) { - // this should really not happen - common_log(LOG_WARN, - "Couldn't find the OAuth app for consumer key: $this->consumer_key"); + // this should really not happen + common_log(LOG_WARN, + "Couldn't find the OAuth app for consumer key: $this->consumer_key"); - throw new OAuthException('No application for that consumer key.'); - } + throw new OAuthException('No application for that consumer key.'); + } - $appUser = Oauth_application_user::staticGet('token', - $this->access_token); + $appUser = Oauth_application_user::staticGet('token', + $this->access_token); - // XXX: check that app->id and appUser->application_id and consumer all - // match? + // XXX: check that app->id and appUser->application_id and consumer all + // match? - if (!empty($appUser)) { + if (!empty($appUser)) { - // read or read-write - $this->oauth_access_type = $appUser->access_type; + // read or read-write + $this->oauth_access_type = $appUser->access_type; - // If access_type == 0 we have either a request token - // or a bad / revoked access token + // If access_type == 0 we have either a request token + // or a bad / revoked access token - if ($this->oauth_access_type != 0) { + if ($this->oauth_access_type != 0) { - $this->auth_user = User::staticGet('id', $appUser->profile_id); + $this->auth_user = User::staticGet('id', $appUser->profile_id); - $msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " . - "application '%s' (id: %d)."; + $msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " . + "application '%s' (id: %d)."; - common_log(LOG_INFO, sprintf($msg, - $this->auth_user->nickname, - $this->auth_user->id, - $app->name, - $app->id)); - return true; - } else { - throw new OAuthException('Bad access token.'); - } - } else { + common_log(LOG_INFO, sprintf($msg, + $this->auth_user->nickname, + $this->auth_user->id, + $app->name, + $app->id)); + return true; + } else { + throw new OAuthException('Bad access token.'); + } + } else { - // also should not happen - throw new OAuthException('No user for that token.'); - } + // also should not happen + throw new OAuthException('No user for that token.'); + } - } catch (OAuthException $e) { - common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); - common_debug(var_export($req, true)); - $this->showOAuthError($e->getMessage()); - exit(); - } + } catch (OAuthException $e) { + common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); + common_debug(var_export($req, true)); + $this->showOAuthError($e->getMessage()); + exit(); + } } function showOAuthError($msg) { - header('HTTP/1.1 401 Unauthorized'); - header('Content-Type: text/html; charset=utf-8'); - print $msg . "\n"; - } - - function cleanRequest() - { - // kill evil effects of magical slashing - - if(get_magic_quotes_gpc() == 1) { - $_POST = array_map('stripslashes', $_POST); - $_GET = array_map('stripslashes', $_GET); - } - - // strip out the p param added in index.php - - // XXX: should we strip anything else? Or alternatively - // only allow a known list of params? - - unset($_GET['p']); - unset($_POST['p']); + header('HTTP/1.1 401 Unauthorized'); + header('Content-Type: text/html; charset=utf-8'); + print $msg . "\n"; } /** diff --git a/lib/apioauth.php b/lib/apioauth.php new file mode 100644 index 000000000..4cb8a6775 --- /dev/null +++ b/lib/apioauth.php @@ -0,0 +1,122 @@ +. + * + * @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/apioauthstore.php'; + +/** + * Base action for API OAuth enpoints. Clean up the + * the request, and possibly some other common things + * here. + * + * @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 Action +{ + /** + * 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(); + } + + 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 + + // XXX: should we strip anything else? Or alternatively + // only allow a known list of params? + + unset($_GET['p']); + unset($_POST['p']); + } + + 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 290ce8973..c39ddbb0f 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -40,44 +40,44 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore { common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); - $rt = new Token(); + $rt = new Token(); $rt->consumer_key = $consumer->key; $rt->tok = $token->key; $rt->type = 0; // request $app = Oauth_application::getByConsumerKey($consumer->key); - if (empty($app)) { - common_debug("empty app!"); - } + if (empty($app)) { + common_debug("empty app!"); + } - if ($rt->find(true) && $rt->state == 1) { // authorized + if ($rt->find(true) && $rt->state == 1) { // authorized common_debug('request token found.', __FILE__); - // find the associated user of the app + // find the associated user of the app - $appUser = new Oauth_application_user(); - $appUser->application_id = $app->id; - $appUser->token = $rt->tok; - $result = $appUser->find(true); + $appUser = new Oauth_application_user(); + $appUser->application_id = $app->id; + $appUser->token = $rt->tok; + $result = $appUser->find(true); - if (!empty($result)) { - common_debug("Oath app user found."); - } else { - common_debug("Oauth app user not found."); - return null; - } + if (!empty($result)) { + common_debug("Oath app user found."); + } else { + common_debug("Oauth app user not found."); + return null; + } - // go ahead and make the access token + // go ahead and make the access token - $at = new Token(); + $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->created = DB_DataObject_Cast::dateTime(); - if (!$at->insert()) { + if (!$at->insert()) { $e = $at->_lastError; common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__); return null; @@ -91,21 +91,30 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore } common_debug('request token "'.$rt->tok.'" updated', __FILE__); - // update the token from req to access for the user + // update the token from req to access for the user + + $orig = clone($appUser); + $appUser->token = $at->tok; - $orig = clone($appUser); - $appUser->token = $at->tok; - $result = $appUser->update($orig); + // It's at this point that we change the access type + // to whatever the application's access is. Request + // tokens should always have an access type of 0, and + // therefore be unuseable for making requests for + // protected resources. - if (empty($result)) { - common_debug('couldn\'t update OAuth app user.'); - return null; - } + $appUser->access_type = $app->access_type; + + $result = $appUser->update($orig); + + if (empty($result)) { + common_debug('couldn\'t update OAuth app user.'); + return null; + } - // Okay, good + // Okay, good - return new OAuthToken($at->tok, $at->secret); - } + return new OAuthToken($at->tok, $at->secret); + } } else { return null; diff --git a/lib/router.php b/lib/router.php index 420f5a0a1..d6e448c2f 100644 --- a/lib/router.php +++ b/lib/router.php @@ -50,8 +50,7 @@ class Router var $m = null; static $inst = null; static $bare = array('requesttoken', 'accesstoken', 'userauthorization', - 'postnotice', 'updateprofile', 'finishremotesubscribe', - 'apioauthrequesttoken', 'apioauthaccesstoken'); + 'postnotice', 'updateprofile', 'finishremotesubscribe'); static function get() { @@ -659,7 +658,13 @@ class Router 'id' => '[0-9]+') ); - $m->connect('oauth/authorize', + $m->connect('api/oauth/request_token', + array('action' => 'apioauthrequesttoken')); + + $m->connect('api/oauth/access_token', + array('action' => 'apioauthaccesstoken')); + + $m->connect('api/oauth/authorize', array('action' => 'apioauthauthorize')); foreach (array('subscriptions', 'subscribers') as $a) { -- cgit v1.2.3-54-g00ecf From 693b16174ad4142d1a543f78878c84c552ce6d74 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 05:31:48 +0000 Subject: Fix icon upload on new apps --- actions/newapplication.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actions') diff --git a/actions/newapplication.php b/actions/newapplication.php index 3d42b657b..7bb81095d 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -266,7 +266,7 @@ class NewApplicationAction extends OwnerDesignAction $app->query('ROLLBACK'); } - $this->app->uploadLogo(); + $app->uploadLogo(); $app->query('COMMIT'); -- cgit v1.2.3-54-g00ecf From e101a6df6ba1cbec4664bb81fc81655e5db18b0f Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 07:33:51 +0000 Subject: Rework application registration workflow to be more private --- actions/apps.php | 170 ----------------------------------- actions/editapplication.php | 8 +- actions/newapplication.php | 8 +- actions/oauthappssettings.php | 166 ++++++++++++++++++++++++++++++++++ actions/oauthconnectionssettings.php | 2 +- actions/showapplication.php | 25 ++---- lib/applicationeditform.php | 61 ++++++------- lib/applicationlist.php | 13 +-- lib/router.php | 23 ++--- 9 files changed, 221 insertions(+), 255 deletions(-) delete mode 100644 actions/apps.php create mode 100644 actions/oauthappssettings.php (limited to 'actions') diff --git a/actions/apps.php b/actions/apps.php deleted file mode 100644 index 7c7b24570..000000000 --- a/actions/apps.php +++ /dev/null @@ -1,170 +0,0 @@ -. - * - * @category Settings - * @package StatusNet - * @author Zach Copley - * @copyright 2008-2009 StatusNet, Inc. - * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 - * @link http://status.net/ - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR . '/lib/settingsaction.php'; -require_once INSTALLDIR . '/lib/applicationlist.php'; - -/** - * Show a user's registered OAuth applications - * - * @category Settings - * @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/ - * - * @see SettingsAction - */ - -class AppsAction extends SettingsAction -{ - var $page = 0; - - function prepare($args) - { - parent::prepare($args); - $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; - - if (!common_logged_in()) { - $this->clientError(_('You must be logged in to list your applications.')); - return false; - } - - return true; - } - - /** - * Title of the page - * - * @return string Title of the page - */ - - function title() - { - return _('OAuth applications'); - } - - /** - * Instructions for use - * - * @return instructions for use - */ - - function getInstructions() - { - return _('Applications you have registered'); - } - - /** - * Content area of the page - * - * @return void - */ - - function showContent() - { - $user = common_current_user(); - - $offset = ($this->page - 1) * APPS_PER_PAGE; - $limit = APPS_PER_PAGE + 1; - - $application = new Oauth_application(); - $application->owner = $user->id; - $application->limit($offset, $limit); - $application->orderBy('created DESC'); - $application->find(); - - $cnt = 0; - - if ($application) { - $al = new ApplicationList($application, $user, $this); - $cnt = $al->show(); - if (0 == $cnt) { - $this->showEmptyListMessage(); - } - } - - $this->elementStart('p', array('id' => 'application_register')); - $this->element('a', - array('href' => common_local_url( - 'newapplication', - array('nickname' => $user->nickname) - ), - 'class' => 'more' - ), - 'Register a new application'); - $this->elementEnd('p'); - - $this->pagination( - $this->page > 1, - $cnt > APPS_PER_PAGE, - $this->page, - 'apps', - array('nickname' => $user->nickname) - ); - } - - function showEmptyListMessage() - { - $message = sprintf(_('You have not registered any applications yet.')); - - $this->elementStart('div', 'guide'); - $this->raw(common_markup_to_html($message)); - $this->elementEnd('div'); - } - - /** - * Handle posts to this form - * - * Based on the button that was pressed, muxes out to other functions - * to do the actual task requested. - * - * All sub-functions reload the form with a message -- success or failure. - * - * @return void - */ - - function handlePost() - { - // CSRF protection - - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->showForm(_('There was a problem with your session token. '. - 'Try again, please.')); - return; - } - - } - -} diff --git a/actions/editapplication.php b/actions/editapplication.php index a0ed3117a..a6db87c61 100644 --- a/actions/editapplication.php +++ b/actions/editapplication.php @@ -125,10 +125,7 @@ class EditApplicationAction extends OwnerDesignAction if ($this->arg('cancel')) { common_redirect(common_local_url('showapplication', - array( - 'nickname' => $cur->nickname, - 'id' => $this->app->id) - ), 303); + array('id' => $this->app->id)), 303); } elseif ($this->arg('save')) { $this->trySave(); } else { @@ -253,8 +250,7 @@ class EditApplicationAction extends OwnerDesignAction $this->app->uploadLogo(); - common_redirect(common_local_url('apps', - array('nickname' => $cur->nickname)), 303); + common_redirect(common_local_url('oauthappssettings'), 303); } } diff --git a/actions/newapplication.php b/actions/newapplication.php index 7bb81095d..c499fe7c7 100644 --- a/actions/newapplication.php +++ b/actions/newapplication.php @@ -114,8 +114,7 @@ class NewApplicationAction extends OwnerDesignAction $cur = common_current_user(); if ($this->arg('cancel')) { - common_redirect(common_local_url('apps', - array('nickname' => $cur->nickname)), 303); + common_redirect(common_local_url('oauthappssettings'), 303); } elseif ($this->arg('save')) { $this->trySave(); } else { @@ -147,7 +146,7 @@ class NewApplicationAction extends OwnerDesignAction function trySave() { - $name = $this->trimmed('name'); + $name = $this->trimmed('name'); $description = $this->trimmed('description'); $source_url = $this->trimmed('source_url'); $organization = $this->trimmed('organization'); @@ -270,8 +269,7 @@ class NewApplicationAction extends OwnerDesignAction $app->query('COMMIT'); - common_redirect(common_local_url('apps', - array('nickname' => $cur->nickname)), 303); + common_redirect(common_local_url('oauthappssettings'), 303); } diff --git a/actions/oauthappssettings.php b/actions/oauthappssettings.php new file mode 100644 index 000000000..6c0670b17 --- /dev/null +++ b/actions/oauthappssettings.php @@ -0,0 +1,166 @@ +. + * + * @category Settings + * @package StatusNet + * @author Zach Copley + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/settingsaction.php'; +require_once INSTALLDIR . '/lib/applicationlist.php'; + +/** + * Show a user's registered OAuth applications + * + * @category Settings + * @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/ + * + * @see SettingsAction + */ + +class OauthappssettingsAction extends SettingsAction +{ + var $page = 0; + + function prepare($args) + { + parent::prepare($args); + $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; + + if (!common_logged_in()) { + $this->clientError(_('You must be logged in to list your applications.')); + return false; + } + + return true; + } + + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('OAuth applications'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('Applications you have registered'); + } + + /** + * Content area of the page + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + + $offset = ($this->page - 1) * APPS_PER_PAGE; + $limit = APPS_PER_PAGE + 1; + + $application = new Oauth_application(); + $application->owner = $user->id; + $application->limit($offset, $limit); + $application->orderBy('created DESC'); + $application->find(); + + $cnt = 0; + + if ($application) { + $al = new ApplicationList($application, $user, $this); + $cnt = $al->show(); + if (0 == $cnt) { + $this->showEmptyListMessage(); + } + } + + $this->elementStart('p', array('id' => 'application_register')); + $this->element('a', + array('href' => common_local_url('newapplication'), + 'class' => 'more' + ), + 'Register a new application'); + $this->elementEnd('p'); + + $this->pagination( + $this->page > 1, + $cnt > APPS_PER_PAGE, + $this->page, + 'oauthappssettings' + ); + } + + function showEmptyListMessage() + { + $message = sprintf(_('You have not registered any applications yet.')); + + $this->elementStart('div', 'guide'); + $this->raw(common_markup_to_html($message)); + $this->elementEnd('div'); + } + + /** + * Handle posts to this form + * + * Based on the button that was pressed, muxes out to other functions + * to do the actual task requested. + * + * All sub-functions reload the form with a message -- success or failure. + * + * @return void + */ + + function handlePost() + { + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + } + +} diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index 56e7b02fb..99bb9022b 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -158,7 +158,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction $this->elementStart('p'); $this->raw(_('Developers can edit the registration settings for their applications ')); $this->element('a', - array('href' => common_local_url('apps', array('nickname' => $cur->nickname))), + array('href' => common_local_url('oauthappssettings')), 'here.'); $this->elementEnd('p'); } diff --git a/actions/showapplication.php b/actions/showapplication.php index f2ff8b900..bd3337136 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -211,15 +211,9 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementStart('ul'); $this->elementStart('li', 'entity_edit'); $this->element('a', - array('href' => - common_local_url( - 'editapplication', - array( - 'nickname' => $this->owner->nickname, - 'id' => $this->application->id - ) - ) - ), 'Edit'); + array('href' => common_local_url('editapplication', + array('id' => $this->application->id))), + 'Edit'); $this->elementEnd('li'); $this->elementStart('li', 'entity_reset_keysecret'); @@ -228,8 +222,7 @@ class ShowApplicationAction extends OwnerDesignAction 'class' => 'form_reset_key', 'method' => 'POST', 'action' => common_local_url('showapplication', - array('nickname' => $cur->nickname, - 'id' => $this->application->id)))); + array('id' => $this->application->id)))); $this->elementStart('fieldset'); $this->hidden('token', common_session_token()); @@ -273,13 +266,9 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementStart('p', array('id' => 'application_action')); $this->element('a', - array( - 'href' => common_local_url( - 'apps', - array('nickname' => $this->owner->nickname)), - 'class' => 'more' - ), - 'View your applications'); + array('href' => common_local_url('oauthappssettings'), + 'class' => 'more'), + 'View your applications'); $this->elementEnd('p'); } diff --git a/lib/applicationeditform.php b/lib/applicationeditform.php index e9ab46780..040d3bf74 100644 --- a/lib/applicationeditform.php +++ b/lib/applicationeditform.php @@ -119,12 +119,9 @@ class ApplicationEditForm extends Form if (!empty($this->application)) { return common_local_url('editapplication', - array('id' => $this->application->id, - 'nickname' => $cur->nickname) - ); + array('id' => $this->application->id)); } else { - return common_local_url('newapplication', - array('nickname' => $cur->nickname)); + return common_local_url('newapplication'); } } @@ -149,7 +146,7 @@ class ApplicationEditForm extends Form { if ($this->application) { $id = $this->application->id; - $icon = $this->application->icon; + $icon = $this->application->icon; $name = $this->application->name; $description = $this->application->description; $source_url = $this->application->source_url; @@ -160,7 +157,7 @@ class ApplicationEditForm extends Form $this->access_type = $this->application->access_type; } else { $id = ''; - $icon = ''; + $icon = ''; $name = ''; $description = ''; $source_url = ''; @@ -171,26 +168,26 @@ class ApplicationEditForm extends Form $this->access_type = ''; } - $this->out->hidden('token', common_session_token()); + $this->out->hidden('token', common_session_token()); $this->out->elementStart('ul', 'form_data'); - $this->out->elementStart('li', array('id' => 'application_icon')); + $this->out->elementStart('li', array('id' => 'application_icon')); - if (!empty($icon)) { - $this->out->element('img', array('src' => $icon)); - } + if (!empty($icon)) { + $this->out->element('img', array('src' => $icon)); + } - $this->out->element('label', array('for' => 'app_icon'), - _('Icon')); + $this->out->element('label', array('for' => 'app_icon'), + _('Icon')); $this->out->element('input', array('name' => 'app_icon', - 'type' => 'file', - 'id' => 'app_icon')); + 'type' => 'file', + 'id' => 'app_icon')); $this->out->element('p', 'form_guide', _('Icon for this application')); $this->out->element('input', array('name' => 'MAX_FILE_SIZE', - 'type' => 'hidden', - 'id' => 'MAX_FILE_SIZE', - 'value' => ImageFile::maxFileSizeInt())); + 'type' => 'hidden', + 'id' => 'MAX_FILE_SIZE', + 'value' => ImageFile::maxFileSizeInt())); $this->out->elementEnd('li'); $this->out->elementStart('li'); @@ -207,13 +204,13 @@ class ApplicationEditForm extends Form $maxDesc = Oauth_application::maxDesc(); if ($maxDesc > 0) { $descInstr = sprintf(_('Describe your application in %d chars'), - $maxDesc); + $maxDesc); } else { $descInstr = _('Describe your application'); } $this->out->textarea('description', _('Description'), ($this->out->arg('description')) ? $this->out->arg('description') : $description, - $descInstr); + $descInstr); $this->out->elementEnd('li'); @@ -259,8 +256,8 @@ class ApplicationEditForm extends Form $this->out->element('input', $attrs); $this->out->element('label', array('for' => 'app_type-browser', - 'class' => 'radio'), - _('Browser')); + 'class' => 'radio'), + _('Browser')); $attrs = array('name' => 'app_type', 'type' => 'radio', @@ -275,8 +272,8 @@ class ApplicationEditForm extends Form $this->out->element('input', $attrs); $this->out->element('label', array('for' => 'app_type-desktop', - 'class' => 'radio'), - _('Desktop')); + 'class' => 'radio'), + _('Desktop')); $this->out->element('p', 'form_guide', _('Type of application, browser or desktop')); $this->out->elementEnd('li'); @@ -298,8 +295,8 @@ class ApplicationEditForm extends Form $this->out->element('input', $attrs); $this->out->element('label', array('for' => 'default_access_type-ro', - 'class' => 'radio'), - _('Read-only')); + 'class' => 'radio'), + _('Read-only')); $attrs = array('name' => 'default_access_type', 'type' => 'radio', @@ -309,15 +306,15 @@ class ApplicationEditForm extends Form if ($this->application->access_type & Oauth_application::$readAccess && $this->application->access_type & Oauth_application::$writeAccess - ) { + ) { $attrs['checked'] = 'checked'; } $this->out->element('input', $attrs); $this->out->element('label', array('for' => 'default_access_type-rw', - 'class' => 'radio'), - _('Read-write')); + 'class' => 'radio'), + _('Read-write')); $this->out->element('p', 'form_guide', _('Default access for this application: read-only, or read-write')); $this->out->elementEnd('li'); @@ -334,8 +331,8 @@ class ApplicationEditForm extends Form function formActions() { $this->out->submit('cancel', _('Cancel'), 'submit form_action-primary', - 'cancel', _('Cancel')); + 'cancel', _('Cancel')); $this->out->submit('save', _('Save'), 'submit form_action-secondary', - 'save', _('Save')); + 'save', _('Save')); } } diff --git a/lib/applicationlist.php b/lib/applicationlist.php index 15c2d588a..f2eaefb40 100644 --- a/lib/applicationlist.php +++ b/lib/applicationlist.php @@ -64,7 +64,7 @@ class ApplicationList extends Widget $this->application = $application; $this->owner = $owner; $this->action = $action; - $this->connections = $connections; + $this->connections = $connections; } function show() @@ -97,10 +97,9 @@ class ApplicationList extends Widget $this->out->elementStart('span', 'vcard author'); if (!$this->connections) { $this->out->elementStart('a', - array('href' => common_local_url('showapplication', - array('nickname' => $user->nickname, - 'id' => $this->application->id)), - 'class' => 'url')); + array('href' => common_local_url('showapplication', + array('id' => $this->application->id)), + 'class' => 'url')); } else { $this->out->elementStart('a', array('href' => $this->application->source_url, @@ -154,8 +153,4 @@ class ApplicationList extends Widget return; } - function highlight($text) - { - return htmlspecialchars($text); - } } diff --git a/lib/router.php b/lib/router.php index d6e448c2f..42bff2778 100644 --- a/lib/router.php +++ b/lib/router.php @@ -141,7 +141,7 @@ class Router // settings foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections', - 'email', 'sms', 'userdesign', 'other') as $s) { + 'oauthapps', 'email', 'sms', 'userdesign', 'other') as $s) { $m->connect('settings/'.$s, array('action' => $s.'settings')); } @@ -634,28 +634,23 @@ class Router // user stuff foreach (array('subscriptions', 'subscribers', - 'nudge', 'all', 'foaf', 'xrds', 'apps', + 'nudge', 'all', 'foaf', 'xrds', 'replies', 'inbox', 'outbox', 'microsummary') as $a) { $m->connect(':nickname/'.$a, array('action' => $a), array('nickname' => '[a-zA-Z0-9]{1,64}')); } - $m->connect(':nickname/apps', - array('action' => 'apps'), - array('nickname' => '['.NICKNAME_FMT.']{1,64}')); - $m->connect(':nickname/apps/show/:id', + $m->connect('settings/oauthapps/show/:id', array('action' => 'showapplication'), - array('nickname' => '['.NICKNAME_FMT.']{1,64}', - 'id' => '[0-9]+') + array('id' => '[0-9]+') ); - $m->connect(':nickname/apps/new', - array('action' => 'newapplication'), - array('nickname' => '['.NICKNAME_FMT.']{1,64}')); - $m->connect(':nickname/apps/edit/:id', + $m->connect('settings/oauthapps/new', + array('action' => 'newapplication') + ); + $m->connect('settings/oauthapps/edit/:id', array('action' => 'editapplication'), - array('nickname' => '['.NICKNAME_FMT.']{1,64}', - 'id' => '[0-9]+') + array('id' => '[0-9]+') ); $m->connect('api/oauth/request_token', -- cgit v1.2.3-54-g00ecf From c0eee277d1058c9c291b3c4474cc8a72cb8c6d0e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 11:31:15 +0000 Subject: Make sure applications are really looked up by consumer key --- actions/apioauthauthorize.php | 42 +++--------------------------------------- lib/apioauthstore.php | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 40 deletions(-) (limited to 'actions') diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index cdf9cb7df..0966ba1d7 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -74,42 +74,11 @@ class ApiOauthAuthorizeAction extends ApiOauthAction $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); return true; } - function getApp() - { - // Look up the full req token - - $req_token = $this->store->lookup_token(null, - 'request', - $this->oauth_token); - - if (empty($req_token)) { - - common_debug("Couldn't find request token!"); - - $this->clientError(_('Bad request.')); - return; - } - - // Look up the app - - $app = new Oauth_application(); - $app->consumer_key = $req_token->consumer_key; - $result = $app->find(true); - - if (!empty($result)) { - $this->app = $app; - return true; - - } else { - common_debug("couldn't find the app!"); - return false; - } - } - /** * Handle input, produce output * @@ -140,7 +109,8 @@ class ApiOauthAuthorizeAction extends ApiOauthAction return; } - if (!$this->getApp()) { + if (empty($this->app)) { + common_debug('No app for that token.'); $this->clientError(_('Bad request.')); return; } @@ -166,11 +136,6 @@ class ApiOauthAuthorizeAction extends ApiOauthAction return; } - if (!$this->getApp()) { - $this->clientError(_('Bad request.')); - return; - } - // check creds $user = null; @@ -416,7 +381,6 @@ class ApiOauthAuthorizeAction extends ApiOauthAction function getInstructions() { return _('Allow or deny access to your account information.'); - } /** diff --git a/lib/apioauthstore.php b/lib/apioauthstore.php index c39ddbb0f..32110d057 100644 --- a/lib/apioauthstore.php +++ b/lib/apioauthstore.php @@ -36,6 +36,44 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore $con->consumer_secret); } + function getAppByRequestToken($token_key) + { + // Look up the full req tokenx + + $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) { common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); @@ -64,7 +102,7 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore if (!empty($result)) { common_debug("Oath app user found."); } else { - common_debug("Oauth app user not found."); + common_debug("Oauth app user not found. app id $app->id token $rt->tok"); return null; } -- cgit v1.2.3-54-g00ecf From ba68e042a8acb9dd1054e0bc1c5cd4dfd415642e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 17:52:25 +0000 Subject: Fix user count --- actions/showapplication.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'actions') diff --git a/actions/showapplication.php b/actions/showapplication.php index bd3337136..b21b994aa 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -194,10 +194,13 @@ class ShowApplicationAction extends OwnerDesignAction $defaultAccess = ($this->application->access_type & Oauth_application::$writeAccess) ? 'read-write' : 'read-only'; $profile = Profile::staticGet($this->application->owner); - $userCnt = 0; // XXX: count how many users use the app + + $appUsers = new Oauth_application_user(); + $appUsers->application_id = $this->application->id; + $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 @@ -222,7 +225,7 @@ class ShowApplicationAction extends OwnerDesignAction 'class' => 'form_reset_key', 'method' => 'POST', 'action' => common_local_url('showapplication', - array('id' => $this->application->id)))); + array('id' => $this->application->id)))); $this->elementStart('fieldset'); $this->hidden('token', common_session_token()); -- cgit v1.2.3-54-g00ecf From 7b3c099f953c1569c18d81fdb8d4230e927d429e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 18:20:03 +0000 Subject: Ensure only the application's owner can edit it --- actions/editapplication.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'actions') diff --git a/actions/editapplication.php b/actions/editapplication.php index a6db87c61..9cc3e3cea 100644 --- a/actions/editapplication.php +++ b/actions/editapplication.php @@ -45,9 +45,9 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { class EditApplicationAction extends OwnerDesignAction { - var $msg = null; - - var $app = null; + var $msg = null; + var $owner = null; + var $app = null; function title() { @@ -68,7 +68,14 @@ class EditApplicationAction extends OwnerDesignAction } $id = (int)$this->arg('id'); - $this->app = Oauth_application::staticGet($id); + + $this->app = Oauth_application::staticGet($id); + $this->owner = User::staticGet($this->app->owner); + $cur = common_current_user(); + + if ($cur->id != $this->owner->id) { + $this->clientError(_('You are not the owner of this application.'), 401); + } if (!$this->app) { $this->clientError(_('No such application.')); -- cgit v1.2.3-54-g00ecf From 6d58ef4abb12d735b6be777ea79f99a07c68694a Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 13 Jan 2010 20:10:09 +0000 Subject: Updated apioauthauthorize markup and styles --- actions/apioauthauthorize.php | 46 +++++++++++-------------------------------- theme/base/css/display.css | 10 ++++++++-- 2 files changed, 20 insertions(+), 36 deletions(-) (limited to 'actions') diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 0966ba1d7..72d142651 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -273,27 +273,6 @@ class ApiOauthAuthorizeAction extends ApiOauthAction return _('An application would like to connect to your account'); } - /** - * Show page notice - * - * Display a notice for how to use the page, or the - * error if it exists. - * - * @return void - */ - - function showPageNotice() - { - if ($this->error) { - $this->element('p', 'error', $this->error); - } else { - $instr = $this->getInstructions(); - $output = common_markup_to_html($instr); - - $this->raw($output); - } - } - /** * Shows the authorization form. * @@ -303,40 +282,38 @@ class ApiOauthAuthorizeAction extends ApiOauthAction function showContent() { $this->elementStart('form', array('method' => 'post', - 'id' => 'form_login', + 'id' => 'form_apioauthauthorize', 'class' => 'form_settings', 'action' => common_local_url('apioauthauthorize'))); + $this->elementStart('fieldset'); + $this->element('legend', array('id' => 'apioauthauthorize_allowdeny'), + _('Allow or deny access')); $this->hidden('token', common_session_token()); $this->hidden('oauth_token', $this->oauth_token); $this->hidden('oauth_callback', $this->callback); - $this->elementStart('fieldset'); - - $this->elementStart('ul'); + $this->elementStart('ul', 'form_data'); $this->elementStart('li'); + $this->elementStart('p'); if (!empty($this->app->icon)) { $this->element('img', array('src' => $this->app->icon)); } - $this->elementEnd('li'); - $this->elementStart('li'); $access = ($this->app->access_type & Oauth_application::$writeAccess) ? 'access and update' : 'access'; - $msg = _("The application %s by %s would like " . - "the ability to %s your account data."); + $msg = _("The application %s by %s would like " . + "the ability to %s your account data."); $this->raw(sprintf($msg, $this->app->name, $this->app->organization, $access)); - + $this->elementEnd('p'); $this->elementEnd('li'); $this->elementEnd('ul'); - $this->elementEnd('fieldset'); - if (!common_logged_in()) { $this->elementStart('fieldset'); @@ -355,17 +332,18 @@ class ApiOauthAuthorizeAction extends ApiOauthAction } $this->element('input', array('id' => 'deny_submit', - 'class' => 'submit', + 'class' => 'submit submit form_action-primary', 'name' => 'deny', 'type' => 'submit', 'value' => _('Deny'))); $this->element('input', array('id' => 'allow_submit', - 'class' => 'submit', + 'class' => 'submit submit form_action-secondary', 'name' => 'allow', 'type' => 'submit', 'value' => _('Allow'))); + $this->elementEnd('fieldset'); $this->elementEnd('form'); } diff --git a/theme/base/css/display.css b/theme/base/css/display.css index 82670c964..ff81d3727 100644 --- a/theme/base/css/display.css +++ b/theme/base/css/display.css @@ -177,7 +177,8 @@ font-weight:bold; #form_password_recover legend, #form_password_change legend, .form_entity_block legend, -#form_filter_bytag legend { +#form_filter_bytag legend, +#apioauthauthorize_allowdeny { display:none; } @@ -906,10 +907,15 @@ list-style-type:none; } .application img, #showapplication .entity_profile img, -#editapplication .form_data #application_icon img { +#editapplication .form_data #application_icon, +#apioauthauthorize .form_data img { max-width:96px; max-height:96px; } +#apioauthauthorize .form_data img { +margin-right:18px; +float:left; +} #showapplication .entity_profile { width:68%; } -- cgit v1.2.3-54-g00ecf From dbcbc2fe7ff0368910a49ed5675b0edc1e2bf18d Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Wed, 13 Jan 2010 20:43:23 +0000 Subject: Changed legend text from Login to Account because it is not really logging iny --- actions/apioauthauthorize.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actions') diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php index 72d142651..fa074c4e7 100644 --- a/actions/apioauthauthorize.php +++ b/actions/apioauthauthorize.php @@ -317,7 +317,7 @@ class ApiOauthAuthorizeAction extends ApiOauthAction if (!common_logged_in()) { $this->elementStart('fieldset'); - $this->element('legend', null, _('Login')); + $this->element('legend', null, _('Account')); $this->elementStart('ul', 'form_data'); $this->elementStart('li'); $this->input('nickname', _('Nickname')); -- cgit v1.2.3-54-g00ecf From 9e7f47652d860d7f1e296dd369c4e68814bf2636 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 13 Jan 2010 21:11:08 +0000 Subject: Revoke access token UI --- actions/oauthconnectionssettings.php | 62 +++++++++++++++++++++++++++++++----- actions/showapplication.php | 1 + classes/Oauth_application_user.php | 2 +- classes/Profile.php | 3 +- lib/applicationlist.php | 14 +++++++- 5 files changed, 71 insertions(+), 11 deletions(-) (limited to 'actions') diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index 99bb9022b..b17729b82 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -50,10 +50,12 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction { var $page = null; + var $id = null; function prepare($args) { parent::prepare($args); + $this->id = (int)$this->arg('id'); $this->page = ($this->arg('page')) ? ($this->arg('page') + 0) : 1; return true; } @@ -101,16 +103,16 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction $application = $profile->getApplications($offset, $limit); - $cnt == 0; + $cnt == 0; - if (!empty($application)) { - $al = new ApplicationList($application, $user, $this, true); - $cnt = $al->show(); - } + if (!empty($application)) { + $al = new ApplicationList($application, $user, $this, true); + $cnt = $al->show(); + } - if ($cnt == 0) { - $this->showEmptyListMessage(); - } + if ($cnt == 0) { + $this->showEmptyListMessage(); + } $this->pagination($this->page > 1, $cnt > APPS_PER_PAGE, $this->page, 'connectionssettings', @@ -139,6 +141,50 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction return; } + if ($this->arg('revoke')) { + $this->revokeAccess($this->id); + + // XXX: Show some indicator to the user of what's been done. + + $this->showPage(); + } else { + $this->clientError(_('Unexpected form submission.'), 401); + return false; + } + } + + function revokeAccess($appId) + { + $cur = common_current_user(); + + $app = Oauth_application::staticGet('id', $appId); + + if (empty($app)) { + $this->clientError(_('No such application.'), 404); + return false; + } + + $appUser = Oauth_application_user::getByKeys($cur, $app); + + if (empty($appUser)) { + $this->clientError(_('You are not a user of that application.'), 401); + return false; + } + + $orig = clone($appUser); + $appUser->access_type = 0; // No access + $result = $appUser->update(); + + if (!$result) { + common_log_db_error($orig, 'UPDATE', __FILE__); + $this->clientError(_('Unable to revoke access for app: ' . $app->id)); + return false; + } + + $msg = 'User %s (id: %d) revoked access to app %s (id: %d)'; + common_log(LOG_INFO, sprintf($msg, $cur->nickname, + $cur->id, $app->name, $app->id)); + } function showEmptyListMessage() diff --git a/actions/showapplication.php b/actions/showapplication.php index b21b994aa..049206375 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -92,6 +92,7 @@ class ShowApplicationAction extends OwnerDesignAction if ($cur->id != $this->owner->id) { $this->clientError(_('You are not the owner of this application.'), 401); + return false; } return true; diff --git a/classes/Oauth_application_user.php b/classes/Oauth_application_user.php index a05371f56..618d68133 100644 --- a/classes/Oauth_application_user.php +++ b/classes/Oauth_application_user.php @@ -34,7 +34,7 @@ class Oauth_application_user extends Memcached_DataObject $oau = new Oauth_application_user(); $oau->profile_id = $user->id; - $oau->application_id = $app->id; + $oau->application_id = $app->id; $oau->limit(1); $result = $oau->find(true); diff --git a/classes/Profile.php b/classes/Profile.php index fef2a2171..1076fb2cb 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -358,7 +358,8 @@ class Profile extends Memcached_DataObject 'SELECT a.* ' . 'FROM oauth_application_user u, oauth_application a ' . 'WHERE u.profile_id = %d ' . - 'AND a.id = u.application_id ' . + 'AND a.id = u.application_id ' . + 'AND u.access_type > 0 ' . 'ORDER BY u.created DESC '; if ($offset > 0) { diff --git a/lib/applicationlist.php b/lib/applicationlist.php index 6eae26135..3abb1f8aa 100644 --- a/lib/applicationlist.php +++ b/lib/applicationlist.php @@ -142,7 +142,19 @@ class ApplicationList extends Widget $this->out->raw($txt); $this->out->elementEnd('li'); - // XXX: Add revoke access button + $this->out->elementStart('li', 'entity_revoke'); + $this->out->elementStart('form', array('id' => 'form_revoke_app', + 'class' => 'form_revoke_app', + 'method' => 'POST', + 'action' => + common_local_url('oauthconnectionssettings'))); + $this->out->elementStart('fieldset'); + $this->out->hidden('id', $this->application->id); + $this->out->hidden('token', common_session_token()); + $this->out->submit('revoke', _('Revoke')); + $this->out->elementEnd('fieldset'); + $this->out->elementEnd('form'); + $this->out->elementEnd('li'); } } -- cgit v1.2.3-54-g00ecf From 4daf76212a6802863d20c6af7597eddded227ae8 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 14 Jan 2010 02:38:01 +0000 Subject: - Had to remove checking read vs. read-write in OAuth authenticated methods - Will now pick up source attr from OAuth app --- actions/apiaccountverifycredentials.php | 14 ++++++++++++++ actions/apistatusesupdate.php | 5 +++++ lib/apiauth.php | 14 +++++--------- 3 files changed, 24 insertions(+), 9 deletions(-) (limited to 'actions') diff --git a/actions/apiaccountverifycredentials.php b/actions/apiaccountverifycredentials.php index 08b201dbf..1095d5162 100644 --- a/actions/apiaccountverifycredentials.php +++ b/actions/apiaccountverifycredentials.php @@ -82,4 +82,18 @@ class ApiAccountVerifyCredentialsAction extends ApiAuthAction } + /** + * Is this action read only? + * + * @param array $args other arguments + * + * @return boolean true + * + **/ + + function isReadOnly($args) + { + return true; + } + } diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php index f594bbf39..f8bf7cf87 100644 --- a/actions/apistatusesupdate.php +++ b/actions/apistatusesupdate.php @@ -85,6 +85,11 @@ class ApiStatusesUpdateAction extends ApiAuthAction $this->lat = $this->trimmed('lat'); $this->lon = $this->trimmed('long'); + // try to set the source attr from OAuth app + if (empty($this->source)) { + $this->source = $this->oauth_source; + } + if (empty($this->source) || in_array($this->source, self::$reserved_sources)) { $this->source = 'api'; } diff --git a/lib/apiauth.php b/lib/apiauth.php index f513ed2c9..37070d212 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -55,6 +55,7 @@ class ApiAuthAction extends ApiAction { var $access_token; var $oauth_access_type; + var $oauth_source; /** * Take arguments for running, and output basic auth header if needed @@ -90,13 +91,6 @@ class ApiAuthAction extends ApiAction function handle($args) { parent::handle($args); - - if ($this->isReadOnly($args) == false) { - if ($this->access == self::READ_ONLY) { - $this->clientError(_('API method requires write access.'), 401); - exit(); - } - } } function checkOAuthRequest() @@ -116,8 +110,6 @@ class ApiAuthAction extends ApiAction $req = OAuthRequest::from_request(); $server->verify_request($req); - common_debug("Good OAuth request!"); - $app = Oauth_application::getByConsumerKey($this->consumer_key); if (empty($app)) { @@ -129,6 +121,10 @@ class ApiAuthAction extends ApiAction throw new OAuthException('No application for that consumer key.'); } + // set the source attr + + $this->oauth_source = $app->name; + $appUser = Oauth_application_user::staticGet('token', $this->access_token); -- cgit v1.2.3-54-g00ecf From e400437d5783a5a1a03feb3ea3e5b6a40a71ed60 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:50:07 -0500 Subject: fix interpolation for positional arguments in showstream --- actions/showstream.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actions') diff --git a/actions/showstream.php b/actions/showstream.php index 75e10858d..90ff67073 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -76,7 +76,7 @@ class ShowstreamAction extends ProfileAction if ($this->page == 1) { return $base; } else { - return sprintf(_("%1$s, page %2$d"), + return sprintf(_('%1$s, page %2$d'), $base, $this->page); } -- cgit v1.2.3-54-g00ecf From fc7afed92478117d009d835a4d1cd0565ada1089 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:52:03 -0500 Subject: fix interpolation for positional arguments in replies --- actions/replies.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actions') diff --git a/actions/replies.php b/actions/replies.php index 2e50f1c3c..164c328db 100644 --- a/actions/replies.php +++ b/actions/replies.php @@ -124,7 +124,7 @@ class RepliesAction extends OwnerDesignAction if ($this->page == 1) { return sprintf(_("Replies to %s"), $this->user->nickname); } else { - return sprintf(_("Replies to %1$s, page %2$d"), + return sprintf(_('Replies to %1$s, page %2$d'), $this->user->nickname, $this->page); } -- cgit v1.2.3-54-g00ecf From 02526f1100621aa522ccb6beccc0a4e507da328a Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:53:29 -0500 Subject: fix interpolation of positional arguments to sprintf in outbox --- actions/outbox.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/outbox.php b/actions/outbox.php index de30de018..b81d4b9d0 100644 --- a/actions/outbox.php +++ b/actions/outbox.php @@ -55,10 +55,10 @@ class OutboxAction extends MailboxAction function title() { if ($this->page > 1) { - return sprintf(_("Outbox for %1$s - page %2$d"), + return sprintf(_('Outbox for %1$s - page %2$d'), $this->user->nickname, $page); } else { - return sprintf(_("Outbox for %s"), $this->user->nickname); + return sprintf(_('Outbox for %s'), $this->user->nickname); } } -- cgit v1.2.3-54-g00ecf From 9077db00a50a6123ece70eb690b658c19c5aeb27 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:54:25 -0500 Subject: fix interpolation of positional arguments to sprintf in inbox --- actions/inbox.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/inbox.php b/actions/inbox.php index f605cc9e8..8330f753f 100644 --- a/actions/inbox.php +++ b/actions/inbox.php @@ -56,10 +56,10 @@ class InboxAction extends MailboxAction function title() { if ($this->page > 1) { - return sprintf(_("Inbox for %1$s - page %2$d"), $this->user->nickname, + return sprintf(_('Inbox for %1$s - page %2$d'), $this->user->nickname, $this->page); } else { - return sprintf(_("Inbox for %s"), $this->user->nickname); + return sprintf(_('Inbox for %s'), $this->user->nickname); } } -- cgit v1.2.3-54-g00ecf From 73fdec6c12c308732f0673f2d10ab1542c927a33 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:55:29 -0500 Subject: fix interpolation of positional arguments to sprintf in usergroups --- actions/usergroups.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/usergroups.php b/actions/usergroups.php index 504226143..97faabae6 100644 --- a/actions/usergroups.php +++ b/actions/usergroups.php @@ -59,9 +59,9 @@ class UsergroupsAction extends OwnerDesignAction function title() { if ($this->page == 1) { - return sprintf(_("%s groups"), $this->user->nickname); + return sprintf(_('%s groups'), $this->user->nickname); } else { - return sprintf(_("%1$s groups, page %2$d"), + return sprintf(_('%1$s groups, page %2$d'), $this->user->nickname, $this->page); } -- cgit v1.2.3-54-g00ecf From 019dad95e12191632a3828fb0950a80f305bbf01 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:56:41 -0500 Subject: fix interpolation of positional arguments to sprintf in show favorites --- actions/showfavorites.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/showfavorites.php b/actions/showfavorites.php index 6023f0156..f2d082293 100644 --- a/actions/showfavorites.php +++ b/actions/showfavorites.php @@ -74,9 +74,9 @@ class ShowfavoritesAction extends OwnerDesignAction function title() { if ($this->page == 1) { - return sprintf(_("%s's favorite notices"), $this->user->nickname); + return sprintf(_('%s\'s favorite notices'), $this->user->nickname); } else { - return sprintf(_("%1$s's favorite notices, page %2$d"), + return sprintf(_('%1$s\'s favorite notices, page %2$d'), $this->user->nickname, $this->page); } -- cgit v1.2.3-54-g00ecf From a9da43a41644b3e1ff27e8250fd858d2c2840e7b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:57:33 -0500 Subject: fix interpolation of positional arguments to sprintf in show group --- actions/showgroup.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/showgroup.php b/actions/showgroup.php index 06ae572e8..8042a4951 100644 --- a/actions/showgroup.php +++ b/actions/showgroup.php @@ -79,9 +79,9 @@ class ShowgroupAction extends GroupDesignAction } if ($this->page == 1) { - return sprintf(_("%s group"), $base); + return sprintf(_('%s group'), $base); } else { - return sprintf(_("%1$s group, page %2$d"), + return sprintf(_('%1$s group, page %2$d'), $base, $this->page); } -- cgit v1.2.3-54-g00ecf From 089305ac7a89ba42286973feb14ca23687ec0995 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 24 Jan 2010 22:59:22 -0500 Subject: fix interpolation of positional arguments to sprintf in tag action --- actions/tag.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'actions') diff --git a/actions/tag.php b/actions/tag.php index 12857236e..e91df6ea9 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -63,9 +63,9 @@ class TagAction extends Action function title() { if ($this->page == 1) { - return sprintf(_("Notices tagged with %s"), $this->tag); + return sprintf(_('Notices tagged with %s'), $this->tag); } else { - return sprintf(_("Notices tagged with %1$s, page %2$d"), + return sprintf(_('Notices tagged with %1$s, page %2$d'), $this->tag, $this->page); } @@ -85,7 +85,7 @@ class TagAction extends Action array('tag' => $this->tag)), sprintf(_('Notice feed for tag %s (RSS 1.0)'), $this->tag)), - new Feed(Feed::RSS2, + new Feed(Feed::RSS2, common_local_url('ApiTimelineTag', array('format' => 'rss', 'tag' => $this->tag)), -- cgit v1.2.3-54-g00ecf From 03685bba1ea6c1b8f2c3db8d2f095a221b29a464 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 26 Jan 2010 15:11:09 -0800 Subject: - Remove redudant/unused 'server' setting from site admin panel - Move 'fancy urls' checkbox from site admin panel to paths admin panel --- actions/pathsadminpanel.php | 33 ++++++++++++++++++++++++++++++--- actions/siteadminpanel.php | 21 +++------------------ 2 files changed, 33 insertions(+), 21 deletions(-) (limited to 'actions') diff --git a/actions/pathsadminpanel.php b/actions/pathsadminpanel.php index 3779fcfaa..9155a7e42 100644 --- a/actions/pathsadminpanel.php +++ b/actions/pathsadminpanel.php @@ -24,7 +24,7 @@ * @author Evan Prodromou * @author Zach Copley * @author Sarven Capadisli - * @copyright 2008-2009 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/ */ @@ -98,6 +98,11 @@ class PathsadminpanelAction extends AdminPanelAction 'background' => array('server', 'dir', 'path') ); + // XXX: If we're only going to have one boolean on thi page we + // can remove some of the boolean processing code --Z + + static $booleans = array('site' => array('fancy')); + $values = array(); foreach ($settings as $section => $parts) { @@ -106,6 +111,12 @@ class PathsadminpanelAction extends AdminPanelAction } } + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0; + } + } + $this->validate($values); // assert(all values are valid); @@ -120,7 +131,13 @@ class PathsadminpanelAction extends AdminPanelAction } } - $config->query('COMMIT'); + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + Config::save($section, $setting, $values[$section][$setting]); + } + } + + $config->query('COMMIT'); return; } @@ -213,10 +230,14 @@ class PathsAdminPanelForm extends AdminForm function formData() { - $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale')); + $this->out->elementStart('fieldset', array('id' => 'settings_paths_locale')); $this->out->element('legend', null, _('Site'), 'site'); $this->out->elementStart('ul', 'form_data'); + $this->li(); + $this->input('server', _('Server'), _('Site\'s server hostname.')); + $this->unli(); + $this->li(); $this->input('path', _('Path'), _('Site path')); $this->unli(); @@ -225,6 +246,12 @@ class PathsAdminPanelForm extends AdminForm $this->input('locale_path', _('Path to locales'), _('Directory path to locales'), 'site'); $this->unli(); + $this->li(); + $this->out->checkbox('fancy', _('Fancy URLs'), + (bool) $this->value('fancy'), + _('Use fancy (more readable and memorable) URLs?')); + $this->unli(); + $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); diff --git a/actions/siteadminpanel.php b/actions/siteadminpanel.php index dd388a18a..1cb57ec09 100644 --- a/actions/siteadminpanel.php +++ b/actions/siteadminpanel.php @@ -24,7 +24,7 @@ * @author Evan Prodromou * @author Zach Copley * @author Sarven Capadisli - * @copyright 2008-2009 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/ */ @@ -95,7 +95,7 @@ class SiteadminpanelAction extends AdminPanelAction 'site', 'textlimit', 'dupelimit'), 'snapshot' => array('run', 'reporturl', 'frequency')); - static $booleans = array('site' => array('private', 'inviteonly', 'closed', 'fancy')); + static $booleans = array('site' => array('private', 'inviteonly', 'closed')); $values = array(); @@ -299,22 +299,7 @@ class SiteAdminPanelForm extends AdminForm $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); - $this->out->elementStart('fieldset', array('id' => 'settings_admin_urls')); - $this->out->element('legend', null, _('URLs')); - $this->out->elementStart('ul', 'form_data'); - $this->li(); - $this->input('server', _('Server'), _('Site\'s server hostname.')); - $this->unli(); - - $this->li(); - $this->out->checkbox('fancy', _('Fancy URLs'), - (bool) $this->value('fancy'), - _('Use fancy (more readable and memorable) URLs?')); - $this->unli(); - $this->out->elementEnd('ul'); - $this->out->elementEnd('fieldset'); - - $this->out->elementStart('fieldset', array('id' => 'settings_admin_access')); + $this->out->elementStart('fieldset', array('id' => 'settings_admin_access')); $this->out->element('legend', null, _('Access')); $this->out->elementStart('ul', 'form_data'); $this->li(); -- cgit v1.2.3-54-g00ecf From aad42427ccd4d3824efcbce43d8e9dba6d5b475d Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 26 Jan 2010 15:56:19 -0800 Subject: New access admin panel for site registration settings --- actions/accessadminpanel.php | 192 +++++++++++++++++++++++++++++++++++++++++++ actions/siteadminpanel.php | 37 --------- lib/adminpanelaction.php | 13 ++- lib/default.php | 2 +- lib/router.php | 1 + 5 files changed, 203 insertions(+), 42 deletions(-) create mode 100644 actions/accessadminpanel.php (limited to 'actions') diff --git a/actions/accessadminpanel.php b/actions/accessadminpanel.php new file mode 100644 index 000000000..4768e2faf --- /dev/null +++ b/actions/accessadminpanel.php @@ -0,0 +1,192 @@ +. + * + * @category Settings + * @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); +} + +/** + * Administer site access settings + * + * @category Admin + * @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 AccessadminpanelAction extends AdminPanelAction +{ + /** + * Returns the page title + * + * @return string page title + */ + + function title() + { + return _('Access'); + } + + /** + * Instructions for using this form. + * + * @return string instructions + */ + + function getInstructions() + { + return _('Site access settings'); + } + + /** + * Show the site admin panel form + * + * @return void + */ + + function showForm() + { + $form = new AccessAdminPanelForm($this); + $form->show(); + return; + } + + /** + * Save settings from the form + * + * @return void + */ + + function saveSettings() + { + static $booleans = array('site' => array('private', 'inviteonly', 'closed')); + + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0; + } + } + + $config = new Config(); + + $config->query('BEGIN'); + + foreach ($booleans as $section => $parts) { + foreach ($parts as $setting) { + Config::save($section, $setting, $values[$section][$setting]); + } + } + + $config->query('COMMIT'); + + return; + } + +} + +class AccessAdminPanelForm extends AdminForm +{ + /** + * ID of the form + * + * @return int ID of the form + */ + + function id() + { + return 'form_site_admin_panel'; + } + + /** + * class of the form + * + * @return string class of the form + */ + + function formClass() + { + return 'form_settings'; + } + + /** + * Action of the form + * + * @return string URL of the action + */ + + function action() + { + return common_local_url('accessadminpanel'); + } + + /** + * Data elements of the form + * + * @return void + */ + + function formData() + { + $this->out->elementStart('fieldset', array('id' => 'settings_admin_access')); + $this->out->element('legend', null, _('Registration')); + $this->out->elementStart('ul', 'form_data'); + $this->li(); + $this->out->checkbox('private', _('Private'), + (bool) $this->value('private'), + _('Prohibit anonymous users (not logged in) from viewing site?')); + $this->unli(); + + $this->li(); + $this->out->checkbox('inviteonly', _('Invite only'), + (bool) $this->value('inviteonly'), + _('Make registration invitation only.')); + $this->unli(); + + $this->li(); + $this->out->checkbox('closed', _('Closed'), + (bool) $this->value('closed'), + _('Disable new registrations.')); + $this->unli(); + $this->out->elementEnd('ul'); + $this->out->elementEnd('fieldset'); + } + + /** + * Action elements + * + * @return void + */ + + function formActions() + { + $this->out->submit('submit', _('Save'), 'submit', null, _('Save access settings')); + } + +} diff --git a/actions/siteadminpanel.php b/actions/siteadminpanel.php index 1cb57ec09..8c8f8b374 100644 --- a/actions/siteadminpanel.php +++ b/actions/siteadminpanel.php @@ -95,8 +95,6 @@ class SiteadminpanelAction extends AdminPanelAction 'site', 'textlimit', 'dupelimit'), 'snapshot' => array('run', 'reporturl', 'frequency')); - static $booleans = array('site' => array('private', 'inviteonly', 'closed')); - $values = array(); foreach ($settings as $section => $parts) { @@ -105,12 +103,6 @@ class SiteadminpanelAction extends AdminPanelAction } } - foreach ($booleans as $section => $parts) { - foreach ($parts as $setting) { - $values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0; - } - } - // This throws an exception on validation errors $this->validate($values); @@ -127,12 +119,6 @@ class SiteadminpanelAction extends AdminPanelAction } } - foreach ($booleans as $section => $parts) { - foreach ($parts as $setting) { - Config::save($section, $setting, $values[$section][$setting]); - } - } - $config->query('COMMIT'); return; @@ -299,29 +285,6 @@ class SiteAdminPanelForm extends AdminForm $this->out->elementEnd('ul'); $this->out->elementEnd('fieldset'); - $this->out->elementStart('fieldset', array('id' => 'settings_admin_access')); - $this->out->element('legend', null, _('Access')); - $this->out->elementStart('ul', 'form_data'); - $this->li(); - $this->out->checkbox('private', _('Private'), - (bool) $this->value('private'), - _('Prohibit anonymous users (not logged in) from viewing site?')); - $this->unli(); - - $this->li(); - $this->out->checkbox('inviteonly', _('Invite only'), - (bool) $this->value('inviteonly'), - _('Make registration invitation only.')); - $this->unli(); - - $this->li(); - $this->out->checkbox('closed', _('Closed'), - (bool) $this->value('closed'), - _('Disable new registrations.')); - $this->unli(); - $this->out->elementEnd('ul'); - $this->out->elementEnd('fieldset'); - $this->out->elementStart('fieldset', array('id' => 'settings_admin_snapshots')); $this->out->element('legend', null, _('Snapshots')); $this->out->elementStart('ul', 'form_data'); diff --git a/lib/adminpanelaction.php b/lib/adminpanelaction.php index a6981ac61..f62bfa458 100644 --- a/lib/adminpanelaction.php +++ b/lib/adminpanelaction.php @@ -319,12 +319,17 @@ class AdminPanelNav extends Widget if ($this->canAdmin('user')) { $this->out->menuItem(common_local_url('useradminpanel'), _('User'), - _('Paths configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel'); + _('User configuration'), $action_name == 'useradminpanel', 'nav_design_admin_panel'); } - if ($this->canAdmin('paths')) { - $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'), - _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel'); + if ($this->canAdmin('access')) { + $this->out->menuItem(common_local_url('accessadminpanel'), _('Access'), + _('Access configuration'), $action_name == 'accessadminpanel', 'nav_design_admin_panel'); + } + + if ($this->canAdmin('paths')) { + $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'), + _('Paths configuration'), $action_name == 'pathsadminpanel', 'nav_design_admin_panel'); } Event::handle('EndAdminPanelNav', array($this)); diff --git a/lib/default.php b/lib/default.php index d2dd8ab33..37fb65b53 100644 --- a/lib/default.php +++ b/lib/default.php @@ -260,7 +260,7 @@ $default = 'OpenID' => null), ), 'admin' => - array('panels' => array('design', 'site', 'user', 'paths')), + array('panels' => array('design', 'site', 'user', 'paths', 'access')), 'singleuser' => array('enabled' => false, 'nickname' => null), diff --git a/lib/router.php b/lib/router.php index c0ddc8db3..03765b39d 100644 --- a/lib/router.php +++ b/lib/router.php @@ -637,6 +637,7 @@ class Router $m->connect('admin/site', array('action' => 'siteadminpanel')); $m->connect('admin/design', array('action' => 'designadminpanel')); $m->connect('admin/user', array('action' => 'useradminpanel')); + $m->connect('admin/access', array('action' => 'accessadminpanel')); $m->connect('admin/paths', array('action' => 'pathsadminpanel')); $m->connect('getfile/:filename', -- cgit v1.2.3-54-g00ecf From 492950b784ccf76ad96a24732c4329e3b1ca5ac8 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Thu, 14 Jan 2010 23:36:13 +0100 Subject: Fix inconsistent title case in page title --- actions/oauthconnectionssettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actions') diff --git a/actions/oauthconnectionssettings.php b/actions/oauthconnectionssettings.php index b17729b82..c2e8d441b 100644 --- a/actions/oauthconnectionssettings.php +++ b/actions/oauthconnectionssettings.php @@ -68,7 +68,7 @@ class OauthconnectionssettingsAction extends ConnectSettingsAction function title() { - return _('Connected Applications'); + return _('Connected applications'); } function isReadOnly($args) -- cgit v1.2.3-54-g00ecf From 97e1acdc329c3f4dbabce37ff5985f655aa91541 Mon Sep 17 00:00:00 2001 From: Siebrand Mazeland Date: Thu, 14 Jan 2010 23:37:06 +0100 Subject: Fix casing for HMAC-SHA1. --- actions/showapplication.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/showapplication.php b/actions/showapplication.php index 049206375..a6ff425c7 100644 --- a/actions/showapplication.php +++ b/actions/showapplication.php @@ -265,7 +265,7 @@ class ShowApplicationAction extends OwnerDesignAction $this->elementEnd('dl'); $this->element('p', 'note', - _('Note: We support hmac-sha1 signatures. We do not support the plaintext signature method.')); + _('Note: We support HMAC-SHA1 signatures. We do not support the plaintext signature method.')); $this->elementEnd('div'); $this->elementStart('p', array('id' => 'application_action')); @@ -325,4 +325,3 @@ class ShowApplicationAction extends OwnerDesignAction } } - -- cgit v1.2.3-54-g00ecf From 923b7de3c661098cfe3d5bdc0878d9329e1ee2bf Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 27 Jan 2010 08:41:26 +0000 Subject: - Check for read-only vs. read-write access to protected API resources (OAuth) - Some cleanup --- actions/apistatusesupdate.php | 18 +++--- lib/apiauth.php | 124 ++++++++++++++++++++++-------------------- 2 files changed, 74 insertions(+), 68 deletions(-) (limited to 'actions') diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php index 31c9b20ce..bf367e1e1 100644 --- a/actions/apistatusesupdate.php +++ b/actions/apistatusesupdate.php @@ -28,7 +28,7 @@ * @author Mike Cochrane * @author Robin Millette * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-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/ */ @@ -79,7 +79,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction { parent::prepare($args); - $this->user = $this->auth_user; $this->status = $this->trimmed('status'); $this->source = $this->trimmed('source'); $this->lat = $this->trimmed('lat'); @@ -145,7 +144,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction return; } - if (empty($this->user)) { + if (empty($this->auth_user)) { $this->clientError(_('No such user.'), 404, $this->format); return; } @@ -172,7 +171,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction // Check for commands $inter = new CommandInterpreter(); - $cmd = $inter->handle_command($this->user, $status_shortened); + $cmd = $inter->handle_command($this->auth_user, $status_shortened); if ($cmd) { @@ -184,7 +183,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction // And, it returns your last status whether the cmd was successful // or not! - $this->notice = $this->user->getCurrentNotice(); + $this->notice = $this->auth_user->getCurrentNotice(); } else { @@ -211,7 +210,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction $upload = null; try { - $upload = MediaFile::fromUpload('media', $this->user); + $upload = MediaFile::fromUpload('media', $this->auth_user); } catch (ClientException $ce) { $this->clientError($ce->getMessage()); return; @@ -234,19 +233,19 @@ class ApiStatusesUpdateAction extends ApiAuthAction $options = array('reply_to' => $reply_to); - if ($this->user->shareLocation()) { + if ($this->auth_user->shareLocation()) { $locOptions = Notice::locationOptions($this->lat, $this->lon, null, null, - $this->user->getProfile()); + $this->auth_user->getProfile()); $options = array_merge($options, $locOptions); } $this->notice = - Notice::saveNew($this->user->id, + Notice::saveNew($this->auth_user->id, $content, $this->source, $options); @@ -255,7 +254,6 @@ class ApiStatusesUpdateAction extends ApiAuthAction $upload->attachToNotice($this->notice); } - } $this->showNotice(); diff --git a/lib/apiauth.php b/lib/apiauth.php index 37070d212..ad9651ff2 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -29,7 +29,7 @@ * @author mEDI * @author Sarven Capadisli * @author Zach Copley - * @copyright 2009 StatusNet, Inc. + * @copyright 2009-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/ */ @@ -53,9 +53,11 @@ require_once INSTALLDIR . '/lib/apioauth.php'; class ApiAuthAction extends ApiAction { - var $access_token; - var $oauth_access_type; - var $oauth_source; + var $auth_user_nickname = null; + var $auth_user_password = null; + var $access_token = null; + var $oauth_source = null; + var $auth_user = null; /** * Take arguments for running, and output basic auth header if needed @@ -70,18 +72,28 @@ class ApiAuthAction extends ApiAction { parent::prepare($args); - if ($this->requiresAuth()) { + $this->consumer_key = $this->arg('oauth_consumer_key'); + $this->access_token = $this->arg('oauth_token'); - $this->consumer_key = $this->arg('oauth_consumer_key'); - $this->access_token = $this->arg('oauth_token'); + // NOTE: $this->auth_user has to get set in prepare(), not handle(), + // because subclasses do stuff with it in their prepares. + if ($this->requiresAuth()) { if (!empty($this->access_token)) { $this->checkOAuthRequest(); } else { $this->checkBasicAuthUser(); - // By default, all basic auth users have read and write access + } + + // Reject API calls with the wrong access level - $this->access = self::READ_WRITE; + if ($this->isReadOnly($args) == false) { + if ($this->access != self::READ_WRITE) { + $msg = 'API resource requires read-write access, ' . + 'but you only have read access.'; + $this->clientError($msg, 401, $this->format); + exit(); + } } } @@ -95,8 +107,6 @@ class ApiAuthAction extends ApiAction function checkOAuthRequest() { - common_debug("We have an OAuth request."); - $datastore = new ApiStatusNetOAuthDataStore(); $server = new OAuthServer($datastore); $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); @@ -114,9 +124,10 @@ class ApiAuthAction extends ApiAction if (empty($app)) { - // this should really not happen - common_log(LOG_WARN, - "Couldn't find the OAuth app for consumer key: $this->consumer_key"); + // this should probably not happen + common_log(LOG_WARNING, + 'Couldn\'t find the OAuth app for consumer key: ' . + $this->consumer_key); throw new OAuthException('No application for that consumer key.'); } @@ -128,20 +139,18 @@ class ApiAuthAction extends ApiAction $appUser = Oauth_application_user::staticGet('token', $this->access_token); - // XXX: check that app->id and appUser->application_id and consumer all + // XXX: Check that app->id and appUser->application_id and consumer all // match? if (!empty($appUser)) { - // read or read-write - $this->oauth_access_type = $appUser->access_type; - // If access_type == 0 we have either a request token // or a bad / revoked access token - if ($this->oauth_access_type != 0) { + if ($appUser->access_type != 0) { + + // Set the access level for the api call - // Set the read or read-write access for the api call $this->access = ($appUser->access_type & Oauth_application::$writeAccess) ? self::READ_WRITE : self::READ_ONLY; @@ -151,38 +160,34 @@ class ApiAuthAction extends ApiAction } $msg = "API OAuth authentication for user '%s' (id: %d) on behalf of " . - "application '%s' (id: %d)."; + "application '%s' (id: %d) with %s access."; common_log(LOG_INFO, sprintf($msg, $this->auth_user->nickname, $this->auth_user->id, $app->name, - $app->id)); + $app->id, + ($this->access = self::READ_WRITE) ? + 'read-write' : 'read-only' + )); return true; } else { throw new OAuthException('Bad access token.'); } } else { - // also should not happen + // Also should not happen + throw new OAuthException('No user for that token.'); - } + } } catch (OAuthException $e) { - common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); - common_debug(var_export($req, true)); - $this->showOAuthError($e->getMessage()); - exit(); + common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); + $this->showAuthError(); + exit; } } - function showOAuthError($msg) - { - header('HTTP/1.1 401 Unauthorized'); - header('Content-Type: text/html; charset=utf-8'); - print $msg . "\n"; - } - /** * Does this API resource require authentication? * @@ -207,39 +212,44 @@ class ApiAuthAction extends ApiAction $realm = common_config('site', 'name') . ' API'; - if (!isset($this->auth_user)) { + if (!isset($this->auth_user_nickname)) { header('WWW-Authenticate: Basic realm="' . $realm . '"'); // show error if the user clicks 'cancel' - $this->showBasicAuthError(); + $this->showAuthError(); exit; } else { - $nickname = $this->auth_user; - $password = $this->auth_pw; - $user = common_check_user($nickname, $password); + + $user = common_check_user($this->auth_user_nickname, + $this->auth_user_password); + if (Event::handle('StartSetApiUser', array(&$user))) { $this->auth_user = $user; Event::handle('EndSetApiUser', array($user)); } + // By default, basic auth users have rw access + + $this->access = self::READ_WRITE; + if (empty($this->auth_user)) { // basic authentication failed list($proxy, $ip) = common_client_ip(); + common_log( LOG_WARNING, 'Failed API auth attempt, nickname = ' . "$nickname, proxy = $proxy, ip = $ip." ); - $this->showBasicAuthError(); + + $this->showAuthError(); exit; } } - - return true; } /** @@ -253,32 +263,30 @@ class ApiAuthAction extends ApiAction { if (isset($_SERVER['AUTHORIZATION']) || isset($_SERVER['HTTP_AUTHORIZATION']) - ) { - $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION']) - ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION']; + ) { + $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION']) + ? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION']; } if (isset($_SERVER['PHP_AUTH_USER'])) { - $this->auth_user = $_SERVER['PHP_AUTH_USER']; - $this->auth_pw = $_SERVER['PHP_AUTH_PW']; + $this->auth_user_nickname = $_SERVER['PHP_AUTH_USER']; + $this->auth_user_password = $_SERVER['PHP_AUTH_PW']; } elseif (isset($authorization_header) && strstr(substr($authorization_header, 0, 5), 'Basic')) { - // decode the HTTP_AUTHORIZATION header on php-cgi server self + // Decode the HTTP_AUTHORIZATION header on php-cgi server self // on fcgid server the header name is AUTHORIZATION $auth_hash = base64_decode(substr($authorization_header, 6)); - list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash); + list($this->auth_user_nickname, + $this->auth_user_password) = explode(':', $auth_hash); - // set all to null on a empty basic auth request + // Set all to null on a empty basic auth request - if ($this->auth_user == "") { - $this->auth_user = null; - $this->auth_pw = null; + if (empty($this->auth_user_nickname)) { + $this->auth_user_nickname = null; + $this->auth_password = null; } - } else { - $this->auth_user = null; - $this->auth_pw = null; } } @@ -289,7 +297,7 @@ class ApiAuthAction extends ApiAction * @return void */ - function showBasicAuthError() + function showAuthError() { header('HTTP/1.1 401 Unauthorized'); $msg = 'Could not authenticate you.'; -- cgit v1.2.3-54-g00ecf From 756da7bc5174f58f714c858f0100f04b3561a250 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 27 Jan 2010 08:45:56 +0000 Subject: s/LOG_WARN/LOG_WARNING/ --- actions/apioauthaccesstoken.php | 2 +- actions/apioauthrequesttoken.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'actions') diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php index 085ef6f0b..887df4c20 100644 --- a/actions/apioauthaccesstoken.php +++ b/actions/apioauthaccesstoken.php @@ -70,7 +70,7 @@ class ApiOauthAccessTokenAction extends ApiOauthAction $atok = $server->fetch_access_token($req); } catch (OAuthException $e) { - common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); + common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); common_debug(var_export($req, true)); $this->outputError($e->getMessage()); return; diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php index 467640b9a..4fa626d86 100644 --- a/actions/apioauthrequesttoken.php +++ b/actions/apioauthrequesttoken.php @@ -89,7 +89,7 @@ class ApiOauthRequestTokenAction extends ApiOauthAction $token = $server->fetch_request_token($req); print $token; } catch (OAuthException $e) { - common_log(LOG_WARN, 'API OAuthException - ' . $e->getMessage()); + common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage()); header('HTTP/1.1 401 Unauthorized'); header('Content-Type: text/html; charset=utf-8'); print $e->getMessage() . "\n"; -- cgit v1.2.3-54-g00ecf From c52951cef5508452733f84dec7815daf4aca1016 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 27 Jan 2010 11:37:22 -0500 Subject: Optionally set a separate Javascript server and path We have about 10-12 JavaScript pages per Web page. They usually are based on the same server as the Web pages, but since they're static files, it makes sense to offload them to a lite server that handles static files well. This commit lets you set a separate Javascript server and path for the default Javascript code in StatusNet. Squashed commit of the following: commit 139d1622fdafe5ad00c820224416d9021efc3234 Author: Evan Prodromou Date: Wed Jan 27 11:30:24 2010 -0500 modules that call htmloutputter::script() don't prescribe js/ path commit c6ca3174af73efed55eaed5ff1e2a3bdc77d2d87 Author: Evan Prodromou Date: Wed Jan 27 11:28:07 2010 -0500 configurable server and path for javascript files --- actions/avatarsettings.php | 4 ++-- actions/designadminpanel.php | 4 ++-- actions/grouplogo.php | 4 ++-- lib/action.php | 16 ++++++++-------- lib/default.php | 3 +++ lib/designsettings.php | 4 ++-- lib/htmloutputter.php | 28 +++++++++++++++++++++++++++- plugins/Facebook/facebookaction.php | 2 +- 8 files changed, 47 insertions(+), 18 deletions(-) (limited to 'actions') diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php index cf4525552..6a7398746 100644 --- a/actions/avatarsettings.php +++ b/actions/avatarsettings.php @@ -416,8 +416,8 @@ class AvatarsettingsAction extends AccountSettingsAction parent::showScripts(); if ($this->mode == 'crop') { - $this->script('js/jcrop/jquery.Jcrop.min.js'); - $this->script('js/jcrop/jquery.Jcrop.go.js'); + $this->script('jcrop/jquery.Jcrop.min.js'); + $this->script('jcrop/jquery.Jcrop.go.js'); } $this->autofocus('avatarfile'); diff --git a/actions/designadminpanel.php b/actions/designadminpanel.php index 72ad6ade2..30e8bde1a 100644 --- a/actions/designadminpanel.php +++ b/actions/designadminpanel.php @@ -302,8 +302,8 @@ class DesignadminpanelAction extends AdminPanelAction { parent::showScripts(); - $this->script('js/farbtastic/farbtastic.js'); - $this->script('js/userdesign.go.js'); + $this->script('farbtastic/farbtastic.js'); + $this->script('userdesign.go.js'); $this->autofocus('design_background-image_file'); } diff --git a/actions/grouplogo.php b/actions/grouplogo.php index f197aef33..3c9b56296 100644 --- a/actions/grouplogo.php +++ b/actions/grouplogo.php @@ -437,8 +437,8 @@ class GrouplogoAction extends GroupDesignAction parent::showScripts(); if ($this->mode == 'crop') { - $this->script('js/jcrop/jquery.Jcrop.min.js'); - $this->script('js/jcrop/jquery.Jcrop.go.js'); + $this->script('jcrop/jquery.Jcrop.min.js'); + $this->script('jcrop/jquery.Jcrop.go.js'); } $this->autofocus('avatarfile'); diff --git a/lib/action.php b/lib/action.php index 3ffc452de..cc4f4aad0 100644 --- a/lib/action.php +++ b/lib/action.php @@ -246,18 +246,18 @@ class Action extends HTMLOutputter // lawsuit { if (Event::handle('StartShowScripts', array($this))) { if (Event::handle('StartShowJQueryScripts', array($this))) { - $this->script('js/jquery.min.js'); - $this->script('js/jquery.form.js'); - $this->script('js/jquery.cookie.js'); - $this->script('js/json2.js'); - $this->script('js/jquery.joverlay.min.js'); + $this->script('jquery.min.js'); + $this->script('jquery.form.js'); + $this->script('jquery.cookie.js'); + $this->script('json2.js'); + $this->script('jquery.joverlay.min.js'); Event::handle('EndShowJQueryScripts', array($this)); } if (Event::handle('StartShowStatusNetScripts', array($this)) && Event::handle('StartShowLaconicaScripts', array($this))) { - $this->script('js/xbImportNode.js'); - $this->script('js/util.js'); - $this->script('js/geometa.js'); + $this->script('xbImportNode.js'); + $this->script('util.js'); + $this->script('geometa.js'); // Frame-busting code to avoid clickjacking attacks. $this->element('script', array('type' => 'text/javascript'), 'if (window.top !== window.self) { window.top.location.href = window.self.location.href; }'); diff --git a/lib/default.php b/lib/default.php index 37fb65b53..10ea34864 100644 --- a/lib/default.php +++ b/lib/default.php @@ -120,6 +120,9 @@ $default = array('server' => null, 'dir' => null, 'path'=> null), + 'javascript' => + array('server' => null, + 'path'=> null), 'throttle' => array('enabled' => false, // whether to throttle edits; false by default 'count' => 20, // number of allowed messages in timespan diff --git a/lib/designsettings.php b/lib/designsettings.php index 8e44c03a9..4955e9219 100644 --- a/lib/designsettings.php +++ b/lib/designsettings.php @@ -327,8 +327,8 @@ class DesignSettingsAction extends AccountSettingsAction { parent::showScripts(); - $this->script('js/farbtastic/farbtastic.js'); - $this->script('js/userdesign.go.js'); + $this->script('farbtastic/farbtastic.js'); + $this->script('userdesign.go.js'); $this->autofocus('design_background-image_file'); } diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php index 31660ce95..317f5ea61 100644 --- a/lib/htmloutputter.php +++ b/lib/htmloutputter.php @@ -351,14 +351,40 @@ class HTMLOutputter extends XMLOutputter function script($src, $type='text/javascript') { if(Event::handle('StartScriptElement', array($this,&$src,&$type))) { + $url = parse_url($src); + if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) { - $src = common_path($src) . '?version=' . STATUSNET_VERSION; + $path = common_config('javascript', 'path'); + + if (empty($path)) { + $path = common_config('site', 'path') . '/js/'; + } + + if ($path[strlen($path)-1] != '/') { + $path .= '/'; + } + + if ($path[0] != '/') { + $path = '/'.$path; + } + + $server = common_config('javascript', 'server'); + + if (empty($server)) { + $server = common_config('site', 'server'); + } + + // XXX: protocol + + $src = 'http://'.$server.$path.$src . '?version=' . STATUSNET_VERSION; } + $this->element('script', array('type' => $type, 'src' => $src), ' '); + Event::handle('EndScriptElement', array($this,$src,$type)); } } diff --git a/plugins/Facebook/facebookaction.php b/plugins/Facebook/facebookaction.php index 815fee094..389e1ea81 100644 --- a/plugins/Facebook/facebookaction.php +++ b/plugins/Facebook/facebookaction.php @@ -89,7 +89,7 @@ class FacebookAction extends Action function showScripts() { - $this->script('js/facebookapp.js'); + $this->script('facebookapp.js'); } /** -- cgit v1.2.3-54-g00ecf