client side of distributed subscription almost complete
darcs-hash:20080527114219-84dde-784ddf4d4650c17bc7a1e3e01219c6948dfc9b3d.gz
This commit is contained in:
parent
47f030ef65
commit
90b4873a00
|
@ -22,6 +22,13 @@ if (!defined('LACONICA')) { exit(1); }
|
||||||
class AccesstokenAction extends Action {
|
class AccesstokenAction extends Action {
|
||||||
function handle($args) {
|
function handle($args) {
|
||||||
parent::handle($args);
|
parent::handle($args);
|
||||||
common_server_error(_t('Not yet implemented.'));
|
try {
|
||||||
|
$req = OAuthRequest::from_request();
|
||||||
|
$server = common_oauth_server();
|
||||||
|
$token = $server->fetch_access_token($req);
|
||||||
|
print $token;
|
||||||
|
} catch (OAuthException $e) {
|
||||||
|
common_server_error($e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
221
actions/finishremotesubscribe.php
Normal file
221
actions/finishremotesubscribe.php
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Laconica - a distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2008, Controlez-Vous, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
|
require_once(INSTALLDIR.'/lib/omb.php');
|
||||||
|
require_once('Auth/Yadis/Yadis.php');
|
||||||
|
|
||||||
|
class FinishremotesubscribeAction extends Action {
|
||||||
|
|
||||||
|
function handle($args) {
|
||||||
|
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
if (common_logged_in()) {
|
||||||
|
common_user_error(_t('You can use the local subscription!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nonce = $this->trimmed('nonce');
|
||||||
|
|
||||||
|
if (!$omb) {
|
||||||
|
common_user_error(_t('No nonce returned!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$omb = $_SESSION[$nonce];
|
||||||
|
|
||||||
|
if (!$omb) {
|
||||||
|
common_user_error(_t('Not expecting this response!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$req = OAuthRequest::from_request();
|
||||||
|
|
||||||
|
$token = $req->get_parameter('oauth_token');
|
||||||
|
|
||||||
|
# I think this is the success metric
|
||||||
|
|
||||||
|
if ($token != $omb['token']) {
|
||||||
|
common_user_error(_t('Not authorized.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$version = $req->get_parameter('omb_version');
|
||||||
|
|
||||||
|
if ($version != OMB_VERSION_01) {
|
||||||
|
common_user_error(_t('Unknown version of OMB protocol.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nickname = $req->get_parameter('omb_listener_nickname');
|
||||||
|
|
||||||
|
if (!$nickname) {
|
||||||
|
common_user_error(_t('No nickname provided by remote server.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile_url = $req->get_parameter('omb_listener_profile');
|
||||||
|
|
||||||
|
if (!$profile_url) {
|
||||||
|
common_user_error(_t('No profile URL returned by server.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Validate::uri($profile_url, array('allowed_schemes' => array('http', 'https')))) {
|
||||||
|
common_user_error(_t('Invalid profile URL returned by server.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = User::staticGet('uri', $omb['listenee']);
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
common_user_error(_t('User being listened to doesn\'t exist.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fullname = $req->get_parameter('omb_listener_fullname');
|
||||||
|
$homepage = $req->get_parameter('omb_listener_homepage');
|
||||||
|
$bio = $req->get_parameter('omb_listener_bio');
|
||||||
|
$location = $req->get_parameter('omb_listener_location');
|
||||||
|
$avatar_url = $req->get_parameter('omb_listener_avatar');
|
||||||
|
|
||||||
|
list($newtok, $newsecret) = $this->access_token($omb);
|
||||||
|
|
||||||
|
if (!$newtok || !$newsecret) {
|
||||||
|
common_user_error(_t('Couldn\'t convert request tokens to access tokens.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# XXX: possible attack point; subscribe and return someone else's profile URI
|
||||||
|
|
||||||
|
$remote = Remote_profile::staticGet('uri', $omb['listener']);
|
||||||
|
|
||||||
|
if ($remote) {
|
||||||
|
$exists = true;
|
||||||
|
$profile = Profile::staticGet($remote->id);
|
||||||
|
$orig_remote = clone($remote);
|
||||||
|
$orig_profile = clone($profile);
|
||||||
|
# XXX: compare current postNotice and updateProfile URLs to the ones
|
||||||
|
# stored in the DB to avoid (possibly...) above attack
|
||||||
|
} else {
|
||||||
|
$exists = false;
|
||||||
|
$remote = new Remote_profile();
|
||||||
|
$remote->uri = $omb['listener'];
|
||||||
|
$profile = new Profile();
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile->nickname = $nickname;
|
||||||
|
$profile->profileurl = $profile_url;
|
||||||
|
|
||||||
|
if ($fullname) {
|
||||||
|
$profile->fullname = $fullname;
|
||||||
|
}
|
||||||
|
if ($homepage) {
|
||||||
|
$profile->homepage = $homepage;
|
||||||
|
}
|
||||||
|
if ($bio) {
|
||||||
|
$profile->bio = $bio;
|
||||||
|
}
|
||||||
|
if ($location) {
|
||||||
|
$profile->location = $location;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($exists) {
|
||||||
|
$profile->update($orig_profile);
|
||||||
|
} else {
|
||||||
|
$profile->created = DB_DataObject_Cast::dateTime(); # current time
|
||||||
|
$id = $profile->insert();
|
||||||
|
$remote->id = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($avatar_url) {
|
||||||
|
$this->add_avatar($avatar_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
$remote->postnoticeurl = $omb[OMB_ENDPOINT_POSTNOTICE];
|
||||||
|
$remote->updateprofileurl = $omb[OMB_ENDPOINT_UPDATEPROFILE];
|
||||||
|
|
||||||
|
if ($exists) {
|
||||||
|
$remote->update($orig_remote);
|
||||||
|
} else {
|
||||||
|
$remote->created = DB_DataObject_Cast::dateTime(); # current time
|
||||||
|
$remote->insert;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sub = new Subscription();
|
||||||
|
$sub->subscriber = $remote->id;
|
||||||
|
$sub->subscribed = $user->id;
|
||||||
|
$sub->token = $newtok;
|
||||||
|
$sub->secret = $newsecret;
|
||||||
|
$sub->created = DB_DataObject_Cast::dateTime(); # current time
|
||||||
|
|
||||||
|
if (!$sub->insert()) {
|
||||||
|
common_user_error(_t('Couldn\'t insert new subscription.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clear the data
|
||||||
|
unset($_SESSION[$nonce]);
|
||||||
|
|
||||||
|
# If we show subscriptions in reverse chron order, this should
|
||||||
|
# show up close to the top of the page
|
||||||
|
|
||||||
|
common_redirect(common_local_url('subscribed', array('nickname' =>
|
||||||
|
$user->nickname)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function access_token($omb) {
|
||||||
|
|
||||||
|
$con = omb_oauth_consumer();
|
||||||
|
$tok = new OAuthToken($omb['token'], $omb['secret']);
|
||||||
|
|
||||||
|
$url = $omb[OAUTH_ENDPOINT_ACCESS][0];
|
||||||
|
|
||||||
|
# XXX: Is this the right thing to do? Strip off GET params and make them
|
||||||
|
# POST params? Seems wrong to me.
|
||||||
|
|
||||||
|
$parsed = parse_url($url);
|
||||||
|
$params = array();
|
||||||
|
parse_str($parsed['query'], $params);
|
||||||
|
|
||||||
|
$req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params);
|
||||||
|
|
||||||
|
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||||
|
|
||||||
|
# XXX: test to see if endpoint accepts this signature method
|
||||||
|
|
||||||
|
$req->sign_request(omb_hmac_sha1(), $con, NULL);
|
||||||
|
|
||||||
|
# We re-use this tool's fetcher, since it's pretty good
|
||||||
|
|
||||||
|
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||||
|
$result = $fetcher->post($req->get_normalized_http_url(),
|
||||||
|
$req->to_postdata());
|
||||||
|
|
||||||
|
if ($result->status != 200) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_str($result->body, $return);
|
||||||
|
|
||||||
|
return array($return['oauth_token'], $return['oauth_token_secret']);
|
||||||
|
}
|
||||||
|
}
|
248
actions/remotesubscribe.php
Normal file
248
actions/remotesubscribe.php
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Laconica - a distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2008, Controlez-Vous, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
|
require_once(INSTALLDIR.'/lib/omb.php');
|
||||||
|
require_once('Auth/Yadis/Yadis.php');
|
||||||
|
|
||||||
|
class RemotesubscribeAction extends Action {
|
||||||
|
|
||||||
|
function handle($args) {
|
||||||
|
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
if (common_logged_in()) {
|
||||||
|
common_user_error(_t('You can use the local subscription!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
$this->remote_subscription();
|
||||||
|
} else {
|
||||||
|
$this->show_form();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_form($err=NULL) {
|
||||||
|
common_show_header(_t('Remote subscribe'));
|
||||||
|
if ($err) {
|
||||||
|
common_element('div', 'error', $err);
|
||||||
|
}
|
||||||
|
common_element_start('form', array('id' => 'remotesubscribe', 'method' => 'POST',
|
||||||
|
'action' => common_local_url('remotesubscribe')));
|
||||||
|
common_input('profile', _t('Profile URL'));
|
||||||
|
common_submit('submit', _t('Subscribe'));
|
||||||
|
common_element_end('form');
|
||||||
|
}
|
||||||
|
|
||||||
|
function remote_subscription() {
|
||||||
|
$user = $this->get_user();
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
$this->show_form(_t('No such user!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile = $this->trimmed('profile');
|
||||||
|
|
||||||
|
if (!$profile) {
|
||||||
|
$this->show_form(_t('No such user!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Validate::uri($profile, array('allowed_schemes' => array('http', 'https')))) {
|
||||||
|
$this->show_form(_t('Invalid profile URL (bad format)'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||||
|
$yadis = Auth_Yadis_Yadis::discover($profile, $fetcher);
|
||||||
|
|
||||||
|
if (!$yadis) {
|
||||||
|
$this->show_form(_t('Not a valid profile URL (no YADIS document).'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$omb = $this->getOmb($yadis);
|
||||||
|
|
||||||
|
if (!$omb) {
|
||||||
|
$this->show_form(_t('Not a valid profile URL (incorrect services).'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list($token, $secret) = $this->request_token($omb);
|
||||||
|
|
||||||
|
if (!$token || !$secret) {
|
||||||
|
$this->show_form(_t('Couldn\'t get a request token.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->request_authorization($user, $omb, $token, $secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_user() {
|
||||||
|
$user = NULL;
|
||||||
|
$nickname = $this->trimmed('nickname');
|
||||||
|
if ($nickname) {
|
||||||
|
$user = User::staticGet('nickname', $nickname);
|
||||||
|
}
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOmb($yadis) {
|
||||||
|
static $endpoints = array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE,
|
||||||
|
OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE,
|
||||||
|
OAUTH_ENDPOINT_ACCESS);
|
||||||
|
$omb = array();
|
||||||
|
$services = $yadis->services(); # ordered by priority
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$types = $service->matchTypes($endpoints);
|
||||||
|
foreach ($types as $type) {
|
||||||
|
# We take the first one, since it's the highest priority
|
||||||
|
if (!array_key_exists($type, $omb)) {
|
||||||
|
# URIs is an array, priority-ordered
|
||||||
|
$omb[$type] = $service->getURIs();
|
||||||
|
# Special handling for request
|
||||||
|
if ($type == OAUTH_ENDPOINT_REQUEST) {
|
||||||
|
$nodes = $service->getElements('LocalID');
|
||||||
|
if (!$nodes) {
|
||||||
|
# error
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
$omb['listener'] = $service->parser->content($nodes[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ($endpoints as $ep) {
|
||||||
|
if (!array_key_exists($ep, $omb)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!array_key_exists('listener', $omb)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return $omb;
|
||||||
|
}
|
||||||
|
|
||||||
|
function request_token($omb) {
|
||||||
|
$con = omb_oauth_consumer();
|
||||||
|
|
||||||
|
$url = $omb[OAUTH_ENDPOINT_REQUEST][0];
|
||||||
|
|
||||||
|
# XXX: Is this the right thing to do? Strip off GET params and make them
|
||||||
|
# POST params? Seems wrong to me.
|
||||||
|
|
||||||
|
$parsed = parse_url($url);
|
||||||
|
$params = array();
|
||||||
|
parse_str($parsed['query'], $params);
|
||||||
|
|
||||||
|
$req = OAuthRequest::from_consumer_and_token($con, NULL, "POST", $url, $params);
|
||||||
|
|
||||||
|
$req->set_parameter('omb_listener', $omb['listener']);
|
||||||
|
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||||
|
|
||||||
|
# XXX: test to see if endpoint accepts this signature method
|
||||||
|
|
||||||
|
$req->sign_request(omb_hmac_sha1(), $con, NULL);
|
||||||
|
|
||||||
|
# We re-use this tool's fetcher, since it's pretty good
|
||||||
|
|
||||||
|
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||||
|
$result = $fetcher->post($req->get_normalized_http_url(),
|
||||||
|
$req->to_postdata());
|
||||||
|
|
||||||
|
if ($result->status != 200) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_str($result->body, $return);
|
||||||
|
|
||||||
|
return array($return['oauth_token'], $return['oauth_token_secret']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function request_authorization($user, $omb, $token, $secret) {
|
||||||
|
global $config; # for license URL
|
||||||
|
|
||||||
|
$con = omb_oauth_consumer();
|
||||||
|
$tok = new OAuthToken($token, $secret);
|
||||||
|
|
||||||
|
$url = $omb[OAUTH_ENDPOINT_AUTHORIZE][0];
|
||||||
|
|
||||||
|
# XXX: Is this the right thing to do? Strip off GET params and make them
|
||||||
|
# POST params? Seems wrong to me.
|
||||||
|
|
||||||
|
$parsed = parse_url($url);
|
||||||
|
$params = array();
|
||||||
|
parse_str($parsed['query'], $params);
|
||||||
|
|
||||||
|
$req = OAuthRequest::from_consumer_and_token($con, $tok, 'GET', $url, $params);
|
||||||
|
|
||||||
|
# We send over a ton of information. This lets the other
|
||||||
|
# server store info about our user, and it lets the current
|
||||||
|
# user decide if they really want to authorize the subscription.
|
||||||
|
|
||||||
|
$req->set_parameter('omb_version', OMB_VERSION_01);
|
||||||
|
$req->set_parameter('omb_listener', $omb['listener']);
|
||||||
|
$req->set_parameter('omb_listenee', $user->uri);
|
||||||
|
$req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname));
|
||||||
|
$req->set_parameter('omb_listenee_nickname', $user->nickname);
|
||||||
|
$req->set_parameter('omb_listenee_license', $config['license']['url']);
|
||||||
|
$profile = $user->getProfile();
|
||||||
|
if ($profile->fullname) {
|
||||||
|
$req->set_parameter('omb_listenee_fullname', $profile->fullname);
|
||||||
|
}
|
||||||
|
if ($profile->homepage) {
|
||||||
|
$req->set_parameter('omb_listenee_homepage', $profile->homepage);
|
||||||
|
}
|
||||||
|
if ($profile->bio) {
|
||||||
|
$req->set_parameter('omb_listenee_bio', $profile->bio);
|
||||||
|
}
|
||||||
|
if ($profile->location) {
|
||||||
|
$req->set_parameter('omb_listenee_location', $profile->location);
|
||||||
|
}
|
||||||
|
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
|
||||||
|
if ($avatar) {
|
||||||
|
$req->set_parameter('omb_listenee_avatar', $avatar->url);
|
||||||
|
}
|
||||||
|
|
||||||
|
$nonce = $this->make_nonce();
|
||||||
|
|
||||||
|
$req->set_parameter('oauth_callback', common_local_url('finishremotesubscribe',
|
||||||
|
array('nonce' => $nonce)));
|
||||||
|
|
||||||
|
# XXX: test to see if endpoint accepts this signature method
|
||||||
|
|
||||||
|
$req->sign_request(omb_hmac_sha1(), $con, $tok);
|
||||||
|
|
||||||
|
# store all our info here
|
||||||
|
|
||||||
|
$omb['listenee'] = $user->nickname;
|
||||||
|
$omb['token'] = $token;
|
||||||
|
$omb['secret'] = $secret;
|
||||||
|
|
||||||
|
$_SESSION[$nonce] = $omb;
|
||||||
|
|
||||||
|
# Redirect to authorization service
|
||||||
|
|
||||||
|
common_redirect($req->to_url());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,9 +19,18 @@
|
||||||
|
|
||||||
if (!defined('LACONICA')) { exit(1); }
|
if (!defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
|
require_once(INSTALLDIR.'/lib/omb.php');
|
||||||
|
|
||||||
class RequesttokenAction extends Action {
|
class RequesttokenAction extends Action {
|
||||||
function handle($args) {
|
function handle($args) {
|
||||||
parent::handle($args);
|
parent::handle($args);
|
||||||
common_server_error(_t('Not yet implemented.'));
|
try {
|
||||||
|
$req = OAuthRequest::from_request();
|
||||||
|
$server = common_oauth_server();
|
||||||
|
$token = $server->fetch_request_token($req);
|
||||||
|
print $token;
|
||||||
|
} catch (OAuthException $e) {
|
||||||
|
common_server_error($e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,10 @@ class ShowstreamAction extends StreamAction {
|
||||||
$user->nickname)),
|
$user->nickname)),
|
||||||
'type' => 'application/rss+xml',
|
'type' => 'application/rss+xml',
|
||||||
'title' => _t('Notice feed for ') . $user->nickname));
|
'title' => _t('Notice feed for ') . $user->nickname));
|
||||||
|
# for remote subscriptions etc.
|
||||||
|
common_element('meta', array('http-equiv' => 'X-XRDS-Location',
|
||||||
|
'content' => common_local_url('xrds', array('nickname' =>
|
||||||
|
$user->nickname))));
|
||||||
}
|
}
|
||||||
|
|
||||||
function no_such_user() {
|
function no_such_user() {
|
||||||
|
|
|
@ -22,6 +22,60 @@ if (!defined('LACONICA')) { exit(1); }
|
||||||
class UserauthorizationAction extends Action {
|
class UserauthorizationAction extends Action {
|
||||||
function handle($args) {
|
function handle($args) {
|
||||||
parent::handle($args);
|
parent::handle($args);
|
||||||
common_server_error(_t('Not yet implemented.'));
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
$this->send_authorization();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$req = $this->get_request();
|
||||||
|
$server = common_oauth_server();
|
||||||
|
list($consumer, $token) = $server->verify_request($req);
|
||||||
|
} catch (OAuthException $e) {
|
||||||
|
$this->clear_request();
|
||||||
|
common_server_error($e->getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (common_logged_in()) {
|
||||||
|
$this->show_form($req);
|
||||||
|
} else {
|
||||||
|
common_return_to(common_local_url('userauthorization'));
|
||||||
|
common_redirect(common_local_url('login'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function store_request($req) {
|
||||||
|
common_ensure_session();
|
||||||
|
$_SESSION['userauthorizationrequest'] = $req;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_request() {
|
||||||
|
common_ensure_session();
|
||||||
|
$req = $_SESSION['userauthorizationrequest'];
|
||||||
|
if (!$req) {
|
||||||
|
# XXX: may have an uncaught exception
|
||||||
|
$req = OAuthRequest::from_request();
|
||||||
|
$this->store_request($req);
|
||||||
|
}
|
||||||
|
return $req;
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_form($req) {
|
||||||
|
common_show_header(_t('Authorize subscription'));
|
||||||
|
|
||||||
|
common_show_footer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_authorization() {
|
||||||
|
$req = $this->get_request();
|
||||||
|
if (!$req) {
|
||||||
|
common_user_error(_t('No authorization request!'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->boolean('authorize')) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,9 @@ class XrdsAction extends Action {
|
||||||
|
|
||||||
function show_service($type, $uri, $params=NULL, $sigs=NULL, $localId=NULL) {
|
function show_service($type, $uri, $params=NULL, $sigs=NULL, $localId=NULL) {
|
||||||
common_element_start('Service');
|
common_element_start('Service');
|
||||||
common_element('URI', NULL, $uri);
|
if ($uri) {
|
||||||
|
common_element('URI', NULL, $uri);
|
||||||
|
}
|
||||||
common_element('Type', NULL, $type);
|
common_element('Type', NULL, $type);
|
||||||
if ($params) {
|
if ($params) {
|
||||||
foreach ($params as $param) {
|
foreach ($params as $param) {
|
||||||
|
|
|
@ -56,6 +56,7 @@ create table subscription (
|
||||||
subscriber integer not null comment 'profile listening',
|
subscriber integer not null comment 'profile listening',
|
||||||
subscribed integer not null comment 'profile being listened to',
|
subscribed integer not null comment 'profile being listened to',
|
||||||
token varchar(255) comment 'authorization token',
|
token varchar(255) comment 'authorization token',
|
||||||
|
secret varchar(255) comment 'token secret',
|
||||||
created datetime not null comment 'date this record was created',
|
created datetime not null comment 'date this record was created',
|
||||||
modified timestamp comment 'date this record was modified',
|
modified timestamp comment 'date this record was modified',
|
||||||
|
|
||||||
|
|
|
@ -9,5 +9,6 @@ This package requires PHP 5.x and the following PHP Pear libraries:
|
||||||
use the openidenabled.com libraries for OpenID auth sometime in the
|
use the openidenabled.com libraries for OpenID auth sometime in the
|
||||||
future. Note that this is no longer distributed separately; it's only
|
future. Note that this is no longer distributed separately; it's only
|
||||||
in the openidenabled.com OpenID PHP tarball.
|
in the openidenabled.com OpenID PHP tarball.
|
||||||
|
- OAuth.php from http://oauth.googlecode.com/svn/code/php/
|
||||||
|
|
||||||
|
|
||||||
|
|
13
doc/TODO
13
doc/TODO
|
@ -53,12 +53,18 @@
|
||||||
+ public stream link in top menu
|
+ public stream link in top menu
|
||||||
+ dump, fix, undump database
|
+ dump, fix, undump database
|
||||||
+ release 0.2
|
+ release 0.2
|
||||||
- YADIS document link on showstream
|
+ YADIS document link on showstream
|
||||||
- YADIS document
|
+ YADIS document
|
||||||
- subscribe remote
|
- subscribe remote
|
||||||
- add subscriber remote
|
- add subscriber remote
|
||||||
- send remote notice
|
- server side of user authorization
|
||||||
|
- server side of request token
|
||||||
|
- server side of access token
|
||||||
|
- OAuth store
|
||||||
|
- log of consumers who ask for access
|
||||||
- receive remote notice
|
- receive remote notice
|
||||||
|
- send remote notice
|
||||||
|
- subscribe form for not-logged-in users on showstream
|
||||||
- pretty URLs
|
- pretty URLs
|
||||||
- doc action
|
- doc action
|
||||||
- about doc
|
- about doc
|
||||||
|
@ -76,6 +82,7 @@
|
||||||
- add a next page link to public
|
- add a next page link to public
|
||||||
- add a next page link to all
|
- add a next page link to all
|
||||||
- AGPL notification
|
- AGPL notification
|
||||||
|
- Check licenses of all libraries for compatibility
|
||||||
- gettext
|
- gettext
|
||||||
- release 0.3
|
- release 0.3
|
||||||
- license per notice
|
- license per notice
|
||||||
|
|
|
@ -60,7 +60,7 @@ notice URI
|
||||||
Initiation
|
Initiation
|
||||||
==========
|
==========
|
||||||
|
|
||||||
The user submits their profile URL [*] to the remote service somehow --
|
The user submits their profile URL [*]_ to the remote service somehow --
|
||||||
for example, with an HTML form on the remote service's Web site.
|
for example, with an HTML form on the remote service's Web site.
|
||||||
|
|
||||||
.. [*] For OAuth Discovery, this is the "protected resource". It may
|
.. [*] For OAuth Discovery, this is the "protected resource". It may
|
||||||
|
@ -96,11 +96,12 @@ Authorization
|
||||||
The remote service must go through the OAuth 1.0 dance to get
|
The remote service must go through the OAuth 1.0 dance to get
|
||||||
authorization to post notices and update profiles.
|
authorization to post notices and update profiles.
|
||||||
|
|
||||||
In all OAuth, the consumer key should be blank (''), unless the remote
|
In all OAuth, the consumer key should be the root URL for the
|
||||||
server and local service have negotiated another key. Such negotiation
|
microblogging service, if available. The secret should be the blank
|
||||||
is out-of-scope for this document, and we assume an "open" network of
|
string (''), unless the remote server and local service have negotiated
|
||||||
microblogging services. But if you want to have that kind of network,
|
another key. Such negotiation is out-of-scope for this document, and we
|
||||||
do it with this key.
|
assume an "open" network of microblogging services. But if you want to
|
||||||
|
have that kind of network, do it with this key.
|
||||||
|
|
||||||
The remote service MUST do OAuth for every new listener, regardless of
|
The remote service MUST do OAuth for every new listener, regardless of
|
||||||
whether they've already received authorization for posting to the
|
whether they've already received authorization for posting to the
|
||||||
|
@ -253,17 +254,17 @@ The local service makes no guarantees about the delivery of the notice
|
||||||
to anyone.
|
to anyone.
|
||||||
|
|
||||||
The remote service SHOULD NOT send a message with the same notice URL
|
The remote service SHOULD NOT send a message with the same notice URL
|
||||||
to the same postNotice URL more than once. [2]_ If the request returns
|
to the same postNotice URL more than once. [*]_ If the request returns
|
||||||
a 403 Unauthorized message, the remote service SHOULD NOT post
|
a 403 Unauthorized message, the remote service SHOULD NOT post
|
||||||
messages to the same URL again with the same listenee, until another
|
messages to the same URL again with the same listenee, until another
|
||||||
listener has gone through the OAuth dance. [3]_
|
listener has gone through the OAuth dance. [*]_
|
||||||
|
|
||||||
.. [2] A half-assed optimization. A local service may have a lot of
|
.. [*] A half-assed optimization. A local service may have a lot of
|
||||||
listeners listening to the same listenee. It would be pointless to
|
listeners listening to the same listenee. It would be pointless to
|
||||||
have the remote service post the same notice 100 times to the same
|
have the remote service post the same notice 100 times to the same
|
||||||
service. However, if the local service wants fine-grained control,
|
service. However, if the local service wants fine-grained control,
|
||||||
it can have a different postNotice URL for each listener.
|
it can have a different postNotice URL for each listener.
|
||||||
.. [3] If there's one postNotice URL per listener, the 403 message
|
.. [*] If there's one postNotice URL per listener, the 403 message
|
||||||
means the listener has told the local service not to allow posting
|
means the listener has told the local service not to allow posting
|
||||||
any more ("unsubscribed"). If there's one postNotice URL per local
|
any more ("unsubscribed"). If there's one postNotice URL per local
|
||||||
service, it means that the count of listeners has dropped to 0.
|
service, it means that the count of listeners has dropped to 0.
|
||||||
|
|
19
lib/omb.php
19
lib/omb.php
|
@ -19,11 +19,15 @@
|
||||||
|
|
||||||
if (!defined('LACONICA')) { exit(1); }
|
if (!defined('LACONICA')) { exit(1); }
|
||||||
|
|
||||||
|
require_once('OAuth.php');
|
||||||
|
|
||||||
define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
|
define('OAUTH_NAMESPACE', 'http://oauth.net/core/1.0/');
|
||||||
define('OMB_NAMESPACE', 'http://openmicroblogging.org/protocol/0.1');
|
define('OMB_NAMESPACE', 'http://openmicroblogging.org/protocol/0.1');
|
||||||
|
define('OMB_VERSION_01', 'http://openmicroblogging.org/protocol/0.1');
|
||||||
define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
|
define('OAUTH_DISCOVERY', 'http://oauth.net/discovery/1.0');
|
||||||
|
|
||||||
define('OMB_ENDPOINT_UPDATEPROFILE', OMB_NAMESPACE.'updateProfile');
|
define('OMB_ENDPOINT_UPDATEPROFILE', OMB_NAMESPACE.'updateProfile');
|
||||||
|
define('OMB_ENDPOINT_POSTNOTICE', OMB_NAMESPACE.'postNotice');
|
||||||
define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
|
define('OAUTH_ENDPOINT_REQUEST', OAUTH_NAMESPACE.'endpoint/request');
|
||||||
define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
|
define('OAUTH_ENDPOINT_AUTHORIZE', OAUTH_NAMESPACE.'endpoint/authorize');
|
||||||
define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
|
define('OAUTH_ENDPOINT_ACCESS', OAUTH_NAMESPACE.'endpoint/access');
|
||||||
|
@ -32,3 +36,18 @@ define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header');
|
||||||
define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
|
define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body');
|
||||||
define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
|
define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1');
|
||||||
|
|
||||||
|
function omb_oauth_consumer() {
|
||||||
|
static $con = null;
|
||||||
|
if (!$con) {
|
||||||
|
$con = new OAuthConsumer(common_root_url(), '');
|
||||||
|
}
|
||||||
|
return $con;
|
||||||
|
}
|
||||||
|
|
||||||
|
function omb_hmac_sha1() {
|
||||||
|
static $hmac_method = NULL;
|
||||||
|
if (!$hmac_method) {
|
||||||
|
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
|
||||||
|
}
|
||||||
|
return $hmac_method;
|
||||||
|
}
|
|
@ -439,6 +439,14 @@ function common_mint_tag($extra) {
|
||||||
$config['tag']['date'].':'.$config['tag']['prefix'].$extra;
|
$config['tag']['date'].':'.$config['tag']['prefix'].$extra;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Should make up a reasonable root URL
|
||||||
|
|
||||||
|
function common_root_url() {
|
||||||
|
global $config;
|
||||||
|
$pathpart = ($config['site']['path']) ? $config['site']['path']."/" : '';
|
||||||
|
return "http://".$config['site']['server'].'/'.$pathpart;
|
||||||
|
}
|
||||||
|
|
||||||
// XXX: set up gettext
|
// XXX: set up gettext
|
||||||
|
|
||||||
function _t($str) {
|
function _t($str) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user