From d4a2ace4032d875604c41d0277afca67b2eb4c43 Mon Sep 17 00:00:00 2001 From: Deon George Date: Mon, 27 May 2013 22:10:41 +1000 Subject: [PATCH] Enabled OAuth/OAuth2 logins --- application/classes/Auth/OSB.php | 2 +- modules/export/classes/Model/Export.php | 2 +- modules/oauth/classes/Auth/Facebook.php | 129 +---- .../oauth/classes/Auth/Facebook_Connect.php | 121 ++++ modules/oauth/classes/Auth/Google.php | 14 + modules/oauth/classes/Auth/Linkedin.php | 14 + modules/oauth/classes/Auth/ORM/External.php | 50 ++ .../oauth/classes/Auth/ORM/External/OAuth.php | 97 ++++ .../classes/Auth/ORM/External/OAuth2.php | 86 +++ modules/oauth/classes/Controller/Oauth.php | 22 +- modules/oauth/classes/Kohana/OAuth.php | 278 +++++++++ modules/oauth/classes/Kohana/OAuth/API.php | 85 +++ .../Kohana/OAuth/API/Profile/Linkedin.php | 17 + .../oauth/classes/Kohana/OAuth/Consumer.php | 109 ++++ .../oauth/classes/Kohana/OAuth/Exception.php | 12 + .../oauth/classes/Kohana/OAuth/Provider.php | 253 ++++++++ .../Kohana/OAuth/Provider/Linkedin.php | 78 +++ .../oauth/classes/Kohana/OAuth/Request.php | 541 ++++++++++++++++++ .../classes/Kohana/OAuth/Request/Access.php | 32 ++ .../Kohana/OAuth/Request/Authorize.php | 26 + .../Kohana/OAuth/Request/Exception.php | 27 + .../classes/Kohana/OAuth/Request/Resource.php | 27 + .../classes/Kohana/OAuth/Request/Token.php | 32 ++ .../oauth/classes/Kohana/OAuth/Response.php | 65 +++ .../oauth/classes/Kohana/OAuth/Signature.php | 77 +++ .../Kohana/OAuth/Signature/HMAC/SHA1.php | 68 +++ .../Kohana/OAuth/Signature/Plaintext.php | 57 ++ modules/oauth/classes/Kohana/OAuth/Token.php | 94 +++ .../classes/Kohana/OAuth/Token/Access.php | 26 + .../classes/Kohana/OAuth/Token/Request.php | 46 ++ modules/oauth/classes/Kohana/OAuth2.php | 48 ++ modules/oauth/classes/Kohana/OAuth2/API.php | 30 + .../Kohana/OAuth2/API/Profile/Facebook.php | 17 + .../Kohana/OAuth2/API/Profile/Google.php | 17 + .../oauth/classes/Kohana/OAuth2/Client.php | 98 ++++ .../oauth/classes/Kohana/OAuth2/Provider.php | 127 ++++ .../Kohana/OAuth2/Provider/Facebook.php | 32 ++ .../classes/Kohana/OAuth2/Provider/Google.php | 44 ++ .../oauth/classes/Kohana/OAuth2/Request.php | 67 +++ .../Kohana/OAuth2/Request/Authorize.php | 20 + .../Kohana/OAuth2/Request/Resource.php | 19 + .../classes/Kohana/OAuth2/Request/Token.php | 22 + modules/oauth/classes/Kohana/OAuth2/Token.php | 29 + .../classes/Kohana/OAuth2/Token/Access.php | 7 + modules/oauth/classes/Model/Oauth.php | 2 +- modules/oauth/classes/OAuth.php | 3 + modules/oauth/classes/OAuth/API.php | 3 + .../classes/OAuth/API/Profile/Linkedin.php | 3 + modules/oauth/classes/OAuth/Consumer.php | 3 + .../oauth/classes/{Oauth => OAuth}/Plugin.php | 4 +- .../{Oauth => OAuth}/Plugin/Facebook.php | 4 +- modules/oauth/classes/OAuth/Plugin/Google.php | 17 + .../oauth/classes/OAuth/Plugin/Linkedin.php | 17 + modules/oauth/classes/OAuth/Provider.php | 3 + .../oauth/classes/OAuth/Provider/Linkedin.php | 3 + modules/oauth/classes/OAuth/Request.php | 3 + .../oauth/classes/OAuth/Request/Access.php | 3 + .../oauth/classes/OAuth/Request/Authorize.php | 3 + .../oauth/classes/OAuth/Request/Resource.php | 3 + modules/oauth/classes/OAuth/Request/Token.php | 3 + modules/oauth/classes/OAuth/Response.php | 3 + modules/oauth/classes/OAuth/Signature.php | 3 + .../classes/OAuth/Signature/HMAC/SHA1.php | 3 + .../classes/OAuth/Signature/Plaintext.php | 3 + modules/oauth/classes/OAuth/Token.php | 3 + modules/oauth/classes/OAuth/Token/Access.php | 3 + modules/oauth/classes/OAuth/Token/Request.php | 3 + modules/oauth/classes/OAuth2.php | 3 + modules/oauth/classes/OAuth2/API.php | 3 + .../classes/OAuth2/API/Profile/Facebook.php | 3 + .../classes/OAuth2/API/Profile/Google.php | 3 + modules/oauth/classes/OAuth2/Client.php | 3 + modules/oauth/classes/OAuth2/Provider.php | 3 + .../classes/OAuth2/Provider/Facebook.php | 3 + .../oauth/classes/OAuth2/Provider/Google.php | 3 + modules/oauth/classes/OAuth2/Request.php | 3 + .../classes/OAuth2/Request/Authorize.php | 3 + .../oauth/classes/OAuth2/Request/Resource.php | 3 + .../oauth/classes/OAuth2/Request/Token.php | 3 + modules/oauth/classes/OAuth2/Token.php | 3 + modules/oauth/classes/OAuth2/Token/Access.php | 3 + modules/oauth/media/js/facebook.js | 2 +- 82 files changed, 3052 insertions(+), 154 deletions(-) create mode 100644 modules/oauth/classes/Auth/Facebook_Connect.php create mode 100644 modules/oauth/classes/Auth/Google.php create mode 100644 modules/oauth/classes/Auth/Linkedin.php create mode 100644 modules/oauth/classes/Auth/ORM/External.php create mode 100644 modules/oauth/classes/Auth/ORM/External/OAuth.php create mode 100644 modules/oauth/classes/Auth/ORM/External/OAuth2.php create mode 100644 modules/oauth/classes/Kohana/OAuth.php create mode 100644 modules/oauth/classes/Kohana/OAuth/API.php create mode 100644 modules/oauth/classes/Kohana/OAuth/API/Profile/Linkedin.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Consumer.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Exception.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Provider.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Provider/Linkedin.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Request.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Request/Access.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Request/Authorize.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Request/Exception.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Request/Resource.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Request/Token.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Response.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Signature.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Signature/HMAC/SHA1.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Signature/Plaintext.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Token.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Token/Access.php create mode 100644 modules/oauth/classes/Kohana/OAuth/Token/Request.php create mode 100644 modules/oauth/classes/Kohana/OAuth2.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/API.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/API/Profile/Facebook.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/API/Profile/Google.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Client.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Provider.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Provider/Facebook.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Provider/Google.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Request.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Request/Authorize.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Request/Resource.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Request/Token.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Token.php create mode 100644 modules/oauth/classes/Kohana/OAuth2/Token/Access.php create mode 100644 modules/oauth/classes/OAuth.php create mode 100644 modules/oauth/classes/OAuth/API.php create mode 100644 modules/oauth/classes/OAuth/API/Profile/Linkedin.php create mode 100644 modules/oauth/classes/OAuth/Consumer.php rename modules/oauth/classes/{Oauth => OAuth}/Plugin.php (85%) rename modules/oauth/classes/{Oauth => OAuth}/Plugin/Facebook.php (92%) create mode 100644 modules/oauth/classes/OAuth/Plugin/Google.php create mode 100644 modules/oauth/classes/OAuth/Plugin/Linkedin.php create mode 100644 modules/oauth/classes/OAuth/Provider.php create mode 100644 modules/oauth/classes/OAuth/Provider/Linkedin.php create mode 100644 modules/oauth/classes/OAuth/Request.php create mode 100644 modules/oauth/classes/OAuth/Request/Access.php create mode 100644 modules/oauth/classes/OAuth/Request/Authorize.php create mode 100644 modules/oauth/classes/OAuth/Request/Resource.php create mode 100644 modules/oauth/classes/OAuth/Request/Token.php create mode 100644 modules/oauth/classes/OAuth/Response.php create mode 100644 modules/oauth/classes/OAuth/Signature.php create mode 100644 modules/oauth/classes/OAuth/Signature/HMAC/SHA1.php create mode 100644 modules/oauth/classes/OAuth/Signature/Plaintext.php create mode 100644 modules/oauth/classes/OAuth/Token.php create mode 100644 modules/oauth/classes/OAuth/Token/Access.php create mode 100644 modules/oauth/classes/OAuth/Token/Request.php create mode 100644 modules/oauth/classes/OAuth2.php create mode 100644 modules/oauth/classes/OAuth2/API.php create mode 100644 modules/oauth/classes/OAuth2/API/Profile/Facebook.php create mode 100644 modules/oauth/classes/OAuth2/API/Profile/Google.php create mode 100644 modules/oauth/classes/OAuth2/Client.php create mode 100644 modules/oauth/classes/OAuth2/Provider.php create mode 100644 modules/oauth/classes/OAuth2/Provider/Facebook.php create mode 100644 modules/oauth/classes/OAuth2/Provider/Google.php create mode 100644 modules/oauth/classes/OAuth2/Request.php create mode 100644 modules/oauth/classes/OAuth2/Request/Authorize.php create mode 100644 modules/oauth/classes/OAuth2/Request/Resource.php create mode 100644 modules/oauth/classes/OAuth2/Request/Token.php create mode 100644 modules/oauth/classes/OAuth2/Token.php create mode 100644 modules/oauth/classes/OAuth2/Token/Access.php diff --git a/application/classes/Auth/OSB.php b/application/classes/Auth/OSB.php index 0243420a..2cc091dd 100644 --- a/application/classes/Auth/OSB.php +++ b/application/classes/Auth/OSB.php @@ -4,7 +4,7 @@ * OSB Auth driver. * * @package OSB - * @category Helpers + * @category Classes * @author Deon George * @copyright (c) 2009-2013 Open Source Billing * @license http://dev.osbill.net/license.html diff --git a/modules/export/classes/Model/Export.php b/modules/export/classes/Model/Export.php index 8efa4208..71d81cc3 100644 --- a/modules/export/classes/Model/Export.php +++ b/modules/export/classes/Model/Export.php @@ -18,7 +18,7 @@ class Model_Export extends ORM_OSB { protected $_form = array('id'=>'id','value'=>'name'); /** - * Return the object of the product plugin + * Return the object of the export plugin */ public function plugin(Model_Export_Module $emo,$type='') { $c = Kohana::classname('Export_Plugin_'.$this->plugin); diff --git a/modules/oauth/classes/Auth/Facebook.php b/modules/oauth/classes/Auth/Facebook.php index 76550338..718e1d37 100644 --- a/modules/oauth/classes/Auth/Facebook.php +++ b/modules/oauth/classes/Auth/Facebook.php @@ -9,133 +9,6 @@ * @copyright (c) 2009-2013 Open Source Billing * @license http://dev.osbill.net/license.html */ -class Auth_Facebook extends Auth_ORM { - // Our Facebook config data - private $config; - private $data; - // Our Facebook Object - private $fb; - private $me; - // Our OAuth Object - private $oo; - // Facebook UID - private $uid; - - /** - * Perform the login processing - * - * We ignore password, since it is required in the parent(), we dont need it in Oauth - */ - protected function _login($user,$password,$remember) { - $this->complete_login($user); - - if ($remember) { - $aoo = ORM::factory('Account_Oauth',array('account_id'=>$user->id)); - - // Record our user in the DB - $aoo->account_id = $user->id; - $aoo->oauth_id = $this->oo->id; - $aoo->userid = $remember->user_id(); - - switch ($this->oo->name) { - case 'facebook': - $aoo->oauth_data = $remember->fb->getAccessToken(); - break; - } - - return $aoo->save(); - } - } - public function __construct(Model_Oauth $oo) { - include Kohana::find_file('vendor', 'facebook'); - $this->oo = $oo; - - // Load configuration "config/facebook" - $this->config = Kohana::$config->load('facebook'); - - parent::__construct((array)Kohana::$config->load('auth')); - - // Create new Facebook object - $this->fb = new Facebook(array( - 'appId' => $oo->app_id, - 'secret' => $oo->secret, - 'cookie' => $this->config->cookie, - 'session_type' => $this->config->session_type, - )); - - try { - $this->me = $this->fb->api('/' . $this->fb->getUser(), 'GET'); - - } catch (FacebookApiException $e) { - // Do nothing. - } - } - - /** - * Returns user data, default in case of failure. - * - * @param $key - * @param null $default - * @return mixed - * @throws FacebookApiException - */ - public function get($key,$default=NULL) { - if (! $uid = $this->user_id()) { - $this->login_url(); - - throw new FacebookApiException('User is not logged in.'); - } - - if (empty($this->data)) - $this->data = $this->fb->api(array( - 'method' => 'fql.query', - 'query' => sprintf('SELECT %s FROM user WHERE uid = %s',$this->config_fields,$uid), - )); - - return (! empty($this->data[0][$key])) ? $this->data[0][$key] : $default; - } - - /** - * Is user currently logged into facebook? - */ - public function logged_in($role=NULL,$debug=NULL) { - return $this->fb->getUser() ? TRUE : FALSE; - } - - /** - * Creates a login url, based on scope, redirect_uri and display. - * - * @return string - */ - public function login_url() { - return urldecode($this->fb->getLoginUrl(array( - 'scope' => $this->config->scope, - 'redirect_uri' => $this->config->redirect_uri, - 'display' => $this->config->display, - ))); - } - - /** - * Creates a logout url based on next. - * - * @return string - */ - public function logout_url() { - return urldecode($this->fb->getLogoutUrl(array('next'=>$this->config->next))); - } - - /** - * Return user id if success, otherwise FALSE. - */ - public function user_id() { - if ($this->logged_in()) { - $this->uid = $this->fb->getUser(); - - return $this->uid; - - } else { - return FALSE; - } - } +class Auth_Facebook extends Auth_ORM_External_OAuth2 { } ?> diff --git a/modules/oauth/classes/Auth/Facebook_Connect.php b/modules/oauth/classes/Auth/Facebook_Connect.php new file mode 100644 index 00000000..ece59e50 --- /dev/null +++ b/modules/oauth/classes/Auth/Facebook_Connect.php @@ -0,0 +1,121 @@ +$problem)); + } + + parent::__construct($oo); + + include Kohana::find_file('vendor', 'facebook'); + + // Load configuration "config/facebook" + $this->config = Kohana::$config->load('facebook'); + + // Create new Facebook object + $this->ao = new Facebook(array( + 'appId' => $oo->app_id, + 'secret' => $oo->secret, + 'cookie' => $this->config->cookie, + 'session_type' => $this->config->session_type, + )); + + try { + $this->me = $this->ao->api('/' . $this->ao->getUser(), 'GET'); + + } catch (FacebookApiException $e) { + // Do nothing. + } + } + + /** + * Returns user data, default in case of failure. + * + * @param $key + * @param null $default + * @return mixed + * @throws FacebookApiException + */ + public function get($key,$default=NULL) { + if (! $uid = $this->user_id()) { + $this->login_url(); + + throw new FacebookApiException('User is not logged in.'); + } + + if (empty($this->data)) + $this->data = $this->ao->api(array( + 'method' => 'fql.query', + 'query' => sprintf('SELECT %s FROM user WHERE uid = %s',$this->config_fields,$uid), + )); + + return (! empty($this->data[0][$key])) ? $this->data[0][$key] : $default; + } + + /** + * Is user currently logged into facebook? + */ + public function logged_in($role=NULL,$debug=NULL) { + return $this->ao->getUser() ? TRUE : FALSE; + } + + /** + * Creates a login url, based on scope, redirect_uri and display. + * + * @return string + */ + public function login_url() { + return urldecode($this->ao->getLoginUrl(array( + 'scope' => $this->config->scope, + 'redirect_uri' => $this->config->redirect_uri, + 'display' => $this->config->display, + ))); + } + + /** + * Creates a logout url based on next. + * + * @return string + */ + public function logout_url() { + return urldecode($this->ao->getLogoutUrl(array('next'=>$this->config->next))); + } + + /** + * Return user id if success, otherwise FALSE. + */ + public function user_id() { + if ($this->logged_in()) { + $this->uid = $this->ao->getUser(); + + return $this->uid; + + } else { + return FALSE; + } + } +} +?> diff --git a/modules/oauth/classes/Auth/Google.php b/modules/oauth/classes/Auth/Google.php new file mode 100644 index 00000000..b9b5b2a6 --- /dev/null +++ b/modules/oauth/classes/Auth/Google.php @@ -0,0 +1,14 @@ + diff --git a/modules/oauth/classes/Auth/Linkedin.php b/modules/oauth/classes/Auth/Linkedin.php new file mode 100644 index 00000000..86573be6 --- /dev/null +++ b/modules/oauth/classes/Auth/Linkedin.php @@ -0,0 +1,14 @@ + diff --git a/modules/oauth/classes/Auth/ORM/External.php b/modules/oauth/classes/Auth/ORM/External.php new file mode 100644 index 00000000..879bed34 --- /dev/null +++ b/modules/oauth/classes/Auth/ORM/External.php @@ -0,0 +1,50 @@ +load('auth')); + + $this->oo = $oo; + } + + protected function _login($user,$password,$remember) { + $this->complete_login($user); + + if ($remember) { + $aoo = $this->oo->account_oauth->where('account_id','=',$user->id)->find(); + + // Record our user in the DB + $aoo->account_id = $user->id; + $aoo->oauth_id = $this->oo->id; + $aoo->userid = $remember->user_id(); + + if ($user instanceof Auth_ORM_External_OAuth2 OR $user instanceof Auth_ORM_External_OAuth) + $aoo->oauth_data = array( + 'token'=>$remember->token, + ); + elseif ($user instanceof Auth_ORM_External) + $aoo->oauth_data = array( + 'token'=>$remember->ao->getAccessToken(), + ); + + return $aoo->save(); + } + + return TRUE; + } +} +?> diff --git a/modules/oauth/classes/Auth/ORM/External/OAuth.php b/modules/oauth/classes/Auth/ORM/External/OAuth.php new file mode 100644 index 00000000..36293340 --- /dev/null +++ b/modules/oauth/classes/Auth/ORM/External/OAuth.php @@ -0,0 +1,97 @@ +$problem)); + } + + parent::__construct($oo); + + // Load the provider + $this->provider = OAuth_Provider::factory($this->oo->name); + + // Load the consumer for this provider + $this->consumer = OAuth_Consumer::factory(array( + 'key'=>$this->oo->app_id, + 'secret'=>$this->oo->secret, + )); + } + + protected function _login($user,$password,$remember) { + // During login, we dont need to keep out token anymore + Session::instance()->delete('oauth.token'); + + return parent::_login($user,$password,$remember); + } + + /** + * Is user currently log in?? + * If we have a valid token, then we are. + */ + public function logged_in($role=NULL,$debug=NULL) { + // Attempt to complete signin + if ($verifier = Arr::get($_REQUEST,'oauth_verifier')) { + // Restart the login process + if (! $token = Session::instance()->get_once('oauth.request') OR $token->token !== Arr::get($_REQUEST,'oauth_token')) + return FALSE; + + // Store the verifier in the token + $token->verifier($verifier); + + // Exchange the request token for an access token + $this->token = $this->provider->access_token($this->consumer,$token); + + // Store the access token + Session::instance()->set('oauth.token',$this->token); + + // Otherwise try and get our token from our session. + } else { + $this->token = Session::instance()->get('oauth.token'); + } + + // @todo We need to check that the token is still valid. + return $this->token ? TRUE : FALSE; + } + + /** + * Our URL that starts the login process + */ + public function login_url() { + // Add the callback URL to the consumer + $this->consumer->callback(URL::site('oauth/login/'.$this->oo->name,TRUE)); + + // Get a request token for the consumer + $token = $this->provider->request_token($this->consumer); + + // Store the token + Session::instance()->set('oauth.request',$token); + + // Get the login URL from the provider + return $this->provider->authorize_url($token); + } + + public function user_id() { + return $this->provider->user_details($this->consumer,$this->token)->id(); + } +} +?> diff --git a/modules/oauth/classes/Auth/ORM/External/OAuth2.php b/modules/oauth/classes/Auth/ORM/External/OAuth2.php new file mode 100644 index 00000000..4ba7d3a8 --- /dev/null +++ b/modules/oauth/classes/Auth/ORM/External/OAuth2.php @@ -0,0 +1,86 @@ +$problem)); + } + + parent::__construct($oo); + + // Load the provider + $this->provider = OAuth2_Provider::factory($this->oo->name); + + // Load the client for this provider + $this->client = OAuth2_Client::factory(array( + 'id'=>$this->oo->app_id, + 'secret'=>$this->oo->secret, + )); + } + + /** + * Is user currently log in?? + * If we have a valid token, then we are. + */ + public function logged_in($role=NULL,$debug=NULL) { + // Attempt to complete signin + if ($code = Arr::get($_REQUEST,'code')) { + $state = Arr::get($_REQUEST,'state'); + if (! $state OR $state != Session::instance()->get_once('oauth.state')) + return FALSE; + + // Add the callback URL to the consumer + $this->client->callback(URL::site('oauth/login/'.$this->oo->name,TRUE)); + + // Exchange the authorization code for an access token + $this->token = $this->provider->access_token($this->client,$code); + Session::instance()->set('oauth.token',$this->token); + + // Otherwise try and get our token from our session. + } else { + $this->token = Session::instance()->get_once('oauth.token'); + } + + // @todo We need to check that the token is still valid. + return $this->token ? TRUE : FALSE; + } + + /** + * Our URL that starts the login process + */ + public function login_url() { + $state = sha1($this->oo->name.time()); + Session::instance()->set('oauth.state',$state); + + // Add the callback URL to the consumer + $this->client->callback(URL::site('oauth/login/'.$this->oo->name,TRUE)); + + // Get the login URL from the provider + return $this->provider->authorize_url($this->client,array('state'=>$state)); + } + + public function user_id() { + return $this->provider->user_details($this->client,array('access_token'=>$this->token))->id(); + } +} +?> diff --git a/modules/oauth/classes/Controller/Oauth.php b/modules/oauth/classes/Controller/Oauth.php index 135cb0ce..9cfa7b14 100644 --- a/modules/oauth/classes/Controller/Oauth.php +++ b/modules/oauth/classes/Controller/Oauth.php @@ -21,26 +21,12 @@ class Controller_Oauth extends Controller_TemplateDefault { if (! $oo->loaded() OR ! $oo->status) HTTP::redirect('login'); - $auth = NULL; + $auth = Auth::instance($oo); - if ($oo->name == 'facebook') { - // User Denied a Facebook authorisation, so we'll go back to login - // We wouldnt normally get here, since we are doing JS authentication - if ($this->request->query('error') AND $this->request->query('error_reason') == 'user_denied') - HTTP::redirect('login'); + if (! $auth->logged_in()) + HTTP::redirect($auth->login_url()); - $auth = Auth::instance($oo); - - // If we are not logged in, do the facebook stuff. - // We wouldnt normally get here, since we are doing JS authentication - if (! $auth->logged_in()) - HTTP::redirect($auth->login_url()); - - // Here we must be logged in to Facebook - // @todo Only use verified accounts - is this applicable? - - $aoo = $oo->account_oauth->where('userid','=',$auth->user_id())->find(); - } + $aoo = $oo->account_oauth->where('userid','=',$auth->user_id())->find(); // If we have an ID, we have been linked, redirect to login if ($aoo->loaded() AND $auth->login($aoo->account,$auth->user_id(),$auth)) diff --git a/modules/oauth/classes/Kohana/OAuth.php b/modules/oauth/classes/Kohana/OAuth.php new file mode 100644 index 00000000..42df6314 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth.php @@ -0,0 +1,278 @@ + TRUE, + * CURLOPT_POSTFIELDS => http_build_query($array), + * )); + * + * @param string remote URL + * @param array curl options + * @return string + * @throws Kohana_Exception + */ + public static function remote($url, array $options = NULL) + { + // The transfer must always be returned + $options[CURLOPT_RETURNTRANSFER] = TRUE; + + // Open a new remote connection + $remote = curl_init($url); + + // Set connection options + if ( ! curl_setopt_array($remote, $options)) + { + throw new Kohana_Exception('Failed to set CURL options, check CURL documentation: :url', + array(':url' => 'http://php.net/curl_setopt_array')); + } + + // Get the response + $response = curl_exec($remote); + + // Get the response information + $code = curl_getinfo($remote, CURLINFO_HTTP_CODE); + + if ($code AND $code < 200 OR $code > 299) + { + $error = $response; + } + elseif ($response === FALSE) + { + $error = curl_error($remote); + } + + // Close the connection + curl_close($remote); + + if (isset($error)) + { + throw new Kohana_OAuth_Request_Exception('Error fetching remote :url [ status :code ] :error', + array(':url' => $url, ':code' => $code, ':error' => $error), + $code, + $response); + } + + return $response; + } + /** + * RFC3986 compatible version of urlencode. Passing an array will encode + * all of the values in the array. Array keys will not be encoded. + * + * $input = OAuth::urlencode($input); + * + * Multi-dimensional arrays are not allowed! + * + * [!!] This method implements [OAuth 1.0 Spec 5.1](http://oauth.net/core/1.0/#rfc.section.5.1). + * + * @param mixed input string or array + * @return mixed + */ + public static function urlencode($input) + { + if (is_array($input)) + { + // Encode the values of the array + return array_map(array('OAuth', 'urlencode'), $input); + } + + // Encode the input + $input = rawurlencode($input); + + if (version_compare(PHP_VERSION, '<', '5.3')) + { + // rawurlencode() is RFC3986 compliant in PHP 5.3 + // the only difference is the encoding of tilde + $input = str_replace('%7E', '~', $input); + } + + return $input; + } + + /** + * RFC3986 complaint version of urldecode. Passing an array will decode + * all of the values in the array. Array keys will not be encoded. + * + * $input = OAuth::urldecode($input); + * + * Multi-dimensional arrays are not allowed! + * + * [!!] This method implements [OAuth 1.0 Spec 5.1](http://oauth.net/core/1.0/#rfc.section.5.1). + * + * @param mixed input string or array + * @return mixed + */ + public static function urldecode($input) + { + if (is_array($input)) + { + // Decode the values of the array + return array_map(array('OAuth', 'urldecode'), $input); + } + + // Decode the input + return rawurldecode($input); + } + + /** + * Normalize all request parameters into a string. + * + * $query = OAuth::normalize_params($params); + * + * [!!] This method implements [OAuth 1.0 Spec 9.1.1](http://oauth.net/core/1.0/#rfc.section.9.1.1). + * + * @param array request parameters + * @return string + * @uses OAuth::urlencode + */ + public static function normalize_params(array $params = NULL) + { + if ( ! $params) + { + // Nothing to do + return ''; + } + + // Encode the parameter keys and values + $keys = OAuth::urlencode(array_keys($params)); + $values = OAuth::urlencode(array_values($params)); + + // Recombine the parameters + $params = array_combine($keys, $values); + + // OAuth Spec 9.1.1 (1) + // "Parameters are sorted by name, using lexicographical byte value ordering." + uksort($params, 'strcmp'); + + // Create a new query string + $query = array(); + + foreach ($params as $name => $value) + { + if (is_array($value)) + { + // OAuth Spec 9.1.1 (1) + // "If two or more parameters share the same name, they are sorted by their value." + natsort($value); + + foreach ($value as $duplicate) + { + $query[] = $name.'='.$duplicate; + } + } + else + { + $query[] = $name.'='.$value; + } + } + + return implode('&', $query); + } + + /** + * Parse the query string out of the URL and return it as parameters. + * All GET parameters must be removed from the request URL when building + * the base string and added to the request parameters. + * + * // parsed parameters: array('oauth_key' => 'abcdef123456789') + * list($url, $params) = OAuth::parse_url('http://example.com/oauth/access?oauth_key=abcdef123456789'); + * + * [!!] This implements [OAuth Spec 9.1.1](http://oauth.net/core/1.0/#rfc.section.9.1.1). + * + * @param string URL to parse + * @return array (clean_url, params) + * @uses OAuth::parse_params + */ + public static function parse_url($url) + { + if ($query = parse_url($url, PHP_URL_QUERY)) + { + // Remove the query string from the URL + list($url) = explode('?', $url, 2); + + // Parse the query string as request parameters + $params = OAuth::parse_params($query); + } + else + { + // No parameters are present + $params = array(); + } + + return array($url, $params); + } + + /** + * Parse the parameters in a string and return an array. Duplicates are + * converted into indexed arrays. + * + * // Parsed: array('a' => '1', 'b' => '2', 'c' => '3') + * $params = OAuth::parse_params('a=1,b=2,c=3'); + * + * // Parsed: array('a' => array('1', '2'), 'c' => '3') + * $params = OAuth::parse_params('a=1,a=2,c=3'); + * + * @param string parameter string + * @return array + */ + public static function parse_params($params) + { + // Split the parameters by & + $params = explode('&', trim($params)); + + // Create an array of parsed parameters + $parsed = array(); + + foreach ($params as $param) + { + // Split the parameter into name and value + list($name, $value) = explode('=', $param, 2); + + // Decode the name and value + $name = OAuth::urldecode($name); + $value = OAuth::urldecode($value); + + if (isset($parsed[$name])) + { + if ( ! is_array($parsed[$name])) + { + // Convert the parameter to an array + $parsed[$name] = array($parsed[$name]); + } + + // Add a new duplicate parameter + $parsed[$name][] = $value; + } + else + { + // Add a new parameter + $parsed[$name] = $value; + } + } + + return $parsed; + } + +} // End OAuth diff --git a/modules/oauth/classes/Kohana/OAuth/API.php b/modules/oauth/classes/Kohana/OAuth/API.php new file mode 100644 index 00000000..365c860b --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/API.php @@ -0,0 +1,85 @@ +name); + + return new $class($options); + } + + /** + * @var string required parameters + */ + protected $required = array( + 'profile', + ); + + /** + * Sets the API data. + * + * @param array API options + * @return void + */ + public function __construct(array $options = NULL) + { + foreach ($this->required as $key) + { + if ( ! isset($options[$key])) + { + throw new Kohana_OAuth_Exception('Required option not passed: :option', + array(':option' => $key)); + } + + $this->$key = $options[$key]; + } + } + + /** + * Return the value of any protected class variable. + * + * // Get the API details + * $secret = $api->id; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + /** + * Returns the API key. + * + * @return string + */ + public function __toString() + { + return (string) $this->profile; + } + +} // End OAuth_API diff --git a/modules/oauth/classes/Kohana/OAuth/API/Profile/Linkedin.php b/modules/oauth/classes/Kohana/OAuth/API/Profile/Linkedin.php new file mode 100644 index 00000000..ce94a0ea --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/API/Profile/Linkedin.php @@ -0,0 +1,17 @@ +profile->id; + } +} +?> diff --git a/modules/oauth/classes/Kohana/OAuth/Consumer.php b/modules/oauth/classes/Kohana/OAuth/Consumer.php new file mode 100644 index 00000000..11bf0290 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Consumer.php @@ -0,0 +1,109 @@ + 'key')); + } + + if ( ! isset($options['secret'])) + { + throw new Kohana_OAuth_Exception('Required option not passed: :option', + array(':option' => 'secret')); + } + + $this->key = $options['key']; + + $this->secret = $options['secret']; + + if (isset($options['callback'])) + { + $this->callback = $options['callback']; + } + + if (isset($options['realm'])) + { + $this->realm = $options['realm']; + } + } + + /** + * Return the value of any protected class variable. + * + * // Get the consumer key + * $key = $consumer->key; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + /** + * Change the consumer callback. + * + * @param string new consumer callback + * @return $this + */ + public function callback($callback) + { + $this->callback = $callback; + + return $this; + } + +} // End OAuth_Consumer diff --git a/modules/oauth/classes/Kohana/OAuth/Exception.php b/modules/oauth/classes/Kohana/OAuth/Exception.php new file mode 100644 index 00000000..7953dcef --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Exception.php @@ -0,0 +1,12 @@ +signature = $options['signature']; + } + + if ( ! is_object($this->signature)) + { + // Convert the signature name into an object + $this->signature = OAuth_Signature::factory($this->signature); + } + + if ( ! $this->name) + { + // Attempt to guess the name from the class name + $this->name = strtolower(substr(get_class($this), strlen('OAuth_Provider_'))); + } + } + + /** + * Return the value of any protected class variable. + * + * // Get the provider signature + * $signature = $provider->signature; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + /** + * Returns the request token URL for the provider. + * + * $url = $provider->url_request_token(); + * + * @return string + */ + abstract public function url_request_token(); + + /** + * Returns the authorization URL for the provider. + * + * $url = $provider->url_authorize(); + * + * @return string + */ + abstract public function url_authorize(); + + /** + * Returns the access token endpoint for the provider. + * + * $url = $provider->url_access_token(); + * + * @return string + */ + abstract public function url_access_token(); + + /** + * Ask for a request token from the OAuth provider. + * + * $token = $provider->request_token($consumer); + * + * @param OAuth_Consumer consumer + * @param array additional request parameters + * @return OAuth_Token_Request + * @uses OAuth_Request_Token + */ + public function request_token(OAuth_Consumer $consumer, array $params = NULL) + { + // Create a new GET request for a request token with the required parameters + $request = OAuth_Request::factory('token', 'GET', $this->url_request_token(), array( + 'realm' => $consumer->realm, + 'oauth_consumer_key' => $consumer->key, + 'oauth_callback' => $consumer->callback, + )); + + if ($params) + { + // Load user parameters + $request->params($params); + } + + // Sign the request using only the consumer, no token is available yet + $request->sign($this->signature, $consumer); + + // Create a response from the request + $response = $request->execute(); + + // Store this token somewhere useful + return OAuth_Token::factory('request', array( + 'token' => $response->param('oauth_token'), + 'secret' => $response->param('oauth_token_secret'), + )); + } + + /** + * Get the authorization URL for the request token. + * + * $this->request->redirect($provider->authorize_url($token)); + * + * @param OAuth_Token_Request token + * @param array additional request parameters + * @return string + */ + public function authorize_url(OAuth_Token_Request $token, array $params = NULL) + { + // Create a new GET request for a request token with the required parameters + $request = OAuth_Request::factory('authorize', 'GET', $this->url_authorize(), array( + 'oauth_token' => $token->token, + 'scope' => $this->scope, + )); + + if ($params) + { + // Load user parameters + $request->params($params); + } + + return $request->as_url(); + } + + /** + * Exchange the request token for an access token. + * + * $token = $provider->access_token($consumer, $token); + * + * @param OAuth_Consumer consumer + * @param OAuth_Token_Request token + * @param array additional request parameters + * @return OAuth_Token_Access + */ + public function access_token(OAuth_Consumer $consumer, OAuth_Token_Request $token, array $params = NULL) + { + // Create a new GET request for a request token with the required parameters + $request = OAuth_Request::factory('access', 'GET', $this->url_access_token(), array( + 'realm' => $consumer->realm, + 'oauth_consumer_key' => $consumer->key, + 'oauth_token' => $token->token, + 'oauth_verifier' => $token->verifier, + )); + + if ($params) + { + // Load user parameters + $request->params($params); + } + + // Sign the request using only the consumer, no token is available yet + $request->sign($this->signature, $consumer, $token); + + // Create a response from the request + $response = $request->execute(); + + // Store this token somewhere useful + return OAuth_Token::factory('access', array( + 'token' => $response->param('oauth_token'), + 'secret' => $response->param('oauth_token_secret'), + )); + } + + public function user_details(OAuth_Consumer $consumer, OAuth_Token $token, array $params = NULL) + { + // Create a new GET request for a request token with the required parameters + $request = OAuth_Request::factory('resource', 'GET', $this->url_user_details(), array( + 'oauth_consumer_key' => $consumer->key, + 'oauth_token' => $token->token, + )); + + if ($params) + { + // Load user parameters + $request->params($params); + } + + // Sign the request using only the consumer, no token is available yet + $request->sign($this->signature, $consumer, $token); + + // Create a response from the request + $response = $request->execute(); + + // Store these user details useful + return OAuth_API::factory($this, 'profile', array( + 'provider' => $this->name, + 'profile' => json_decode($response), + )); + } + +} // End OAuth_Signature diff --git a/modules/oauth/classes/Kohana/OAuth/Provider/Linkedin.php b/modules/oauth/classes/Kohana/OAuth/Provider/Linkedin.php new file mode 100644 index 00000000..a3bec1d0 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Provider/Linkedin.php @@ -0,0 +1,78 @@ +. + * + * [!!] This class does not implement the Linkedin API. It is only an + * implementation of standard OAuth with Linkedin as the service provider. + * + * @package Kohana/OAuth + * @category Provider + * @author Kohana Team + * @copyright (c) 2010 Kohana Team + * @license http://kohanaframework.org/license + * @author Deon George + * @copyright (c) 2009-2013 Deon George + * @license http://dev.leenooks.net/license.html + * @since 3.0.7 + */ +class Kohana_OAuth_Provider_Linkedin extends OAuth_Provider { + + /** + * @var string Provider name + */ + public $name = 'linkedin'; + + /** + * @var string Signature + */ + protected $signature = 'HMAC-SHA1'; + + /** + * @var string Our API Items that we want to access + */ + protected $scope = 'r_basicprofile+r_emailaddress'; + + /** + * Request token URL + * + * @return string + */ + public function url_request_token() + { + return 'https://api.linkedin.com/uas/oauth/requestToken'; + } + + /** + * Authorize URL + * + * @return string + */ + public function url_authorize() + { + return 'https://api.linkedin.com/uas/oauth/authorize'; + } + + /** + * Access token URL + * + * @return string + */ + public function url_access_token() + { + return 'https://api.linkedin.com/uas/oauth/accessToken'; + } + + /** + * Access to Profile Details URL + * + * @return string + */ + public function url_user_details() + { + return 'https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address)?format=json'; + } + +} // End Kohana_OAuth_Provider_Linkedin diff --git a/modules/oauth/classes/Kohana/OAuth/Request.php b/modules/oauth/classes/Kohana/OAuth/Request.php new file mode 100644 index 00000000..9f7da7a7 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Request.php @@ -0,0 +1,541 @@ +method = strtoupper($method); + } + + // Separate the URL and query string, which will be used as additional + // default parameters + list ($url, $default) = OAuth::parse_url($url); + + // Set the request URL + $this->url = $url; + + if ($default) + { + // Set the default parameters + $this->params($default); + } + + if ($params) + { + // Set the request parameters + $this->params($params); + } + + if ($this->required('oauth_version') AND ! isset($this->params['oauth_version'])) + { + // Set the version of this request + $this->params['oauth_version'] = OAuth::$version; + } + + if ($this->required('oauth_timestamp') AND ! isset($this->params['oauth_timestamp'])) + { + // Set the timestamp of this request + $this->params['oauth_timestamp'] = $this->timestamp(); + } + + if ($this->required('oauth_nonce') AND ! isset($this->params['oauth_nonce'])) + { + // Set the unique nonce of this request + $this->params['oauth_nonce'] = $this->nonce(); + } + } + + /** + * Return the value of any protected class variable. + * + * // Get the request parameters + * $params = $request->params; + * + * // Get the request URL + * $url = $request->url; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + /** + * Generates the UNIX timestamp for a request. + * + * $time = $request->timestamp(); + * + * [!!] This method implements [OAuth 1.0 Spec 8](http://oauth.net/core/1.0/#rfc.section.8). + * + * @return integer + */ + public function timestamp() + { + return time(); + } + + /** + * Generates the nonce for a request. + * + * $nonce = $request->nonce(); + * + * [!!] This method implements [OAuth 1.0 Spec 8](http://oauth.net/core/1.0/#rfc.section.8). + * + * @return string + * @uses Text::random + */ + public function nonce() + { + return Text::random('alnum', 40); + } + + /** + * Get the base signature string for a request. + * + * $base = $request->base_string(); + * + * [!!] This method implements [OAuth 1.0 Spec A5.1](http://oauth.net/core/1.0/#rfc.section.A.5.1). + * + * @param OAuth_Request request to sign + * @return string + * @uses OAuth::urlencode + * @uses OAuth::normalize_params + */ + public function base_string() + { + $url = $this->url; + + // Get the request parameters + $params = array_diff_key($this->params, $this->upload); + + // "oauth_signature" is never included in the base string! + unset($params['oauth_signature']); + + // method & url & sorted-parameters + return implode('&', array( + $this->method, + OAuth::urlencode($url), + OAuth::urlencode(OAuth::normalize_params($params)), + )); + } + + /** + * Parameter getter and setter. Setting the value to `NULL` will remove it. + * + * // Set the "oauth_consumer_key" to a new value + * $request->param('oauth_consumer_key', $key); + * + * // Get the "oauth_consumer_key" value + * $key = $request->param('oauth_consumer_key'); + * + * @param string parameter name + * @param mixed parameter value + * @param boolean allow duplicates? + * @return mixed when getting + * @return $this when setting + * @uses Arr::get + */ + public function param($name, $value = NULL, $duplicate = FALSE) + { + if ($value === NULL) + { + // Get the parameter + return Arr::get($this->params, $name); + } + + if (isset($this->params[$name]) AND $duplicate) + { + if ( ! is_array($this->params[$name])) + { + // Convert the parameter into an array + $this->params[$name] = array($this->params[$name]); + } + + // Add the duplicate value + $this->params[$name][] = $value; + } + else + { + // Set the parameter value + $this->params[$name] = $value; + } + + return $this; + } + + /** + * Set multiple parameters. + * + * $request->params($params); + * + * @param array parameters + * @param boolean allow duplicates? + * @return $this + * @uses OAuth_Request::param + */ + public function params(array $params, $duplicate = FALSE) + { + foreach ($params as $name => $value) + { + $this->param($name, $value, $duplicate); + } + + return $this; + } + + public function body($body) + { + $this->body = $body; + + return $this; + } + + /** + * Upload getter and setter. Setting the value to `NULL` will remove it. + * + * // Set the "image" file path for uploading + * $request->upload('image', $file_path); + * + * // Get the "image" file path + * $key = $request->param('oauth_consumer_key'); + * + * @param string upload name + * @param mixed upload file path + * @return mixed when getting + * @return $this when setting + * @uses OAuth_Request::param + */ + public function upload($name, $value = NULL) + { + if ($value !== NULL) + { + // This is an upload parameter + $this->upload[$name] = TRUE; + + // Get the mime type of the image + $mime = File::mime($value); + + // Format the image path for CURL + $value = "@{$value};type={$mime}"; + } + + return $this->param($name, $value, FALSE); + } + + /** + * Get and set required parameters. + * + * $request->required($field, $value); + * + * @param string parameter name + * @param boolean field value + * @return boolean when getting + * @return $this when setting + */ + public function required($param, $value = NULL) + { + if ($value === NULL) + { + // Get the current status + return ! empty($this->required[$param]); + } + + // Change the requirement value + $this->required[$param] = (boolean) $value; + + return $this; + } + + /** + * Convert the request parameters into an `Authorization` header. + * + * $header = $request->as_header(); + * + * [!!] This method implements [OAuth 1.0 Spec 5.4.1](http://oauth.net/core/1.0/#rfc.section.5.4.1). + * + * @return string + */ + public function as_header() + { + $header = array(); + + // Check for the existance of "realm" + if(array_key_exists('realm', $this->params) and ! empty($this->params['realm'])) + { + // OAuth Spec 5.4.1 + // "Parameter names and values are encoded per Parameter Encoding [RFC 3986]." + $header[] = OAuth::urlencode('realm').'="'.OAuth::urlencode($this->params['realm']).'"'; + } + + foreach ($this->params as $name => $value) + { + if (strpos($name, 'oauth_') === 0) + { + // OAuth Spec 5.4.1 + // "Parameter names and values are encoded per Parameter Encoding [RFC 3986]." + $header[] = OAuth::urlencode($name).'="'.OAuth::urlencode($value).'"'; + } + } + + return $header ? 'OAuth '.implode(', ', $header) : NULL; + } + + /** + * Convert the request parameters into a query string, suitable for GET and + * POST requests. + * + * $query = $request->as_query(); + * + * [!!] This method implements [OAuth 1.0 Spec 5.2 (2,3)](http://oauth.net/core/1.0/#rfc.section.5.2). + * + * @param boolean include oauth parameters? + * @param boolean return a normalized string? + * @return string + */ + public function as_query($include_oauth = NULL, $as_string = TRUE) + { + if ($include_oauth === NULL) + { + // If we are sending a header, OAuth parameters should not be + // included in the query string. + $include_oauth = ! $this->send_header; + } + + if ($include_oauth) + { + $params = $this->params; + } + else + { + $params = array(); + foreach ($this->params as $name => $value) + { + if ( ! preg_match($this->auth_params, $name)) + { + // This is not an OAuth parameter + $params[$name] = $value; + } + } + } + + return $as_string ? OAuth::normalize_params($params) : $params; + } + + /** + * Return the entire request URL with the parameters as a GET string. + * + * $url = $request->as_url(); + * + * @return string + * @uses OAuth_Request::as_query + */ + public function as_url() + { + return $this->url.'?'.$this->as_query(TRUE); + } + + /** + * Sign the request, setting the `oauth_signature_method` and `oauth_signature`. + * + * @param OAuth_Signature signature + * @param OAuth_Consumer consumer + * @param OAuth_Token token + * @return $this + * @uses OAuth_Signature::sign + */ + public function sign(OAuth_Signature $signature, OAuth_Consumer $consumer, OAuth_Token $token = NULL) + { + // Create a new signature class from the method + $this->param('oauth_signature_method', $signature->name); + + // Sign the request using the consumer and token + $this->param('oauth_signature', $signature->sign($this, $consumer, $token)); + + return $this; + } + + /** + * Checks that all required request parameters have been set. Throws an + * exception if any parameters are missing. + * + * try + * { + * $request->check(); + * } + * catch (OAuth_Exception $e) + * { + * // Request has missing parameters + * } + * + * @return TRUE + * @throws Kohana_OAuth_Exception + */ + public function check() + { + foreach ($this->required as $param => $required) + { + if ($required AND ! isset($this->params[$param])) + { + throw new Kohana_OAuth_Exception('Request to :url requires missing parameter ":param"', array( + ':url' => $this->url, + ':param' => $param, + )); + } + } + + return TRUE; + } + + /** + * Execute the request and return a response. + * + * @param array additional cURL options + * @return string request response body + * @uses OAuth_Request::check + * @uses Arr::get + * @uses Remote::get + */ + public function execute(array $options = NULL) + { + // Check that all required fields are set + $this->check(); + + // Get the URL of the request + $url = $this->url; + + if ($query = $this->as_query()) + { + // Append the parameters to the query string + $url = "{$url}?{$query}"; + } + + if ( ! isset($options[CURLOPT_CONNECTTIMEOUT])) + { + // Use the request default timeout + $options[CURLOPT_CONNECTTIMEOUT] = $this->timeout; + } + + if ($this->send_header AND $header = $this->as_header()) + { + // Store the new headers + $options[CURLOPT_HTTPHEADER][] = 'Authorization: '.$header; + } + + // Set the request method for this request + $options[CURLOPT_CUSTOMREQUEST] = $this->method; + + if ($this->body) + { + $options[CURLOPT_POSTFIELDS] = $this->body; + } + elseif ($this->method === 'POST') + { + if ($post = $this->as_query(empty($header), empty($this->upload))) + { + // Attach the post fields to the request + $options[CURLOPT_POSTFIELDS] = $post; + } + } + + if (! is_null($this->body)) + { + $options[CURLOPT_HTTPHEADER][] = 'Content-Length: '.strlen($this->body); + } + + return OAuth::remote($url, $options); + } + +} // End OAuth_Request diff --git a/modules/oauth/classes/Kohana/OAuth/Request/Access.php b/modules/oauth/classes/Kohana/OAuth/Request/Access.php new file mode 100644 index 00000000..629abf5a --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Request/Access.php @@ -0,0 +1,32 @@ + TRUE, + 'oauth_token' => TRUE, + 'oauth_signature_method' => TRUE, + 'oauth_signature' => TRUE, + 'oauth_timestamp' => TRUE, + 'oauth_nonce' => TRUE, + 'oauth_verifier' => TRUE, + 'oauth_version' => TRUE, + ); + + public function execute(array $options = NULL) + { + return OAuth_Response::factory(parent::execute($options)); + } + +} // End OAuth_Request_Access diff --git a/modules/oauth/classes/Kohana/OAuth/Request/Authorize.php b/modules/oauth/classes/Kohana/OAuth/Request/Authorize.php new file mode 100644 index 00000000..2ce9037c --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Request/Authorize.php @@ -0,0 +1,26 @@ + TRUE, + ); + + public function execute(array $options = NULL) + { + return Request::current()->redirect($this->as_url()); + } + +} // End OAuth_Request_Authorize diff --git a/modules/oauth/classes/Kohana/OAuth/Request/Exception.php b/modules/oauth/classes/Kohana/OAuth/Request/Exception.php new file mode 100644 index 00000000..0f2172a1 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Request/Exception.php @@ -0,0 +1,27 @@ +body = $body; + + return parent::__construct($message, $variables, $code); + } + + /** + * Get the response body. + * + * @return string + */ + public function getBody() + { + return $this->body; + } + +} diff --git a/modules/oauth/classes/Kohana/OAuth/Request/Resource.php b/modules/oauth/classes/Kohana/OAuth/Request/Resource.php new file mode 100644 index 00000000..26220d16 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Request/Resource.php @@ -0,0 +1,27 @@ + TRUE, + 'oauth_token' => TRUE, + 'oauth_signature_method' => TRUE, + 'oauth_signature' => TRUE, + 'oauth_timestamp' => TRUE, + 'oauth_nonce' => TRUE, + 'oauth_version' => TRUE, + ); + +} // End OAuth_Request_Resource diff --git a/modules/oauth/classes/Kohana/OAuth/Request/Token.php b/modules/oauth/classes/Kohana/OAuth/Request/Token.php new file mode 100644 index 00000000..267a3cca --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Request/Token.php @@ -0,0 +1,32 @@ + TRUE, + 'oauth_consumer_key' => TRUE, + 'oauth_signature_method' => TRUE, + 'oauth_signature' => TRUE, + 'oauth_timestamp' => TRUE, + 'oauth_nonce' => TRUE, + 'oauth_version' => TRUE, + ); + + public function execute(array $options = NULL) + { + return OAuth_Response::factory(parent::execute($options)); + } + +} // End OAuth_Request_Token diff --git a/modules/oauth/classes/Kohana/OAuth/Response.php b/modules/oauth/classes/Kohana/OAuth/Response.php new file mode 100644 index 00000000..80700e4d --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Response.php @@ -0,0 +1,65 @@ +params = $params; + } + else + { + // parse as GET string + $this->params = OAuth::parse_params($body); + } + } + } + + /** + * Return the value of any protected class variable. + * + * // Get the response parameters + * $params = $response->params; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + public function param($name, $default = NULL) + { + return Arr::get($this->params, $name, $default); + } + + public function params() + { + return $this->params; + } + +} // End OAuth_Response diff --git a/modules/oauth/classes/Kohana/OAuth/Signature.php b/modules/oauth/classes/Kohana/OAuth/Signature.php new file mode 100644 index 00000000..52cf1711 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Signature.php @@ -0,0 +1,77 @@ +name; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + /** + * Get a signing key from a consumer and token. + * + * $key = $signature->key($consumer, $token); + * + * [!!] This method implements the signing key of [OAuth 1.0 Spec 9](http://oauth.net/core/1.0/#rfc.section.9). + * + * @param OAuth_Consumer consumer + * @param OAuth_Token token + * @return string + * @uses OAuth::urlencode + */ + public function key(OAuth_Consumer $consumer, OAuth_Token $token = NULL) + { + $key = OAuth::urlencode($consumer->secret).'&'; + + if ($token) + { + $key .= OAuth::urlencode($token->secret); + } + + return $key; + } + + abstract public function sign(OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL); + + abstract public function verify($signature, OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL); + +} // End OAuth_Signature diff --git a/modules/oauth/classes/Kohana/OAuth/Signature/HMAC/SHA1.php b/modules/oauth/classes/Kohana/OAuth/Signature/HMAC/SHA1.php new file mode 100644 index 00000000..d77ee20c --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Signature/HMAC/SHA1.php @@ -0,0 +1,68 @@ +sign($request, $consumer, $token); + * + * [!!] This method implements [OAuth 1.0 Spec 9.2.1](http://oauth.net/core/1.0/#rfc.section.9.2.1). + * + * @param OAuth_Request request + * @param OAuth_Consumer consumer + * @param OAuth_Token token + * @return string + * @uses OAuth_Signature::key + * @uses OAuth_Request::base_string + */ + public function sign(OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL) + { + // Get the signing key + $key = $this->key($consumer, $token); + + // Get the base string for the signature + $base_string = $request->base_string(); + + // Sign the base string using the key + return base64_encode(hash_hmac('sha1', $base_string, $key, TRUE)); + } + + /** + * Verify a HMAC-SHA1 signature. + * + * if ( ! $signature->verify($signature, $request, $consumer, $token)) + * { + * throw new Kohana_OAuth_Exception('Failed to verify signature'); + * } + * + * [!!] This method implements [OAuth 1.0 Spec 9.2.2](http://oauth.net/core/1.0/#rfc.section.9.2.2). + * + * @param string signature to verify + * @param OAuth_Request request + * @param OAuth_Consumer consumer + * @param OAuth_Token token + * @return boolean + * @uses OAuth_Signature_HMAC_SHA1::sign + */ + public function verify($signature, OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL) + { + return $signature === $this->sign($request, $consumer, $token); + } + +} // End OAuth_Signature_HMAC_SHA1 diff --git a/modules/oauth/classes/Kohana/OAuth/Signature/Plaintext.php b/modules/oauth/classes/Kohana/OAuth/Signature/Plaintext.php new file mode 100644 index 00000000..db5a1125 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Signature/Plaintext.php @@ -0,0 +1,57 @@ +sign($request, $consumer, $token); + * + * [!!] This method implements [OAuth 1.0 Spec 9.4.1](http://oauth.net/core/1.0/#rfc.section.9.4.1). + * + * @param OAuth_Request request + * @param OAuth_Consumer consumer + * @param OAuth_Token token + * @return $this + */ + public function sign(OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL) + { + // Use the signing key as the signature + return $this->key($consumer, $token); + } + + /** + * Verify a plaintext signature. + * + * if ( ! $signature->verify($signature, $request, $consumer, $token)) + * { + * throw new Kohana_OAuth_Exception('Failed to verify signature'); + * } + * + * [!!] This method implements [OAuth 1.0 Spec 9.4.2](http://oauth.net/core/1.0/#rfc.section.9.4.2). + * + * @param string signature to verify + * @param OAuth_Request request + * @param OAuth_Consumer consumer + * @param OAuth_Token token + * @return boolean + * @uses OAuth_Signature_PLAINTEXT::sign + */ + public function verify($signature, OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL) + { + return $signature === $this->key($consumer, $token); + } + +} // End OAuth_Signature_PLAINTEXT diff --git a/modules/oauth/classes/Kohana/OAuth/Token.php b/modules/oauth/classes/Kohana/OAuth/Token.php new file mode 100644 index 00000000..69d8b50f --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Token.php @@ -0,0 +1,94 @@ +required as $key) + { + if ( ! isset($options[$key])) + { + throw new Kohana_OAuth_Exception('Required option not passed: :option', + array(':option' => $key)); + } + + $this->$key = $options[$key]; + } + } + + /** + * Return the value of any protected class variable. + * + * // Get the token secret + * $secret = $token->secret; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + /** + * Returns the token key. + * + * @return string + */ + public function __toString() + { + return (string) $this->token; + } + +} // End OAuth_Token diff --git a/modules/oauth/classes/Kohana/OAuth/Token/Access.php b/modules/oauth/classes/Kohana/OAuth/Token/Access.php new file mode 100644 index 00000000..7ad811e5 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth/Token/Access.php @@ -0,0 +1,26 @@ +verifier($key); + * + * @param string new verifier + * @return $this + */ + public function verifier($verifier) + { + $this->verifier = $verifier; + + return $this; + } + +} // End OAuth_Token_Request diff --git a/modules/oauth/classes/Kohana/OAuth2.php b/modules/oauth/classes/Kohana/OAuth2.php new file mode 100644 index 00000000..4f363f5d --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2.php @@ -0,0 +1,48 @@ +name); + + return new $class($options); + } + +} // End OAuth2_API +?> diff --git a/modules/oauth/classes/Kohana/OAuth2/API/Profile/Facebook.php b/modules/oauth/classes/Kohana/OAuth2/API/Profile/Facebook.php new file mode 100644 index 00000000..c756b95b --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/API/Profile/Facebook.php @@ -0,0 +1,17 @@ +profile->id; + } +} +?> diff --git a/modules/oauth/classes/Kohana/OAuth2/API/Profile/Google.php b/modules/oauth/classes/Kohana/OAuth2/API/Profile/Google.php new file mode 100644 index 00000000..96b081eb --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/API/Profile/Google.php @@ -0,0 +1,17 @@ +profile->id; + } +} +?> diff --git a/modules/oauth/classes/Kohana/OAuth2/Client.php b/modules/oauth/classes/Kohana/OAuth2/Client.php new file mode 100644 index 00000000..cdd68911 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/Client.php @@ -0,0 +1,98 @@ + 'id')); + } + + if ( ! isset($options['secret'])) + { + throw new Kohana_OAuth_Exception('Required option not passed: :option', + array(':option' => 'secret')); + } + + $this->id = $options['id']; + + $this->secret = $options['secret']; + + if (isset($options['callback'])) + { + $this->callback = $options['callback']; + } + } + + /** + * Return the value of any protected class variable. + * + * // Get the client key + * $key = $client->key; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + /** + * Change the client callback. + * + * @param string new consumer callback + * @return $this + */ + public function callback($callback) + { + $this->callback = $callback; + + return $this; + } + +} diff --git a/modules/oauth/classes/Kohana/OAuth2/Provider.php b/modules/oauth/classes/Kohana/OAuth2/Provider.php new file mode 100644 index 00000000..b3b3ad20 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/Provider.php @@ -0,0 +1,127 @@ +signature; + * + * @param string variable name + * @return mixed + */ + public function __get($key) + { + return $this->$key; + } + + abstract public function url_authorize(); + + abstract public function url_access_token(); + + public $name; + + protected $scope; + + public function url_refresh_token() + { + // By default its the same as access token URL + return $this->url_access_token(); + } + + public function authorize_url(OAuth2_Client $client, array $params = NULL) + { + // Create a new GET request for a request token with the required parameters + $request = OAuth2_Request::factory('authorize', 'GET', $this->url_authorize(), array( + 'response_type' => 'code', + 'client_id' => $client->id, + 'redirect_uri' => $client->callback, + 'scope' => $this->scope, + )); + + if ($params) + { + // Load user parameters + $request->params($params); + } + + return $request->as_url(); + } + + public function access_token(OAuth2_Client $client, $code, array $params = NULL) + { + $request = OAuth2_Request::factory('token', 'POST', $this->url_access_token(), array( + 'grant_type' => 'authorization_code', + 'code' => $code, + 'client_id' => $client->id, + 'client_secret' => $client->secret, + )); + + if ($client->callback) + { + $request->param('redirect_uri', $client->callback); + } + + if ($params) + { + // Load user parameters + $request->params($params); + } + + $response = $request->execute(); + + return OAuth2_Token::factory('access', array( + 'token' => $response->param('access_token') + )); + } + + public function user_details(OAuth2_Client $client, array $params = NULL) { + $request = OAuth2_Request::factory('resource', 'GET', $this->url_user_details(), array( + )); + + if ($params) + { + // Load user parameters + $request->params($params); + } + + // Create a response from the request + $response = $request->execute(); + + // Store these user details useful + return OAuth2_API::factory($this, 'profile', array( + 'provider' => $this->name, + 'profile' => json_decode($response), + )); + } + + /** + * Execute an OAuth2 request, apply any provider-specfic options to the request. + * + * @param object request object + * @param array request options + * @return mixed + */ + public function execute(OAuth2_Request $request, array $options = NULL) + { + return $request->execute($options); + } + +} diff --git a/modules/oauth/classes/Kohana/OAuth2/Provider/Facebook.php b/modules/oauth/classes/Kohana/OAuth2/Provider/Facebook.php new file mode 100644 index 00000000..96fde7ff --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/Provider/Facebook.php @@ -0,0 +1,32 @@ +. + * Individual Google APIs have separate documentation. A complete list is + * available at . + * + * [!!] This class does not implement any Google API. It is only an + * implementation of standard OAuth with Google as the service provider. + * + * @package Kohana/OAuth2 + * @category Provider + * @author Kohana Team + * @copyright (c) 2010 Kohana Team + * @license http://kohanaframework.org/license + * @author Deon George + * @copyright (c) 2009-2013 Deon George + * @license http://dev.leenooks.net/license.html + * @since 3.0.7 + */ +class Kohana_OAuth2_Provider_Google extends OAuth2_Provider { + + public $name = 'google'; + + protected $scope = 'openid email'; + + public function url_authorize() + { + return 'https://accounts.google.com/o/oauth2/auth'; + } + + public function url_access_token() + { + return 'https://accounts.google.com/o/oauth2/token'; + } + + public function url_user_details() + { + return 'https://www.googleapis.com/oauth2/v2/userinfo'; + } + +} // End OAuth_Provider_Google diff --git a/modules/oauth/classes/Kohana/OAuth2/Request.php b/modules/oauth/classes/Kohana/OAuth2/Request.php new file mode 100644 index 00000000..d00c1431 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/Request.php @@ -0,0 +1,67 @@ +as_header(); + * + * [!!] This method implements [OAuth 2.0 v22 Spec 7.1](http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-7.1). + * + * @return string + */ + public function as_header() + { + if ($access = Arr::get($this->params, 'access_token')) + { + if (is_string($this->send_header)) + { + $header = $this->send_header; + } + else + { + $header = 'Bearer'; + } + + $access = $header.' '.$access; + } + + return $access ? $access : NULL; + } + +} diff --git a/modules/oauth/classes/Kohana/OAuth2/Request/Authorize.php b/modules/oauth/classes/Kohana/OAuth2/Request/Authorize.php new file mode 100644 index 00000000..6e2a2e5d --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/Request/Authorize.php @@ -0,0 +1,20 @@ +redirect($this->as_url()); + } + +} diff --git a/modules/oauth/classes/Kohana/OAuth2/Request/Resource.php b/modules/oauth/classes/Kohana/OAuth2/Request/Resource.php new file mode 100644 index 00000000..20ee0267 --- /dev/null +++ b/modules/oauth/classes/Kohana/OAuth2/Request/Resource.php @@ -0,0 +1,19 @@ +name); + $c = 'OAuth_Plugin_'.ucfirst($this->name); if (! $this->name OR ! class_exists($c)) return NULL; diff --git a/modules/oauth/classes/OAuth.php b/modules/oauth/classes/OAuth.php new file mode 100644 index 00000000..f73d3480 --- /dev/null +++ b/modules/oauth/classes/OAuth.php @@ -0,0 +1,3 @@ +oo = $oo; diff --git a/modules/oauth/classes/Oauth/Plugin/Facebook.php b/modules/oauth/classes/OAuth/Plugin/Facebook.php similarity index 92% rename from modules/oauth/classes/Oauth/Plugin/Facebook.php rename to modules/oauth/classes/OAuth/Plugin/Facebook.php index 2f9624f4..e21198f2 100644 --- a/modules/oauth/classes/Oauth/Plugin/Facebook.php +++ b/modules/oauth/classes/OAuth/Plugin/Facebook.php @@ -10,7 +10,7 @@ * @license http://dev.osbill.net/license.html */ -class Oauth_Plugin_Facebook extends Oauth_Plugin { +class OAuth_Plugin_Facebook extends OAuth_Plugin { public function html() { // @todo Needs to work with https Script::factory() @@ -34,7 +34,7 @@ $(document).ready(function(){ //FB.getLoginStatus(checkLoginStatus); } - $(".fb-login").click(function() { + $(".btn-facebook").click(function() { FB.login(checkLoginStatus, {scope:"email"}); // We stop the click, but pick up the href in the javascript diff --git a/modules/oauth/classes/OAuth/Plugin/Google.php b/modules/oauth/classes/OAuth/Plugin/Google.php new file mode 100644 index 00000000..e60281b5 --- /dev/null +++ b/modules/oauth/classes/OAuth/Plugin/Google.php @@ -0,0 +1,17 @@ + diff --git a/modules/oauth/classes/OAuth/Plugin/Linkedin.php b/modules/oauth/classes/OAuth/Plugin/Linkedin.php new file mode 100644 index 00000000..561f56f9 --- /dev/null +++ b/modules/oauth/classes/OAuth/Plugin/Linkedin.php @@ -0,0 +1,17 @@ + diff --git a/modules/oauth/classes/OAuth/Provider.php b/modules/oauth/classes/OAuth/Provider.php new file mode 100644 index 00000000..99d7e406 --- /dev/null +++ b/modules/oauth/classes/OAuth/Provider.php @@ -0,0 +1,3 @@ +