4 * This file is (c) Copyright 2010 by Olivier BERGER, Madhumita DHAR, Institut TELECOM
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * This program has been developed in the frame of the COCLICO
21 * project with financial support of its funders.
26 // Inspired from examples described in "Creating a OAuth Service
27 // Provider in PHP" by Morten Fangel
28 // (http://sevengoslings.net/~fangel/oauthprovider-sp-guide.html)
30 require_once 'OAuth.php';
33 * OAuthDataStore singleton class to manage tokens, consumers and nonce in FusionForge DB
35 * Everything specific to the DB model is handled in this class : no other SQL request should exist outside it
36 * It should be reimplemented for other apps, the rest of the classes being untouched
38 * It will assume that OauthAuthzConsumer, OauthAuthzToken and its sub-classes are used
40 * @author Olivier Berger
44 class FFDbOAuthDataStore extends OAuthDataStore {
46 // Hold an instance of the class
47 private static $instance;
50 * Singleton pattern's method to retrieve the instance
52 public static function singleton()
54 if (!isset(self::$instance)) {
56 self::$instance = new $c;
59 return self::$instance;
63 * Prevent users to clone the instance
65 public function __clone()
67 exit_error('Clone is not allowed.', 'oauthprovider');
71 * Converts request or access token types to table names for FusionForge
73 * @param string $token_type
76 protected function token_table_name($token_type) {
77 $t_token_table = null;
78 if( ($token_type == 'request') || ($token_type == 'access') ) {
79 $t_token_table = "plugin_oauthprovider_".$token_type."_token";
81 return $t_token_table;
85 * Retrieve values of columns for a consumer in the DB provided its id
87 * @param int $p_id ID in the DB
88 * @return array of column values
90 function find_consumer_from_id( $p_id ) {
91 $t_consumer_table = "plugin_oauthprovider_consumer";
93 $t_result = db_query_params ("SELECT * FROM $t_consumer_table WHERE id=$1",
94 array ( (int) $p_id )) ;
95 if (!$t_result || ( db_numrows( $t_result ) < 1 )) {
96 exit_error( "Consumer not found!", 'oauthprovider' );
99 $t_row = db_fetch_array( $t_result );
105 * Retrieve a table of columns values for all consumers
107 * @return array of arrays of column values
109 function find_all_consumers() {
110 $t_consumer_table = "plugin_oauthprovider_consumer";
111 $t_result = db_query_params("SELECT * FROM $t_consumer_table ORDER BY name ASC", array());
115 while ( $t_row = db_fetch_array( $t_result ) ) {
123 * Retrieve values of columns for a consumer in the DB provided its key
125 * @param string $p_consumer_key consumer's key
126 * @return array of column values
128 function find_consumer_from_key( $p_consumer_key ) {
129 $t_consumer_table = "plugin_oauthprovider_consumer";
131 $t_query = "SELECT * FROM $t_consumer_table WHERE consumer_key = $1";
132 $t_result = db_query_params( $t_query, array( $p_consumer_key ) );
134 if ( db_numrows( $t_result ) < 1 ) {
135 exit_error( "Consumer not found!", 'oauthprovider' );
138 $t_row = db_fetch_array( $t_result );
144 * Retrieve values of columns for a consumer in the DB provided its key
146 * @param string $p_consumer_key consumer's key
147 * @return array of column values
149 public function lookup_consumer( $p_consumer_key ) {
150 $t_consumer_table = "plugin_oauthprovider_consumer";
152 $t_query = "SELECT * FROM $t_consumer_table WHERE consumer_key = $1";
153 $t_result = db_query_params( $t_query, array( $p_consumer_key ) );
155 if ( db_numrows( $t_result ) < 1 ) {
156 trigger_error("Consumer not found!");
159 $t_row = db_fetch_array( $t_result );
160 $t_consumer = OauthAuthzConsumer::row_to_new_consumer($t_row);
165 * Retrieve values of columns for a consumer in the DB provided its name
167 * @param string $p_consumer_name
168 * @return array of column values
170 function find_consumer_from_name( $p_consumer_name ) {
171 $t_consumer_table = "plugin_oauthprovider_consumer";
173 $t_query = "SELECT * FROM $t_consumer_table WHERE name = $1";
174 $t_result = db_query_params( $t_query, array( $p_consumer_name ) );
176 if ( db_numrows( $t_result ) < 1 ) {
180 $t_row = db_fetch_array( $t_result );
186 * Saves an OauthAuthzConsumer to the DB
188 * @param OauthAuthzConsumer $consumer
189 * @return int the consumer ID in the DB
191 public function save_consumer($consumer) {
192 $t_consumer_table = "plugin_oauthprovider_consumer";
194 $consumer_id = $consumer->getId();
195 if ( 0 == $consumer_id ) { # create
198 $result = db_query_params ("INSERT INTO $t_consumer_table".' ( name, consumer_key, consumer_secret, consumer_url, consumer_desc, consumer_email ) VALUES ($1,$2,$3,$4,$5,$6)',
199 array ($consumer->getName(), $consumer->key, $consumer->secret, $consumer->getUrl(), $consumer->getDesc(), $consumer->getEmail())) ;
201 //$this->setError('Error Adding Consumer: '.db_error());
205 $consumer_id = db_insertid($result, $t_consumer_table, 'id' );
210 $t_query = "UPDATE $t_consumer_table SET name=$1, consumer_key=$2, consumer_secret=$3, consumer_url=$4, consumer_desc=$5, consumer_email=$6 WHERE id=$7";
211 db_query_params( $t_query, array( $consumer->getName(), $consumer->key, $consumer->secret, $consumer->getUrl(), $consumer->getDesc(), $consumer->getEmail(), $consumer->getId() ) );
217 * Creates a new consumer key-secret
219 function new_consumer_keys()
221 $key = md5(util_randbytes(20));
222 $secret = md5(util_randbytes(20));
223 return array($key, $secret);
227 * Deletes a consumer from the DB
229 * @param int $consumer_id
231 public function delete_consumer( $consumer_id ) {
233 $t_consumer_table = "plugin_oauthprovider_consumer";
235 $t_query = "DELETE FROM $t_consumer_table WHERE id=$1";
236 $t_result = db_query_params( $t_query, array( (int) $consumer_id ) );
248 * Retrieve values of columns for a token in the DB provided its key
250 * @param string $token_type
251 * @param string $token_string
252 * @return array of column values
254 public function find_token_from_key($token_type, $token_string) {
255 $t_token_table = $this->token_table_name($token_type);
257 $t_query = "SELECT * FROM $t_token_table WHERE token_key = $1";
258 $t_result = db_query_params( $t_query, array( $token_string ) );
260 if ( db_numrows( $t_result ) < 1 ) {
264 $t_row = db_fetch_array( $t_result );
270 * Retrieve values of columns for a token in the DB provided its id
272 * @param string $token_type
273 * @param int $token_id
274 * @return array of column values
276 public function find_token_from_id($token_type, $token_id) {
277 $t_token_table = $this->token_table_name($token_type);
279 $t_query = "SELECT * FROM $t_token_table WHERE id = $1";
280 $t_result = db_query_params( $t_query, array( (int) $token_id ) );
282 if ( db_numrows( $t_result ) < 1 ) {
286 $t_row = db_fetch_array( $t_result );
292 * Retrieve a table of columns values for all tokens (of a user)
294 * @param string $token_type
295 * @param optional int $user_id
296 * @return array of arrays of column values
298 public function find_all_tokens($token_type, $user_id=null) {
299 $t_token_table = $this->token_table_name($token_type);
300 if(isset($user_id)||($user_id)) {
301 $t_query = "SELECT * FROM $t_token_table WHERE user_id = $1";
302 $t_result = db_query_params( $t_query, array( (int) $user_id ) );
306 $t_query = "SELECT * FROM $t_token_table";
307 $t_result = db_query_params( $t_query, array() );
312 while ( $t_row = db_fetch_array( $t_result ) ) {
320 * Retrieve a table of columns values for all tokens issued for a consumer (and a user)
322 * @param string $token_type
323 * @param int $consumer_id
324 * @param optional int $user_id
325 * @return array of arrays of column values
327 public function find_tokens_by_consumer($token_type, $consumer_id, $user_id=null) {
328 $t_token_table = $this->token_table_name($token_type);
330 if(isset($user_id)) {
331 $t_query = "SELECT * FROM $t_token_table WHERE consumer_id = $1 AND user_id = $2";
332 $t_result = db_query_params( $t_query, array( (int) $consumer_id, (int) $user_id ) );
335 $t_query = "SELECT * FROM $t_token_table WHERE consumer_id = $1";
336 $t_result = db_query_params( $t_query, array( (int) $consumer_id ) );
341 while ( $t_row = db_fetch_array( $t_result ) ) {
349 * Retrieve an OAuthToken from its key
351 * Concrete class implementation required for OAuthDataStore
353 * @param string $token_type
354 * @param string $token_string
355 * @return OauthAuthzToken
357 /* public */ function lookup_token($consumer, $token_type, $token_string) {
361 $t_row=$this->find_token_from_key($token_type, $token_string);
367 // will refuse request tokens too old (older than 24 hours)
368 if( $token_type == 'request' ) {
370 $time_stamp = $t_row['time_stamp'];
372 if ( $time_stamp < ($now - (int)(24 * 3600) ) ) {
373 throw new OAuthException("Invalid (too old) $token_type token: $token_string");
377 if( $t_row['consumer_id'] == $consumer->getId() ) {
378 $token = new OAuthToken($t_row['token_key'], $t_row['token_secret'] );
386 * Check a nonce already existed in the DB
388 * It will auto-purge nonce older than 10 minutes (cleanup made every 100 nonce creation) to avoid the table to fillup
390 * Concrete class implementation required for OAuthDataStore
392 * @param OAuthConsumer $consumer
393 * @param OAuthToken $token
394 * @params string $nonce
395 * @params int $time_stamp
398 /* public */ function lookup_nonce($consumer, $token, $nonce, $time_stamp) {
399 $t_nonce_table = "plugin_oauthprovider_consumer_nonce";
401 $token_key = ($token) ? $token->key : 'two-legged';
403 $t_query = "SELECT * FROM $t_nonce_table WHERE consumer_id = $1 AND token_key = $2 AND nonce = $3 AND time_stamp = $4";
404 $t_result = db_query_params( $t_query, array( $consumer->getId(), $token_key, $nonce, (int) $time_stamp) );
406 // if( ! $consumer->check_nonce ) return false;
408 if ( db_numrows( $t_result ) < 1 ) {
410 $t_query = "INSERT INTO $t_nonce_table ( consumer_id, token_key, nonce, time_stamp ) VALUES ( $1, $2, $3, $4 )";
411 $t_insert_result = db_query_params( $t_query, array( $consumer->getId(), $token_key, $nonce, (int) $time_stamp) );
413 $nonce_id = db_insertid($t_insert_result, $t_nonce_table, 'id' );
415 // every 100 nonce, try and remove obsolete nonces
416 if (($nonce_id % 100) == 0) {
417 // will remove nonces older than 10 minutes (2* OAuthServer's time_stamp_threshold)
419 $t_query = "DELETE FROM $t_nonce_table WHERE time_stamp < $1";
420 db_query_params( $t_query, array( (int) ($now - 600) ) );
431 // make sure this fails... as it seems not implemented / used in parent class
432 function fetch_request_token($consumer) {
433 exit_error('fetch_request_token() not yet implemented.', 'oauthprovider');
436 // make sure this fails... as it seems not implemented / used in parent class
437 function fetch_access_token($token, $consumer) {
438 exit_error('fetch_access_token() not yet implemented.', 'oauthprovider');
442 * Generates an new token in the DB
444 * It will auto-purge request tokens older than 24 hours that haven't been converted to access tokens in time (cleanup made every 100 request token creation)
446 * @param OAuthConsumer $consumer
447 * @param string $token_type
450 protected function new_token($consumer, $token_type, $role_id=0) {
451 $t_token_table = $this->token_table_name($token_type);
453 $random = util_randbytes(32);
454 $hash = sha1($random);
455 $key = substr($hash, 0, 20);
456 $secret = substr($hash, 20, 40);
458 $time_stamp = time();
460 $token = new OAuthToken($key, $secret);
462 $t_query = "INSERT INTO $t_token_table ( consumer_id, token_key, token_secret, role_id, time_stamp ) VALUES ( $1, $2, $3, $4, $5 )";
463 $t_result = db_query_params( $t_query, array( $consumer->getId(), $token->key, $token->secret, $role_id, $time_stamp) );
465 $token_id = db_insertid($t_result, $t_token_table, 'id');
467 if( $token_type == 'request' ) {
468 // every 100 request token, try and remove obsolete ones
469 if (($token_id % 100) == 0) {
470 // will remove request tokens older than 24 hours
472 $t_query = "DELETE FROM $t_token_table WHERE time_stamp < $1";
473 db_query_params( $t_query, array( (int) ($now - (24 * 3600) ) ) );
480 * Generates a new request token in the DB
482 * Concrete class implboundementation
483 * called by the OAuthServer
485 * @param OAuthConsumer $consumer
488 public function new_request_token($consumer) {
489 $token = $this->new_token($consumer, 'request');
491 // TODO : return an OauthAuthzRequestToken
496 * Generates a new access token in the DB
498 * Concrete class implementation
499 * called by the OAuthServer
501 * @param OAuthToken $request_token
502 * @param OAuthConsumer $consumer
505 public function new_access_token($request_token, $consumer) {
507 // $t_row=$this->find_token_from_key('access', $request_token->key);
508 $t_row=$this->find_token_from_key('request', $request_token->key);
510 $token_id = $t_row['id'];
511 $consumer_id = $t_row['consumer_id'];
512 $authorized = $t_row['authorized'];
513 $user_id = $t_row['user_id'];
514 $role_id = $t_row['role_id'];
516 // delete in any case to avoid replaying and such
517 $this->delete_token('request', $token_id);
519 if( $consumer->getId() === $consumer_id ) {
520 if( $authorized && isset($user_id) ) {
522 $access_token = $this->new_token($consumer, 'access', $role_id);
524 $t_token_table = "plugin_oauthprovider_access_token";
526 $t_query = "UPDATE $t_token_table SET user_id=$1 WHERE token_key = $2";
527 db_query_params( $t_query, array( $user_id, $access_token->key ) );
529 // TODO : return an OauthAuthzAccessToken
530 return $access_token;
532 // Token wasn't authorized
533 throw new OAuthException('You can\'t swap a unauthorized request token for a access token. Your Access Token was still deleted though. Nice try..');
537 throw new OAuthException('This Request Token doesn\'t belong to your Consumer Key. Your Access Token was still deleted though. Nice Try.');
542 * Saves an OauthAuthzAccessToken to the DB
544 * @param OauthAuthzAccessToken $token
545 * @return int the token ID in the DB
547 public function save_access_token($token) {
549 $t_token_table = $this->token_table_name('access');
551 $token_id = $token->getId();
552 if ( 0 == $token_id ) { # create
553 $t_query = "INSERT INTO $t_token_table ( consumer_id, token_key, token_secret, user_id, role_id, time_stamp ) VALUES ($1, $2, $3 $4, $5, $6)";
554 $t_result = db_query_params( $t_query, array( $token->getConsumerId(), $token->key, $token->secret, $token->getUserId(), $token->getRoleId(), $token->gettime_stamp() ) );
556 $token_id = db_insertid($t_result, $t_token_table, 'id');
559 else { # TODO feature to be added later, with lifetime/limited access feature support
560 //$t_query = "UPDATE $t_token_table SET consumer_id=$1, token_key=$2, token_secret=$3, user_id=$4, time_stamp=$4 WHERE id=$5";
561 //db_query_params( $t_query, array( $token->getConsumerId(), $token->key, $token->secret, $token->getUserId(), $token->gettime_stamp(), $token->getId() ) );
562 exit_error("The access token already exists and cannot be modified.", 'oauthprovider');
568 * Saves an OauthAuthzRequestToken to the DB
570 * @param OauthAuthzRequestToken $token
571 * @return int the token ID in the DB
573 public function save_request_token($token) {
575 $t_token_table = $this->token_table_name('request');
577 $token_id = $token->getId();
578 if ( 0 == $token_id ) { # create
579 $t_query = "INSERT INTO $t_token_table ( consumer_id, token_key, token_secret, authorized, user_id, role_id, time_stamp ) VALUES ($1, $2, $3, $4, $5, $6, $7)";
580 $t_result = db_query_params( $t_query, array( $token->getConsumerId(), $token->key, $token->secret, $token->getAuthorized(), $token->getUserId(), $token->getRoleId(), $token->gettime_stamp() ) );
582 $token_id = db_insertid($t_result, $t_token_table, 'id');
584 $t_query = "UPDATE $t_token_table SET consumer_id=$1, token_key=$2, token_secret=$3, authorized=$4, verifier=$5, user_id=$6, role_id=$7, time_stamp=$8 WHERE id=$9";
585 db_query_params( $t_query, array( $token->getConsumerId(), $token->key, $token->secret, $token->getAuthorized(), $token->getVerifier(), $token->getUserId(), $token->getRoleId(), $token->gettime_stamp(), $token->getId() ) );
592 * Deletes a token from the DB
594 * @param string $token_type
595 * @param int $token_id
597 function delete_token( $token_type, $token_id) {
598 $t_token_table = $this->token_table_name($token_type);
600 $t_query = "DELETE FROM $t_token_table WHERE id=$1";
601 $t_result = db_query_params( $t_query, array( (int) $token_id ) );