We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

Commit e56385a7 authored by Zach Copley's avatar Zach Copley

Use a new table (oauth_token_association) to associate authorized

request tokins with OAuth client applications and profiles.
parent 3d6a0f73
......@@ -78,7 +78,8 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
$this->reqToken = $req->get_parameter('oauth_token');
$this->verifier = $req->get_parameter('oauth_verifier');
$app = $datastore->getAppByRequestToken($this->reqToken);
$app = $datastore->getAppByRequestToken($this->reqToken);
$atok = $server->fetch_access_token($req);
} catch (Exception $e) {
......@@ -106,7 +107,7 @@ class ApiOauthAccessTokenAction extends ApiOauthAction
common_log(
LOG_INFO,
sprintf(
"Issued now access token '%s' for application %d (%s).",
"Issued access token '%s' for application %d (%s).",
$atok->key,
$app->id,
$app->name
......
......@@ -177,28 +177,24 @@ class ApiOauthAuthorizeAction extends Action
$this->serverError($e->getMessage());
}
// associated the authorized req token with the user and the app
// XXX: Make sure we have a oauth_token_association table. The table
// is now in the main schema, but because it is being added with
// a point release, it's unlikely to be there. This code can be
// removed as of 1.0.
$this->ensureOauthTokenAssociationTable();
$appUser = new Oauth_application_user();
$tokenAssoc = new Oauth_token_association();
$appUser->profile_id = $user->id;
$appUser->application_id = $this->app->id;
$tokenAssoc->profile_id = $user->id;
$tokenAssoc->application_id = $this->app->id;
$tokenAssoc->token = $this->oauthTokenParam;
$tokenAssoc->created = common_sql_now();
// 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->token = $this->oauthTokenParam;
$appUser->created = common_sql_now();
$result = $appUser->insert();
$result = $tokenAssoc->insert();
if (!$result) {
common_log_db_error($appUser, 'INSERT', __FILE__);
$this->serverError(_('Database error inserting OAuth application user.'));
common_log_db_error($tokenAssoc, 'INSERT', __FILE__);
$this->serverError(_('Database error inserting oauth_token_association.'));
}
// If we have a callback redirect and provide the token
......@@ -265,6 +261,30 @@ class ApiOauthAuthorizeAction extends Action
}
}
// XXX Remove this function when we hit 1.0
function ensureOauthTokenAssociationTable()
{
$schema = Schema::get();
$reqTokenCols = array(
new ColumnDef('profile_id', 'integer', null, true, 'PRI'),
new ColumnDef('application_id', 'integer', null, true, 'PRI'),
new ColumnDef('token', 'varchar', 255, true, 'PRI'),
new ColumnDef('created', 'datetime', null, false),
new ColumnDef(
'modified',
'timestamp',
null,
false,
null,
'CURRENT_TIMESTAMP',
'on update CURRENT_TIMESTAMP'
)
);
$schema->ensureTable('oauth_token_association', $reqTokenCols);
}
function showForm($error=null)
{
$this->error = $error;
......
<?php
/**
* Table Definition for oauth_association
*/
require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
class Oauth_token_association extends Memcached_DataObject
{
###START_AUTOCODE
/* the code below is auto generated do not remove the above tag */
public $__table = 'oauth_token_association'; // table name
public $profile_id; // int(4) primary_key not_null
public $application_id; // int(4) primary_key not_null
public $token; // varchar(255) primary key not null
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_token_association', $k, $v);
}
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
static function getByUserAndToken($user, $token)
{
if (empty($user) || empty($token)) {
return null;
}
$oau = new oauth_request_token();
$oau->profile_id = $user->id;
$oau->token = $token;
$oau->limit(1);
$result = $oau->find(true);
return empty($result) ? null : $oau;
}
}
......@@ -401,6 +401,18 @@ modified = 384
profile_id = K
application_id = K
[oauth_token_association]
profile_id = 129
application_id = 129
token = 130
created = 142
modified = 384
[oauth_token_association__keys]
profile_id = K
application_id = K
token = K
[profile]
id = 129
nickname = 130
......
......@@ -237,6 +237,15 @@ create table oauth_application_user (
constraint primary key (profile_id, application_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
create table oauth_token_association (
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),
token varchar(255) comment 'request or access token',
created datetime not null comment 'date this record was created',
modified timestamp comment 'date this record was modified',
constraint primary key (profile_id, application_id, token)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
/* These are used by JanRain OpenID library */
create table oid_associations (
......
......@@ -32,24 +32,41 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
// Create an anon consumer and anon application if one
// doesn't exist already
if ($consumerKey == 'anonymous') {
common_debug("API OAuth - creating anonymous consumer");
$con = new Consumer();
$con->consumer_key = $consumerKey;
$con->consumer_secret = $consumerKey;
$con->created = common_sql_now();
$result = $con->insert();
if (!$result) {
$this->serverError(_("Could not create anonymous consumer."));
}
$app = new OAuth_application();
$app->consumer_key = $con->consumer_key;
$app->name = 'anonymous';
// XXX: allow the user to set the access type when
// authorizing? Currently we default to r+w for anonymous
// OAuth client applications
$app->access_type = 3; // read + write
$id = $app->insert();
if (!$id) {
$this->serverError(_("Could not create anonymous OAuth application."));
$app = Oauth_application::getByConsumerKey('anonymous');
if (!$app) {
common_debug("API OAuth - creating anonymous application");
$app = new OAuth_application();
$app->owner = 1; // XXX: What to do here?
$app->consumer_key = $con->consumer_key;
$app->name = 'anonymous';
$app->icon = 'default-avatar-stream.png'; // XXX: Fix this!
$app->description = "An anonymous application";
// XXX: allow the user to set the access type when
// authorizing? Currently we default to r+w for anonymous
// OAuth client applications
$app->access_type = 3; // read + write
$app->type = 2; // desktop
$app->created = common_sql_now();
$id = $app->insert();
if (!$id) {
$this->serverError(_("Could not create anonymous OAuth application."));
}
}
} else {
return null;
......@@ -64,10 +81,12 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
function getAppByRequestToken($token_key)
{
// Look up the full req tokenx
$req_token = $this->lookup_token(null,
'request',
$token_key);
// Look up the full req token
$req_token = $this->lookup_token(
null,
'request',
$token_key
);
if (empty($req_token)) {
common_debug("couldn't get request token from oauth datastore");
......@@ -85,7 +104,6 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
}
// Look up the app
$app = new Oauth_application();
$app->consumer_key = $token->consumer_key;
$result = $app->find(true);
......@@ -102,12 +120,12 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
{
common_debug(
sprintf(
"%s - New access token from request token %s, consumer %s and verifier %s ",
__FILE__,
"New access token from request token %s, consumer %s and verifier %s ",
$token,
$consumer,
$verifier
)
),
__FILE__
);
$rt = new Token();
......@@ -121,73 +139,121 @@ class ApiStatusNetOAuthDataStore extends StatusNetOAuthDataStore
if ($rt->find(true) && $rt->state == 1 && $rt->verifier == $verifier) { // authorized
common_debug('request token found.');
common_debug('Request token found.', __FILE__);
// find the app and profile associated with this token
// find the associated user of the app
$tokenAssoc = OAuth_token_association::staticGet('token', $rt->tok);
if (!$tokenAssoc) {
throw new Exception(
_('Could not find a profile and application associated with the request token.')
);
}
// check to see if we have previously issued an access token for this application
// and profile
$appUser = new Oauth_application_user();
$appUser->application_id = $app->id;
$appUser->token = $rt->tok;
$appUser->profile_id = $tokenAssoc->profile_id;
$result = $appUser->find(true);
if (!empty($result)) {
common_debug("Ouath app user found.");
} else {
common_debug("Oauth app user not found. app id $app->id token $rt->tok");
return null;
}
// go ahead and make the access token
common_log(LOG_INFO,
sprintf(
"Existing access token found for application %s, profile %s.",
$app->id,
$tokenAssoc->profile_id
)
);
$at = new Token();
$at->consumer_key = $consumer->key;
$at->tok = common_good_rand(16);
$at->secret = common_good_rand(16);
$at->type = 1; // access
$at->verifier = $verifier;
$at->verified_callback = $rt->verified_callback; // 1.0a
$at->created = DB_DataObject_Cast::dateTime();
$at = new Token();
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;
// fetch the full access token
$at->consumer_key = $consumer->key;
$at->tok = $appUser->token;
$result = $at->find(true);
if (!$result) {
throw new Exception(
_('Could not issue access token.')
);
}
common_debug('request token "'.$rt->tok.'" updated', __FILE__);
// update the token from req to access for the user
// Yay, we can re-issue the access token
return new OAuthToken($at->tok, $at->secret);
$orig = clone($appUser);
} else {
$appUser->token = $at->tok;
common_log(LOG_INFO,
sprintf(
"Creating new access token for application %s, profile %s.",
$app->id,
$tokenAssoc->profile_id
)
);
// make a brand new 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->verifier = $verifier;
$at->verified_callback = $rt->verified_callback; // 1.0a
$at->created = common_sql_now();
if (!$at->insert()) {
$e = $at->_lastError;
common_debug('access token "' . $at->tok . '" not inserted: "' . $e->message . '"', __FILE__);
return null;
} else {
common_debug('access token "' . $at->tok . '" inserted', __FILE__);
// burn the old one
$orig_rt = clone($rt);
$rt->state = 2; // used
if (!$rt->update($orig_rt)) {
return null;
}
common_debug('request token "' . $rt->tok . '" updated', __FILE__);
}
// 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.
// insert a new Oauth_application_user record w/access token
$appUser = new Oauth_application_user();
$appUser->access_type = $app->access_type;
$appUser->profile_id = $tokenAssoc->profile_id;;
$appUser->application_id = $app->id;
$appUser->access_type = $app->access_type;
$appUser->token = $at->tok;
$appUser->created = common_sql_now();
$result = $appUser->updateKeys($orig);
$result = $appUser->insert();
if (!$result) {
throw new Exception('Couldn\'t update OAuth app user.');
common_log_db_error($appUser, 'INSERT', __FILE__);
$this->serverError(_('Database error inserting OAuth application user.'));
}
// Okay, good
return new OAuthToken($at->tok, $at->secret);
}
} else {
// the token was not authorized or not verfied
common_log(
LOG_INFO,
sprintf(
"API OAuth - Attempt to exchange unauthorized or unverified request token %s for an access token.",
$rt->tok
)
);
return null;
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment