Repository: airavata-php-gateway Updated Branches: refs/heads/master 088dfff02 -> cc8308999
Adding OAuth request_code grant flow support for PGA Project: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/repo Commit: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/commit/cc830899 Tree: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/tree/cc830899 Diff: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/diff/cc830899 Branch: refs/heads/master Commit: cc83089998a0c33cd5bf713f086b8220545f0dcb Parents: 088dfff Author: Supun Nakandala <[email protected]> Authored: Thu Sep 3 19:31:33 2015 +0530 Committer: Supun Nakandala <[email protected]> Committed: Thu Sep 3 19:31:33 2015 +0530 ---------------------------------------------------------------------- app/config/pga_config.php.template | 24 ++- app/controllers/AccountController.php | 54 ++++++- app/filters.php | 23 ++- app/libraries/Airavata/API/Airavata.php | 42 +----- app/libraries/Wsis/Stubs/OAuthManager.php | 147 +++++++++++++++++++ app/libraries/Wsis/Stubs/UserProfileManager.php | 5 + app/libraries/Wsis/Wsis.php | 52 +++++++ app/routes.php | 2 + 8 files changed, 300 insertions(+), 49 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/config/pga_config.php.template ---------------------------------------------------------------------- diff --git a/app/config/pga_config.php.template b/app/config/pga_config.php.template index 19c4707..58323d0 100644 --- a/app/config/pga_config.php.template +++ b/app/config/pga_config.php.template @@ -26,12 +26,32 @@ return array( /** * Tenant admin's username */ - 'admin-username' => '[email protected]', + 'admin-username' => '[email protected]', /** * Tenant admin's password */ - 'admin-password' => '[email protected]', + 'admin-password' => 'master', + + /** + * Authentication mode (basic, oauth) + */ + 'auth-mode' => 'basic', + + /** + * OAuth client key + */ + 'oauth-client-key' => 'iGEREhSBLuGapdcXwMU0b8jEpA4a', + + /** + * OAuth client secret + */ + 'oauth-client-secret' => 'g4Lgp05JIJcNQryJkNKjXJYi8A8a', + + /** + * OAuth callback url + */ + 'oauth-callback-url' => 'http://localhost/airavata-php-gateway/public/callback-url', /** * Identity server domain http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/controllers/AccountController.php ---------------------------------------------------------------------- diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index 64a0b09..8dcbd0f 100755 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -60,8 +60,6 @@ class AccountController extends BaseController //update user profile WSIS::updateUserProfile($username, $email, $first_name, $last_name); - //creating a default project for user - ProjectUtilities::create_default_project($username); CommonUtilities::print_success_message('New user created!'); return View::make('account/login'); } @@ -69,7 +67,55 @@ class AccountController extends BaseController public function loginView() { - return View::make('account/login'); + if(Config::get('pga_config.wsis')['auth-mode'] == "oauth"){ + $url = WSIS::getOAuthRequestCodeUrl(); + return Redirect::away($url); + }else{ + return View::make('account/login'); + } + } + + public function oauthCallback() + { + if (!isset($_GET["code"])) { + CommonUtilities::print_error_message("Require the code parameter to validate!"); + } + + $code = $_GET["code"]; + $response = WSIS::getOAuthToken($code); + $accessToken = $response->access_token; + $refreshToken = $response->refresh_token; + $expirationTime = time() + $response->expires_in - 5; //5 seconds safe margin + $authzToken = new Airavata\Model\Security\AuthzToken(); + $authzToken->accessToken = $accessToken; + Session::put('authz-token',$authzToken); + Session::put('oauth-refresh-code',$refreshToken); + Session::put('oauth-expiration-time',$expirationTime); + + $userProfile = WSIS::getUserProfileFromOAuthToken($accessToken); + Session::put("user-profile", $userProfile); + + $userRoles = $userProfile['roles']; + if (in_array(Config::get('pga_config.wsis')['admin-role-name'], $userRoles)) { + Session::put("admin", true); + } + if (in_array(Config::get('pga_config.wsis')['read-only-admin'], $userRoles)) { + Session::put("admin-read-only", true); + } + + $username = $userProfile['username']; + CommonUtilities::store_id_in_session($username); + CommonUtilities::print_success_message('Login successful! You will be redirected to your home page shortly.'); + Session::put("gateway_id", Config::get('pga_config.airavata')['gateway-id']); + + //creating a default project for user + $projects = ProjectUtilities::get_all_user_projects(Config::get('pga_config.airavata')['gateway-id'], $username); + if($projects == null || count($projects) == 0){ + //creating a default project for user + ProjectUtilities::create_default_project($username); + } + + return Redirect::to("home"); } public function loginSubmit() @@ -95,9 +141,7 @@ class AccountController extends BaseController CommonUtilities::store_id_in_session($username); CommonUtilities::print_success_message('Login successful! You will be redirected to your home page shortly.'); - //TODO::If this option is not safe, have to find a better method to send credentials to identity server on every connection. Session::put("gateway_id", Config::get('pga_config.airavata')['gateway-id']); - Session::put("password", $_POST["password"]); //creating a default project for user $projects = ProjectUtilities::get_all_user_projects(Config::get('pga_config.airavata')['gateway-id'], $username); http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/filters.php ---------------------------------------------------------------------- diff --git a/app/filters.php b/app/filters.php index 80336ab..ac98976 100755 --- a/app/filters.php +++ b/app/filters.php @@ -12,13 +12,26 @@ */ App::before(function ($request) { - $authzToken = new Airavata\Model\Security\AuthzToken(); - $authzToken->accessToken = "emptyToken"; - $apiVersion = Airavata::getAPIVersion($authzToken); + //Check Airavata Server is up + $apiVersion = Airavata::getAPIVersion(); if (empty($apiVersion)) return View::make("server-down"); - else - Session::put('authz-token',$authzToken); + + //Check OAuth token has expired + if(Config::get('pga_config.wsis')['auth-mode']=="oauth" && Session::has('authz-token')){ + $currentTime = time(); + if($currentTime > Session::get('oauth-expiration-time')){ + $response = WSIS::getRefreshedOAutheToken(Session::get('oauth-refresh-code')); + $accessToken = $response->access_token; + $refreshToken = $response->refresh_token; + $expirationTime = time()/1000 + $response->expires_in - 300; + $authzToken = new Airavata\Model\Security\AuthzToken(); + $authzToken->accessToken = $accessToken; + Session::put('authz-token',$authzToken); + Session::put('oauth-refresh-code',$refreshToken); + Session::put('oauth-expiration-time',$expirationTime); + } + } }); http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/libraries/Airavata/API/Airavata.php ---------------------------------------------------------------------- diff --git a/app/libraries/Airavata/API/Airavata.php b/app/libraries/Airavata/API/Airavata.php index cab3cef..c068aa6 100644 --- a/app/libraries/Airavata/API/Airavata.php +++ b/app/libraries/Airavata/API/Airavata.php @@ -20,14 +20,13 @@ interface AiravataIf { /** * Fetch Apache Airavata API version * - * @param \Airavata\Model\Security\AuthzToken $authzToken * @return string * @throws \Airavata\API\Error\InvalidRequestException * @throws \Airavata\API\Error\AiravataClientException * @throws \Airavata\API\Error\AiravataSystemException * @throws \Airavata\API\Error\AuthorizationException */ - public function getAPIVersion(\Airavata\Model\Security\AuthzToken $authzToken); + public function getAPIVersion(); /** * @param \Airavata\Model\Security\AuthzToken $authzToken * @param \Airavata\Model\Workspace\Gateway $gateway @@ -2555,16 +2554,15 @@ class AiravataClient implements \Airavata\API\AiravataIf { $this->output_ = $output ? $output : $input; } - public function getAPIVersion(\Airavata\Model\Security\AuthzToken $authzToken) + public function getAPIVersion() { - $this->send_getAPIVersion($authzToken); + $this->send_getAPIVersion(); return $this->recv_getAPIVersion(); } - public function send_getAPIVersion(\Airavata\Model\Security\AuthzToken $authzToken) + public function send_getAPIVersion() { $args = new \Airavata\API\Airavata_getAPIVersion_args(); - $args->authzToken = $authzToken; $bin_accel = ($this->output_ instanceof TBinaryProtocolAccelerated) && function_exists('thrift_protocol_write_binary'); if ($bin_accel) { @@ -10204,26 +10202,12 @@ class AiravataClient implements \Airavata\API\AiravataIf { class Airavata_getAPIVersion_args { static $_TSPEC; - /** - * @var \Airavata\Model\Security\AuthzToken - */ - public $authzToken = null; - public function __construct($vals=null) { + public function __construct() { if (!isset(self::$_TSPEC)) { self::$_TSPEC = array( - 1 => array( - 'var' => 'authzToken', - 'type' => TType::STRUCT, - 'class' => '\Airavata\Model\Security\AuthzToken', - ), ); } - if (is_array($vals)) { - if (isset($vals['authzToken'])) { - $this->authzToken = $vals['authzToken']; - } - } } public function getName() { @@ -10245,14 +10229,6 @@ class Airavata_getAPIVersion_args { } switch ($fid) { - case 1: - if ($ftype == TType::STRUCT) { - $this->authzToken = new \Airavata\Model\Security\AuthzToken(); - $xfer += $this->authzToken->read($input); - } else { - $xfer += $input->skip($ftype); - } - break; default: $xfer += $input->skip($ftype); break; @@ -10266,14 +10242,6 @@ class Airavata_getAPIVersion_args { public function write($output) { $xfer = 0; $xfer += $output->writeStructBegin('Airavata_getAPIVersion_args'); - if ($this->authzToken !== null) { - if (!is_object($this->authzToken)) { - throw new TProtocolException('Bad type in structure.', TProtocolException::INVALID_DATA); - } - $xfer += $output->writeFieldBegin('authzToken', TType::STRUCT, 1); - $xfer += $this->authzToken->write($output); - $xfer += $output->writeFieldEnd(); - } $xfer += $output->writeFieldStop(); $xfer += $output->writeStructEnd(); return $xfer; http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/libraries/Wsis/Stubs/OAuthManager.php ---------------------------------------------------------------------- diff --git a/app/libraries/Wsis/Stubs/OAuthManager.php b/app/libraries/Wsis/Stubs/OAuthManager.php new file mode 100644 index 0000000..5188fd6 --- /dev/null +++ b/app/libraries/Wsis/Stubs/OAuthManager.php @@ -0,0 +1,147 @@ +<?php + +namespace Wsis\Stubs; + +class OAuthManager +{ + + public $CurlHeaders; + public $ResponseCode; + + private $_AuthorizeUrl; + private $_AccessTokenUrl; + private $_UserInfoUrl; + private $_verifyPeer; + private $_cafilePath; + + public function __construct($serverUrl, $verifyPeer, $cafilePath) + { + $this->_AuthorizeUrl = $serverUrl . "oauth2/authorize"; + $this->_AccessTokenUrl = $serverUrl . "oauth2/token"; + $this->_UserInfoUrl = $serverUrl . "oauth2/userinfo?schema=openid"; + $this->_verifyPeer = $verifyPeer; + $this->_cafilePath = $cafilePath; + $this->CurlHeaders = array(); + $this->ResponseCode = 0; + } + + public function requestAccessCode($client_id, $redirect_url) + { + return ($this->_AuthorizeUrl . "?client_id=" . $client_id . "&response_type=code&scope=openid&redirect_uri=" . $redirect_url); + } + + // Convert an authorization code from callback into an access token. + public function getAccessToken($client_id, $client_secret, $auth_code, $redirect_url) + { + // Init cUrl. + $r = $this->initCurl($this->_AccessTokenUrl); + + // Add client ID and client secret to the headers. + curl_setopt($r, CURLOPT_HTTPHEADER, array( + "Authorization: Basic " . base64_encode($client_id . ":" . $client_secret), + )); + + // Assemble POST parameters for the request. + $post_fields = "code=" . urlencode($auth_code) . "&grant_type=authorization_code&redirect_uri=" . $redirect_url; + + // Obtain and return the access token from the response. + curl_setopt($r, CURLOPT_POST, true); + curl_setopt($r, CURLOPT_POSTFIELDS, $post_fields); + + $response = curl_exec($r); + if ($response == false) { + die("curl_exec() failed. Error: " . curl_error($r)); + } + + //Parse JSON return object. + return json_decode($response); + } + + // To get a refreshed access token + public function getRefreshedAccessToken($client_id, $client_secret, $refresh_token) + { + // Init cUrl. + $r = $this->initCurl($this->_AccessTokenUrl); + + // Add client ID and client secret to the headers. + curl_setopt($r, CURLOPT_HTTPHEADER, array( + "Authorization: Basic " . base64_encode($client_id . ":" . $client_secret), + )); + + // Assemble POST parameters for the request. + $post_fields = "refresh_token=" . urlencode($refresh_token) . "&grant_type=refresh_token"; + + // Obtain and return the access token from the response. + curl_setopt($r, CURLOPT_POST, true); + curl_setopt($r, CURLOPT_POSTFIELDS, $post_fields); + + $response = curl_exec($r); + if ($response == false) { + die("curl_exec() failed. Error: " . curl_error($r)); + } + + //Parse JSON return object. + return json_decode($response); + } + + private function initCurl($url) + { + $r = null; + + if (($r = @curl_init($url)) == false) { + header("HTTP/1.1 500", true, 500); + die("Cannot initialize cUrl session. Is cUrl enabled for your PHP installation?"); + } + + curl_setopt($r, CURLOPT_RETURNTRANSFER, 1); + + // Decode compressed responses. + curl_setopt($r, CURLOPT_ENCODING, 1); + + curl_setopt($r, CURLOPT_SSL_VERIFYPEER, $this->_verifyPeer); + curl_setopt($r, CURLOPT_CAINFO, $this->_cafilePath); + + return ($r); + } + + + public function getUserProfile($access_token) + { + $r = $this->initCurl($this->_UserInfoUrl); + + curl_setopt($r, CURLOPT_HTTPHEADER, array( + "Authorization: Bearer " . $access_token + )); + + $response = curl_exec($r); + if ($response == false) { + die("curl_exec() failed. Error: " . curl_error($r)); + } + + //Parse JSON return object. + return json_decode($response); + } + + // A generic function that executes an API request. + public function execRequest($url, $access_token, $get_params) + { + // Create request string. + $full_url = http_build_query($url, $get_params); + + $r = $this->initCurl($full_url); + + curl_setopt($r, CURLOPT_HTTPHEADER, array( + "Authorization: Basic " . base64_encode($access_token) + )); + + $response = curl_exec($r); + if ($response == false) { + die("curl_exec() failed. Error: " . curl_error($r)); + } + + //Parse JSON return object. + return json_decode($response); + } +} + +?> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/libraries/Wsis/Stubs/UserProfileManager.php ---------------------------------------------------------------------- diff --git a/app/libraries/Wsis/Stubs/UserProfileManager.php b/app/libraries/Wsis/Stubs/UserProfileManager.php index eff2a35..6d5bcc8 100644 --- a/app/libraries/Wsis/Stubs/UserProfileManager.php +++ b/app/libraries/Wsis/Stubs/UserProfileManager.php @@ -38,6 +38,11 @@ class UserProfileManager { $profile = new UserProfileDTO(); $fieldValues = array(); + $usernameDTO = new UserFieldDTO(); + $usernameDTO->claimUri = "http://wso2.org/claims/sub"; + $usernameDTO->fieldValue = $username; + array_push($fieldValues, $usernameDTO); + $emailDTO = new UserFieldDTO(); $emailDTO->claimUri = "http://wso2.org/claims/emailaddress"; $emailDTO->fieldValue = $email; http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/libraries/Wsis/Wsis.php ---------------------------------------------------------------------- diff --git a/app/libraries/Wsis/Wsis.php b/app/libraries/Wsis/Wsis.php index 456b02b..e3f8e26 100755 --- a/app/libraries/Wsis/Wsis.php +++ b/app/libraries/Wsis/Wsis.php @@ -6,6 +6,9 @@ use Wsis\Stubs\UserProfileManager; use Wsis\Stubs\UserStoreManager; use Wsis\Stubs\TenantManager; use Wsis\Stubs\UserInformationRecoveryManager; +use Wsis\Stubs\OAuthManager; + +use Illuminate\Support\Facades\Config; class Wsis { @@ -34,6 +37,12 @@ class Wsis { private $userInfoRecoveryManager; /** + * @var + * @access private + */ + private $oauthManger; + + /** * @var string * @access private */ @@ -87,6 +96,7 @@ class Wsis { $this->tenantManager = new TenantManager($service_url, $parameters); $this->userProfileManager = new UserProfileManager($service_url, $parameters); $this->userInfoRecoveryManager = new UserInformationRecoveryManager($service_url, $parameters); + $this->oauthManger = new OAuthManager(Config::get('pga_config.wsis')['service-url'], $verify_peer, $cafile_path); } catch (Exception $ex) { throw new Exception("Unable to instantiate WSO2 IS client", 0, $ex); } @@ -142,6 +152,48 @@ class Wsis { } /** + * Function to get OAuth request code url + * @return mixed + */ + public function getOAuthRequestCodeUrl(){ + $url = $this->oauthManger->requestAccessCode(Config::get('pga_config.wsis')['oauth-client-key'], + Config::get('pga_config.wsis')['oauth-callback-url']); + return $url; + } + + /** + * Function to get OAuth Access token + * @return string + */ + public function getOAuthToken($code){ + $response = $this->oauthManger->getAccessToken(Config::get('pga_config.wsis')['oauth-client-key'], + Config::get('pga_config.wsis')['oauth-client-secret'], $code, + Config::get('pga_config.wsis')['oauth-callback-url']); + return $response; + } + + /** + * Method to get refreshed access token + * @param $refreshToken + * @return mixed + */ + public function getRefreshedOAutheToken($refreshToken){ + $response = $this->oauthManger->getRefreshedAccessToken(Config::get('pga_config.wsis')['oauth-client-key'], + Config::get('pga_config.wsis')['oauth-client-secret'], $refreshToken); + return $response; + } + + /** + * Function to get user profile from OAuth token + * @param $token + */ + public function getUserProfileFromOAuthToken($token){ + $userProfile = $this->oauthManger->getUserProfile($token); + return array('username'=>$userProfile->sub, 'email'=>$userProfile->email, 'firstname'=>$userProfile->given_name, + 'lastname'=>$userProfile->family_name, 'roles'=>explode(",",$userProfile->roles)); + } + + /** * Function to check whether username exists * * @param string $username http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cc830899/app/routes.php ---------------------------------------------------------------------- diff --git a/app/routes.php b/app/routes.php index bb707a4..361291a 100755 --- a/app/routes.php +++ b/app/routes.php @@ -24,6 +24,8 @@ Route::get("login", "AccountController@loginView"); Route::post("login", "AccountController@loginSubmit"); +Route::get("callback-url", "AccountController@oauthCallback"); + Route::get("logout", "AccountController@logout"); Route::get("forgot-password", "AccountController@forgotPassword");
