AIRAVATA-2342 Keycloak: implement password grant type flow
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/e9f3b24a Tree: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/tree/e9f3b24a Diff: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/diff/e9f3b24a Branch: refs/heads/develop Commit: e9f3b24a04bf5e2353039b31779578aaad25418d Parents: 60951a2 Author: Marcus Christie <[email protected]> Authored: Thu Apr 27 12:17:44 2017 -0400 Committer: Marcus Christie <[email protected]> Committed: Thu Apr 27 12:17:44 2017 -0400 ---------------------------------------------------------------------- app/controllers/AccountController.php | 16 ++----- .../Keycloak/API/BaseKeycloakAPIEndpoint.php | 49 +++++++++++++++++++ app/libraries/Keycloak/API/RoleMapper.php | 50 ++------------------ app/libraries/Keycloak/API/Roles.php | 45 +----------------- app/libraries/Keycloak/API/Users.php | 49 ++----------------- app/libraries/Keycloak/Keycloak.php | 45 ++++++++++++++++++ 6 files changed, 107 insertions(+), 147 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/e9f3b24a/app/controllers/AccountController.php ---------------------------------------------------------------------- diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index 7900c7c..303c3b4 100644 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -99,32 +99,22 @@ class AccountController extends BaseController { if (CommonUtilities::form_submitted()) { $wsisConfig = Config::get('pga_config.wsis'); - if( $wsisConfig['tenant-domain'] == "") - $username = Input::get("username"); - else - $username = Input::get("username") . "@" . $wsisConfig['tenant-domain']; + $username = Input::get("username"); $password = $_POST['password']; - $response = WSIS::authenticate($username, $password); + $response = Keycloak::authenticate($username, $password); if(!isset($response->access_token)){ return Redirect::to("login")->with("invalid-credentials", true); } $accessToken = $response->access_token; $refreshToken = $response->refresh_token; - $expirationTime = time() + $response->expires_in - 5; //5 seconds safe margin + $expirationTime = time() + $response->expires_in - 300; // 5 minutes safe margin $userProfile = Keycloak::getUserProfileFromOAuthToken($accessToken); $username = $userProfile['username']; $userRoles = $userProfile['roles']; - //FIXME There is a bug in WSO2 IS which doest not return the admin role for the default admin user. - //FIXME Hence as a workaround we manually add it here. - if ($username == Config::get('pga_config.wsis')['admin-username'] - || $username == Config::get('pga_config.wsis')['admin-username'] . '@' . Config::get('pga_config.wsis')['tenant-domain']){ - $userRoles[] = Config::get('pga_config.wsis')['admin-role-name']; - } - $authzToken = new Airavata\Model\Security\AuthzToken(); $authzToken->accessToken = $accessToken; $authzToken->claimsMap['gatewayID'] = Config::get('pga_config.airavata')['gateway-id']; http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/e9f3b24a/app/libraries/Keycloak/API/BaseKeycloakAPIEndpoint.php ---------------------------------------------------------------------- diff --git a/app/libraries/Keycloak/API/BaseKeycloakAPIEndpoint.php b/app/libraries/Keycloak/API/BaseKeycloakAPIEndpoint.php new file mode 100644 index 0000000..c440179 --- /dev/null +++ b/app/libraries/Keycloak/API/BaseKeycloakAPIEndpoint.php @@ -0,0 +1,49 @@ +<?php +namespace Keycloak\API; + +use Exception; +use Log; + +class BaseKeycloakAPIEndpoint { + + protected $base_endpoint_url; + protected $admin_username; + protected $admin_password; + protected $verify_peer; + + function __construct($base_endpoint_url, $admin_username, $admin_password, $verify_peer) { + $this->base_endpoint_url = $base_endpoint_url; + $this->admin_username = $admin_username; + $this->admin_password = $admin_password; + $this->verify_peer = $verify_peer; + } + + protected function getAPIAccessToken($realm) { + + // http://www.keycloak.org/docs/2.5/server_development/topics/admin-rest-api.html + // curl -d client_id=admin-cli -d username=username \ + // -d "password=password" -d grant_type=password https://149.165.156.62:8443/auth/realms/master/protocol/openid-connect/token + + $r = curl_init($this->base_endpoint_url . '/realms/' . rawurlencode($realm) . '/protocol/openid-connect/token'); + curl_setopt($r, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($r, CURLOPT_ENCODING, 1); + curl_setopt($r, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); + + // Assemble POST parameters for the request. + $post_fields = "client_id=admin-cli&username=" . urlencode($this->admin_username) . "&password=" . urlencode($this->admin_password) . "&grant_type=password"; + + // 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) { + Log::error("Failed to retrieve API Access Token"); + die("curl_exec() failed. Error: " . curl_error($r)); + } + + $result = json_decode($response); + // Log::debug("API Access Token result", array($result)); + return $result->access_token; + } +} http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/e9f3b24a/app/libraries/Keycloak/API/RoleMapper.php ---------------------------------------------------------------------- diff --git a/app/libraries/Keycloak/API/RoleMapper.php b/app/libraries/Keycloak/API/RoleMapper.php index 18a477b..f2fab42 100644 --- a/app/libraries/Keycloak/API/RoleMapper.php +++ b/app/libraries/Keycloak/API/RoleMapper.php @@ -10,19 +10,7 @@ use Log; * This class provide an easy to use interface for * the Keycloak RoleMapper REST API. */ -class RoleMapper { - - private $base_endpoint_url; - private $admin_username; - private $admin_password; - private $verify_peer; - - public function __construct($base_endpoint_url, $admin_username, $admin_password, $verify_peer) { - $this->base_endpoint_url = $base_endpoint_url; - $this->admin_username = $admin_username; - $this->admin_password = $admin_password; - $this->verify_peer = $verify_peer; - } +class RoleMapper extends BaseKeycloakAPIEndpoint { /** * Get realm-level role mappings for a user @@ -35,7 +23,7 @@ class RoleMapper { // curl -H "Authorization: bearer $access_token" https://149.165.156.62:8443/auth/admin/realms/airavata/users/2c9ad2c6-0212-4aef-a5fb-9df862578934/role-mappings/realm // get access token for admin API - $access_token = $this->getAPIAccessToken(); + $access_token = $this->getAPIAccessToken($realm); $url = $this->base_endpoint_url . '/admin/realms/' . rawurlencode($realm) . '/users/' . rawurlencode($user_id) . '/role-mappings/realm'; // Log::debug("getRealmRoleMappingsForUser url", array($url)); $r = curl_init($url); @@ -63,7 +51,7 @@ class RoleMapper { public function addRealmRoleMappingsToUser($realm, $user_id, $role_representations) { // get access token for admin API - $access_token = $this->getAPIAccessToken(); + $access_token = $this->getAPIAccessToken($realm); $url = $this->base_endpoint_url . '/admin/realms/' . rawurlencode($realm) . '/users/' . rawurlencode($user_id) . '/role-mappings/realm'; // Log::debug("addRealmRoleMappingsToUser", array($url, $role_representations)); $r = curl_init($url); @@ -96,7 +84,7 @@ class RoleMapper { public function deleteRealmRoleMappingsToUser($realm, $user_id, $role_representations) { // get access token for admin API - $access_token = $this->getAPIAccessToken(); + $access_token = $this->getAPIAccessToken($realm); $url = $this->base_endpoint_url . '/admin/realms/' . rawurlencode($realm) . '/users/' . rawurlencode($user_id) . '/role-mappings/realm'; // Log::debug("deleteRealmRoleMappingsToUser", array($url, $role_representations)); $r = curl_init($url); @@ -122,34 +110,4 @@ class RoleMapper { } return; } - - // TODO: factor this out into base class? - private function getAPIAccessToken() { - - // http://www.keycloak.org/docs/2.5/server_development/topics/admin-rest-api.html - // curl -d client_id=admin-cli -d username=username \ - // -d "password=password" -d grant_type=password https://149.165.156.62:8443/auth/realms/master/protocol/openid-connect/token - - $r = curl_init($this->base_endpoint_url . '/realms/master/protocol/openid-connect/token'); - curl_setopt($r, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($r, CURLOPT_ENCODING, 1); - curl_setopt($r, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); - - // Assemble POST parameters for the request. - $post_fields = "client_id=admin-cli&username=" . urlencode($this->admin_username) . "&password=" . urlencode($this->admin_password) . "&grant_type=password"; - - // 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) { - Log::error("Failed to retrieve API Access Token"); - die("curl_exec() failed. Error: " . curl_error($r)); - } - - $result = json_decode($response); - // Log::debug("API Access Token result", array($result)); - return $result->access_token; - } } http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/e9f3b24a/app/libraries/Keycloak/API/Roles.php ---------------------------------------------------------------------- diff --git a/app/libraries/Keycloak/API/Roles.php b/app/libraries/Keycloak/API/Roles.php index 02f5b8f..565b860 100644 --- a/app/libraries/Keycloak/API/Roles.php +++ b/app/libraries/Keycloak/API/Roles.php @@ -7,19 +7,7 @@ namespace Keycloak\API; * This class provide an easy to use interface for * the Keycloak Roles REST API. */ -class Roles { - - private $base_endpoint_url; - private $admin_username; - private $admin_password; - private $verify_peer; - - public function __construct($base_endpoint_url, $admin_username, $admin_password, $verify_peer) { - $this->base_endpoint_url = $base_endpoint_url; - $this->admin_username = $admin_username; - $this->admin_password = $admin_password; - $this->verify_peer = $verify_peer; - } +class Roles extends BaseKeycloakAPIEndpoint { /** * Get representations of all of a realm's roles @@ -29,7 +17,7 @@ class Roles { public function getRoles($realm){ // get access token for admin API - $access_token = $this->getAPIAccessToken(); + $access_token = $this->getAPIAccessToken($realm); $r = curl_init($this->base_endpoint_url . '/admin/realms/' . rawurlencode($realm) . '/roles'); curl_setopt($r, CURLOPT_RETURNTRANSFER, 1); curl_setopt($r, CURLOPT_ENCODING, 1); @@ -46,33 +34,4 @@ class Roles { // Log::debug("getRealmRoleMappingsForUser result", array($result)); return $result; } - - // TODO: factor this out into base class? - private function getAPIAccessToken() { - - // http://www.keycloak.org/docs/2.5/server_development/topics/admin-rest-api.html - // curl -d client_id=admin-cli -d username=username \ - // -d "password=password" -d grant_type=password https://149.165.156.62:8443/auth/realms/master/protocol/openid-connect/token - - $r = curl_init($this->base_endpoint_url . '/realms/master/protocol/openid-connect/token'); - curl_setopt($r, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($r, CURLOPT_ENCODING, 1); - curl_setopt($r, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); - - // Assemble POST parameters for the request. - $post_fields = "client_id=admin-cli&username=" . urlencode($this->admin_username) . "&password=" . urlencode($this->admin_password) . "&grant_type=password"; - - // 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)); - } - - $result = json_decode($response); - // Log::debug("API Access Token result", array($result)); - return $result->access_token; - } } http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/e9f3b24a/app/libraries/Keycloak/API/Users.php ---------------------------------------------------------------------- diff --git a/app/libraries/Keycloak/API/Users.php b/app/libraries/Keycloak/API/Users.php index 089ad3a..d03be02 100644 --- a/app/libraries/Keycloak/API/Users.php +++ b/app/libraries/Keycloak/API/Users.php @@ -9,19 +9,7 @@ use Log; * This class provide an easy to use interface for * the Keycloak Users REST API. */ -class Users { - - private $base_endpoint_url; - private $admin_username; - private $admin_password; - private $verify_peer; - - public function __construct($base_endpoint_url, $admin_username, $admin_password, $verify_peer) { - $this->base_endpoint_url = $base_endpoint_url; - $this->admin_username = $admin_username; - $this->admin_password = $admin_password; - $this->verify_peer = $verify_peer; - } +class Users extends BaseKeycloakAPIEndpoint { /** * Get representations of all users @@ -31,7 +19,7 @@ class Users { public function getUsers($realm, $username = null){ // get access token for admin API - $access_token = $this->getAPIAccessToken(); + $access_token = $this->getAPIAccessToken($realm); $url = $this->base_endpoint_url . '/admin/realms/' . rawurlencode($realm) . '/users'; if ($username) { $url = $url . '?username=' . rawurlencode($username); @@ -64,7 +52,7 @@ class Users { public function searchUsers($realm, $keyword){ // get access token for admin API - $access_token = $this->getAPIAccessToken(); + $access_token = $this->getAPIAccessToken($realm); $url = $this->base_endpoint_url . '/admin/realms/' . rawurlencode($realm) . '/users?search=' . rawurlencode($keyword); // Log::debug("getUsers url", array($url)); $r = curl_init($url); @@ -92,7 +80,7 @@ class Users { public function getUser($realm, $user_id) { // get access token for admin API - $access_token = $this->getAPIAccessToken(); + $access_token = $this->getAPIAccessToken($realm); $url = $this->base_endpoint_url . '/admin/realms/' . rawurlencode($realm) . '/users/' . rawurlencode($user_id); // Log::debug("getUser url", array($url)); $r = curl_init($url); @@ -111,33 +99,4 @@ class Users { // Log::debug("getUsers result", array($result)); return $result; } - - // TODO: factor this out into base class? - private function getAPIAccessToken() { - - // http://www.keycloak.org/docs/2.5/server_development/topics/admin-rest-api.html - // curl -d client_id=admin-cli -d username=username \ - // -d "password=password" -d grant_type=password https://149.165.156.62:8443/auth/realms/master/protocol/openid-connect/token - - $r = curl_init($this->base_endpoint_url . '/realms/master/protocol/openid-connect/token'); - curl_setopt($r, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($r, CURLOPT_ENCODING, 1); - curl_setopt($r, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); - - // Assemble POST parameters for the request. - $post_fields = "client_id=admin-cli&username=" . urlencode($this->admin_username) . "&password=" . urlencode($this->admin_password) . "&grant_type=password"; - - // 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)); - } - - $result = json_decode($response); - // Log::debug("API Access Token result", array($result)); - return $result->access_token; - } } http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/e9f3b24a/app/libraries/Keycloak/Keycloak.php ---------------------------------------------------------------------- diff --git a/app/libraries/Keycloak/Keycloak.php b/app/libraries/Keycloak/Keycloak.php index 1f876b0..71706e5 100644 --- a/app/libraries/Keycloak/Keycloak.php +++ b/app/libraries/Keycloak/Keycloak.php @@ -43,6 +43,51 @@ class Keycloak { $this->users = new Users($base_endpoint_url, $admin_username, $admin_password, $verify_peer); } + /** + * Function to authenticate user + * + * @param string $username + * @param string $password + * @return boolean + * @throws Exception + */ + public function authenticate($username, $password){ + + $config = $this->getOpenIDConnectDiscoveryConfiguration(); + $token_endpoint = $config->token_endpoint; + + // Init cUrl. + $r = curl_init($token_endpoint); + curl_setopt($r, CURLOPT_RETURNTRANSFER, 1); + // Decode compressed responses. + curl_setopt($r, CURLOPT_ENCODING, 1); + curl_setopt($r, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); + + // Add client ID and client secret to the headers. + curl_setopt($r, CURLOPT_HTTPHEADER, array( + "Authorization: Basic " . base64_encode($this->client_id . ":" . $this->client_secret), + )); + + // Assemble POST parameters for the request. + $post_fields = "client_id=" . urlencode($this->client_id) . "&client_secret=" . urlencode($this->client_secret) . "&grant_type=password"; + $post_fields .= "&username=" . urlencode($username) . "&password=" . urlencode($password); + + // 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. + $result = json_decode($response); + // Log::debug("password grant type authenciation response", array($result)); + + return $result; + } + public function getOAuthRequestCodeUrl(){ $config = $this->getOpenIDConnectDiscoveryConfiguration(); $authorization_endpoint = $config->authorization_endpoint;
