From 97b468081c264851a4bbb6bb01dcbcfb460edbc7 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Tue, 12 Jan 2016 13:03:34 +0400 Subject: [PATCH 01/16] Initial --- src/OAuth2/Storage/Cassandra.php | 474 +++--------------------- src/OAuth2/Storage/CouchbaseDB.php | 329 +--------------- src/OAuth2/Storage/KeyValueAbstract.php | 424 +++++++++++++++++++++ src/OAuth2/Storage/Memcached.php | 49 +++ src/OAuth2/Storage/MongoDB.php | 70 ++++ 5 files changed, 608 insertions(+), 738 deletions(-) create mode 100644 src/OAuth2/Storage/KeyValueAbstract.php create mode 100644 src/OAuth2/Storage/Memcached.php create mode 100644 src/OAuth2/Storage/MongoDB.php diff --git a/src/OAuth2/Storage/Cassandra.php b/src/OAuth2/Storage/Cassandra.php index 5f7068af0..a5341cd50 100644 --- a/src/OAuth2/Storage/Cassandra.php +++ b/src/OAuth2/Storage/Cassandra.php @@ -5,471 +5,89 @@ use phpcassa\ColumnFamily; use phpcassa\ColumnSlice; use phpcassa\Connection\ConnectionPool; -use OAuth2\OpenID\Storage\UserClaimsInterface; -use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface; -/** - * Cassandra storage for all storage types - * - * To use, install "thobbs/phpcassa" via composer - * - * composer require thobbs/phpcassa:dev-master - * - * - * Once this is done, instantiate the - * - * $cassandra = new \phpcassa\Connection\ConnectionPool('oauth2_server', array('127.0.0.1:9160')); - * - * - * Then, register the storage client: - * - * $storage = new OAuth2\Storage\Cassandra($cassandra); - * $storage->setClientDetails($client_id, $client_secret, $redirect_uri); - * - * - * @see test/lib/OAuth2/Storage/Bootstrap::getCassandraStorage - */ -class Cassandra implements AuthorizationCodeInterface, - AccessTokenInterface, - ClientCredentialsInterface, - UserCredentialsInterface, - RefreshTokenInterface, - JwtBearerInterface, - ScopeInterface, - PublicKeyInterface, - UserClaimsInterface, - OpenIDAuthorizationCodeInterface +class Cassandra extends KeyValueAbstract { - private $cache; + protected $db; - /* The cassandra client */ - protected $cassandra; - - /* Configuration array */ - protected $config; - - /** - * Cassandra Storage! uses phpCassa - * - * @param \phpcassa\ConnectionPool $cassandra - * @param array $config - */ public function __construct($connection = array(), array $config = array()) { if ($connection instanceof ConnectionPool) { - $this->cassandra = $connection; - } else { - if (!is_array($connection)) { - throw new \InvalidArgumentException('First argument to OAuth2\Storage\Cassandra must be an instance of phpcassa\Connection\ConnectionPool or a configuration array'); - } + $this->db = $connection; + } else if (is_array($connection)) { $connection = array_merge(array( 'keyspace' => 'oauth2', 'servers' => null, ), $connection); - - $this->cassandra = new ConnectionPool($connection['keyspace'], $connection['servers']); + + $this->db = new ConnectionPool($connection['keyspace'], $connection['servers']); + } else { + throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of phpcassa\Connection\ConnectionPool or a configuration array'); } - - $this->config = array_merge(array( - // cassandra config + + $this->config = array_merge($this->config, array( 'column_family' => 'auth', - - // key names - 'client_key' => 'oauth_clients:', - 'access_token_key' => 'oauth_access_tokens:', - 'refresh_token_key' => 'oauth_refresh_tokens:', - 'code_key' => 'oauth_authorization_codes:', - 'user_key' => 'oauth_users:', - 'jwt_key' => 'oauth_jwt:', - 'scope_key' => 'oauth_scopes:', - 'public_key_key' => 'oauth_public_keys:', + 'expire' => 0, ), $config); } - - protected function getValue($key) + + protected function _makeKey($table, $key) + { + return $table . ':' . $key; + } + + protected function get($table, $key) { + $key = $this->_makeKey($table, $key); + if (isset($this->cache[$key])) { return $this->cache[$key]; } - $cf = new ColumnFamily($this->cassandra, $this->config['column_family']); - + $cf = new ColumnFamily($this->db, $this->config['column_family']); + try { $value = $cf->get($key, new ColumnSlice("", "")); - $value = array_shift($value); - } catch (\cassandra\NotFoundException $e) { - return false; + return json_decode(current($value), true); + } catch (\Exception $e) { + } - - return json_decode($value, true); + + return false; } - protected function setValue($key, $value, $expire = 0) + protected function set($table, $key, $value) { + $key = $this->_makeKey($table, $key); + $this->cache[$key] = $value; - - $cf = new ColumnFamily($this->cassandra, $this->config['column_family']); - + + $cf = new ColumnFamily($this->db, $this->config['column_family']); + $str = json_encode($value); - if ($expire > 0) { - try { - $seconds = $expire - time(); - // __data key set as C* requires a field, note: max TTL can only be 630720000 seconds - $cf->insert($key, array('__data' => $str), null, $seconds); - } catch (\Exception $e) { - return false; - } - } else { - try { - // __data key set as C* requires a field - $cf->insert($key, array('__data' => $str)); - } catch (\Exception $e) { - return false; - } + try { + $cf->insert($key, array('__data' => $str), null, ($this->config['expire'] > 0 ? $this->config['expire'] - time() : null)); + return true; + } catch (\Exception $e) { } - - return true; + + return false; } - protected function expireValue($key) + protected function delete($table, $key) { + $key = $this->_makeKey($table, $key); + unset($this->cache[$key]); - - $cf = new ColumnFamily($this->cassandra, $this->config['column_family']); + + $cf = new ColumnFamily($this->db, $this->config['column_family']); try { // __data key set as C* requires a field $cf->remove($key, array('__data')); + return true; } catch (\Exception $e) { - return false; } - - return true; - } - - /* AuthorizationCodeInterface */ - public function getAuthorizationCode($code) - { - return $this->getValue($this->config['code_key'] . $code); - } - - public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) - { - return $this->setValue( - $this->config['code_key'] . $authorization_code, - compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), - $expires - ); - } - - public function expireAuthorizationCode($code) - { - $key = $this->config['code_key'] . $code; - unset($this->cache[$key]); - - return $this->expireValue($key); - } - - /* UserCredentialsInterface */ - public function checkUserCredentials($username, $password) - { - if ($user = $this->getUser($username)) { - return $this->checkPassword($user, $password); - } - + return false; } - - // plaintext passwords are bad! Override this for your application - protected function checkPassword($user, $password) - { - return $user['password'] == $this->hashPassword($password); - } - - // use a secure hashing algorithm when storing passwords. Override this for your application - protected function hashPassword($password) - { - return sha1($password); - } - - public function getUserDetails($username) - { - return $this->getUser($username); - } - - public function getUser($username) - { - if (!$userInfo = $this->getValue($this->config['user_key'] . $username)) { - return false; - } - - // the default behavior is to use "username" as the user_id - return array_merge(array( - 'user_id' => $username, - ), $userInfo); - } - - public function setUser($username, $password, $first_name = null, $last_name = null) - { - $password = $this->hashPassword($password); - - return $this->setValue( - $this->config['user_key'] . $username, - compact('username', 'password', 'first_name', 'last_name') - ); - } - - /* ClientCredentialsInterface */ - public function checkClientCredentials($client_id, $client_secret = null) - { - if (!$client = $this->getClientDetails($client_id)) { - return false; - } - - return isset($client['client_secret']) - && $client['client_secret'] == $client_secret; - } - - public function isPublicClient($client_id) - { - if (!$client = $this->getClientDetails($client_id)) { - return false; - } - - return empty($client['client_secret']);; - } - - /* ClientInterface */ - public function getClientDetails($client_id) - { - return $this->getValue($this->config['client_key'] . $client_id); - } - - public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) - { - return $this->setValue( - $this->config['client_key'] . $client_id, - compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id') - ); - } - - public function checkRestrictedGrantType($client_id, $grant_type) - { - $details = $this->getClientDetails($client_id); - if (isset($details['grant_types'])) { - $grant_types = explode(' ', $details['grant_types']); - - return in_array($grant_type, (array) $grant_types); - } - - // if grant_types are not defined, then none are restricted - return true; - } - - /* RefreshTokenInterface */ - public function getRefreshToken($refresh_token) - { - return $this->getValue($this->config['refresh_token_key'] . $refresh_token); - } - - public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) - { - return $this->setValue( - $this->config['refresh_token_key'] . $refresh_token, - compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'), - $expires - ); - } - - public function unsetRefreshToken($refresh_token) - { - return $this->expireValue($this->config['refresh_token_key'] . $refresh_token); - } - - /* AccessTokenInterface */ - public function getAccessToken($access_token) - { - return $this->getValue($this->config['access_token_key'].$access_token); - } - - public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) - { - return $this->setValue( - $this->config['access_token_key'].$access_token, - compact('access_token', 'client_id', 'user_id', 'expires', 'scope'), - $expires - ); - } - - public function unsetAccessToken($access_token) - { - return $this->expireValue($this->config['access_token_key'] . $access_token); - } - - /* ScopeInterface */ - public function scopeExists($scope) - { - $scope = explode(' ', $scope); - - $result = $this->getValue($this->config['scope_key'].'supported:global'); - - $supportedScope = explode(' ', (string) $result); - - return (count(array_diff($scope, $supportedScope)) == 0); - } - - public function getDefaultScope($client_id = null) - { - if (is_null($client_id) || !$result = $this->getValue($this->config['scope_key'].'default:'.$client_id)) { - $result = $this->getValue($this->config['scope_key'].'default:global'); - } - - return $result; - } - - public function setScope($scope, $client_id = null, $type = 'supported') - { - if (!in_array($type, array('default', 'supported'))) { - throw new \InvalidArgumentException('"$type" must be one of "default", "supported"'); - } - - if (is_null($client_id)) { - $key = $this->config['scope_key'].$type.':global'; - } else { - $key = $this->config['scope_key'].$type.':'.$client_id; - } - - return $this->setValue($key, $scope); - } - - /*JWTBearerInterface */ - public function getClientKey($client_id, $subject) - { - if (!$jwt = $this->getValue($this->config['jwt_key'] . $client_id)) { - return false; - } - - if (isset($jwt['subject']) && $jwt['subject'] == $subject ) { - return $jwt['key']; - } - - return null; - } - - public function setClientKey($client_id, $key, $subject = null) - { - return $this->setValue($this->config['jwt_key'] . $client_id, array( - 'key' => $key, - 'subject' => $subject - )); - } - - /*ScopeInterface */ - public function getClientScope($client_id) - { - if (!$clientDetails = $this->getClientDetails($client_id)) { - return false; - } - - if (isset($clientDetails['scope'])) { - return $clientDetails['scope']; - } - - return null; - } - - public function getJti($client_id, $subject, $audience, $expiration, $jti) - { - //TODO: Needs cassandra implementation. - throw new \Exception('getJti() for the Cassandra driver is currently unimplemented.'); - } - - public function setJti($client_id, $subject, $audience, $expiration, $jti) - { - //TODO: Needs cassandra implementation. - throw new \Exception('setJti() for the Cassandra driver is currently unimplemented.'); - } - - /* PublicKeyInterface */ - public function getPublicKey($client_id = '') - { - $public_key = $this->getValue($this->config['public_key_key'] . $client_id); - if (is_array($public_key)) { - return $public_key['public_key']; - } - $public_key = $this->getValue($this->config['public_key_key']); - if (is_array($public_key)) { - return $public_key['public_key']; - } - } - - public function getPrivateKey($client_id = '') - { - $public_key = $this->getValue($this->config['public_key_key'] . $client_id); - if (is_array($public_key)) { - return $public_key['private_key']; - } - $public_key = $this->getValue($this->config['public_key_key']); - if (is_array($public_key)) { - return $public_key['private_key']; - } - } - - public function getEncryptionAlgorithm($client_id = null) - { - $public_key = $this->getValue($this->config['public_key_key'] . $client_id); - if (is_array($public_key)) { - return $public_key['encryption_algorithm']; - } - $public_key = $this->getValue($this->config['public_key_key']); - if (is_array($public_key)) { - return $public_key['encryption_algorithm']; - } - - return 'RS256'; - } - - /* UserClaimsInterface */ - public function getUserClaims($user_id, $claims) - { - $userDetails = $this->getUserDetails($user_id); - if (!is_array($userDetails)) { - return false; - } - - $claims = explode(' ', trim($claims)); - $userClaims = array(); - - // for each requested claim, if the user has the claim, set it in the response - $validClaims = explode(' ', self::VALID_CLAIMS); - foreach ($validClaims as $validClaim) { - if (in_array($validClaim, $claims)) { - if ($validClaim == 'address') { - // address is an object with subfields - $userClaims['address'] = $this->getUserClaim($validClaim, $userDetails['address'] ?: $userDetails); - } else { - $userClaims = array_merge($userClaims, $this->getUserClaim($validClaim, $userDetails)); - } - } - } - - return $userClaims; - } - - protected function getUserClaim($claim, $userDetails) - { - $userClaims = array(); - $claimValuesString = constant(sprintf('self::%s_CLAIM_VALUES', strtoupper($claim))); - $claimValues = explode(' ', $claimValuesString); - - foreach ($claimValues as $value) { - if ($value == 'email_verified') { - $userClaims[$value] = $userDetails[$value]=='true' ? true : false; - } else { - $userClaims[$value] = isset($userDetails[$value]) ? $userDetails[$value] : null; - } - } - - return $userClaims; - } - } diff --git a/src/OAuth2/Storage/CouchbaseDB.php b/src/OAuth2/Storage/CouchbaseDB.php index 1eb55f027..f29525dbc 100755 --- a/src/OAuth2/Storage/CouchbaseDB.php +++ b/src/OAuth2/Storage/CouchbaseDB.php @@ -1,331 +1,40 @@ - */ -class CouchbaseDB implements AuthorizationCodeInterface, - AccessTokenInterface, - ClientCredentialsInterface, - UserCredentialsInterface, - RefreshTokenInterface, - JwtBearerInterface, - OpenIDAuthorizationCodeInterface +class CouchbaseDB extends KeyValueAbstract { protected $db; - protected $config; - + public function __construct($connection, $config = array()) { if ($connection instanceof \Couchbase) { $this->db = $connection; + } else if (is_array($connection) && isset($connection['servers']) && is_array($connection['servers'])) { + $this->db = new \Couchbase($connection['servers'], (isset($connection['username']) ? $connection['username'] : ''), (isset($connection['password']) ? $connection['password'] : ''), $connection['bucket'], false); } else { - if (!is_array($connection) || !is_array($connection['servers'])) { - throw new \InvalidArgumentException('First argument to OAuth2\Storage\CouchbaseDB must be an instance of Couchbase or a configuration array containing a server array'); - } - - $this->db = new \Couchbase($connection['servers'], (!isset($connection['username'])) ? '' : $connection['username'], (!isset($connection['password'])) ? '' : $connection['password'], $connection['bucket'], false); - } - - $this->config = array_merge(array( - 'client_table' => 'oauth_clients', - 'access_token_table' => 'oauth_access_tokens', - 'refresh_token_table' => 'oauth_refresh_tokens', - 'code_table' => 'oauth_authorization_codes', - 'user_table' => 'oauth_users', - 'jwt_table' => 'oauth_jwt', - ), $config); - } - - // Helper function to access couchbase item by type: - protected function getObjectByType($name,$id) - { - return json_decode($this->db->get($this->config[$name].'-'.$id),true); - } - - // Helper function to set couchbase item by type: - protected function setObjectByType($name,$id,$array) - { - $array['type'] = $name; - - return $this->db->set($this->config[$name].'-'.$id,json_encode($array)); - } - - // Helper function to delete couchbase item by type, wait for persist to at least 1 node - protected function deleteObjectByType($name,$id) - { - $this->db->delete($this->config[$name].'-'.$id,"",1); - } - - /* ClientCredentialsInterface */ - public function checkClientCredentials($client_id, $client_secret = null) - { - if ($result = $this->getObjectByType('client_table',$client_id)) { - return $result['client_secret'] == $client_secret; - } - - return false; - } - - public function isPublicClient($client_id) - { - if (!$result = $this->getObjectByType('client_table',$client_id)) { - return false; - } - - return empty($result['client_secret']); - } - - /* ClientInterface */ - public function getClientDetails($client_id) - { - $result = $this->getObjectByType('client_table',$client_id); - - return is_null($result) ? false : $result; - } - - public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) - { - if ($this->getClientDetails($client_id)) { - - $this->setObjectByType('client_table',$client_id, array( - 'client_id' => $client_id, - 'client_secret' => $client_secret, - 'redirect_uri' => $redirect_uri, - 'grant_types' => $grant_types, - 'scope' => $scope, - 'user_id' => $user_id, - )); - } else { - $this->setObjectByType('client_table',$client_id, array( - 'client_id' => $client_id, - 'client_secret' => $client_secret, - 'redirect_uri' => $redirect_uri, - 'grant_types' => $grant_types, - 'scope' => $scope, - 'user_id' => $user_id, - )); + throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of Couchbase or a configuration array containing a servers array'); } - - return true; + + $this->config = array_merge($this->config, $config); } - - public function checkRestrictedGrantType($client_id, $grant_type) + + protected function _makeKey($table, $key) { - $details = $this->getClientDetails($client_id); - if (isset($details['grant_types'])) { - $grant_types = explode(' ', $details['grant_types']); - - return in_array($grant_type, $grant_types); - } - - // if grant_types are not defined, then none are restricted - return true; + return $table . '-' . $key; } - - /* AccessTokenInterface */ - public function getAccessToken($access_token) + + public function get($table, $key) { - $token = $this->getObjectByType('access_token_table',$access_token); - - return is_null($token) ? false : $token; + return json_decode($this->db->get($this->_makeKey($table, $key)), true); } - - public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + + public function set($table, $key, $value) { - // if it exists, update it. - if ($this->getAccessToken($access_token)) { - $this->setObjectByType('access_token_table',$access_token, array( - 'access_token' => $access_token, - 'client_id' => $client_id, - 'expires' => $expires, - 'user_id' => $user_id, - 'scope' => $scope - )); - } else { - $this->setObjectByType('access_token_table',$access_token, array( - 'access_token' => $access_token, - 'client_id' => $client_id, - 'expires' => $expires, - 'user_id' => $user_id, - 'scope' => $scope - )); - } - - return true; - } - - /* AuthorizationCodeInterface */ - public function getAuthorizationCode($code) - { - $code = $this->getObjectByType('code_table',$code); - - return is_null($code) ? false : $code; - } - - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) - { - // if it exists, update it. - if ($this->getAuthorizationCode($code)) { - $this->setObjectByType('code_table',$code, array( - 'authorization_code' => $code, - 'client_id' => $client_id, - 'user_id' => $user_id, - 'redirect_uri' => $redirect_uri, - 'expires' => $expires, - 'scope' => $scope, - 'id_token' => $id_token, - )); - } else { - $this->setObjectByType('code_table',$code,array( - 'authorization_code' => $code, - 'client_id' => $client_id, - 'user_id' => $user_id, - 'redirect_uri' => $redirect_uri, - 'expires' => $expires, - 'scope' => $scope, - 'id_token' => $id_token, - )); - } - - return true; + return $this->db->set($this->_makeKey($table, $key), json_encode($value)); } - - public function expireAuthorizationCode($code) - { - $this->deleteObjectByType('code_table',$code); - - return true; - } - - /* UserCredentialsInterface */ - public function checkUserCredentials($username, $password) - { - if ($user = $this->getUser($username)) { - return $this->checkPassword($user, $password); - } - - return false; - } - - public function getUserDetails($username) - { - if ($user = $this->getUser($username)) { - $user['user_id'] = $user['username']; - } - - return $user; - } - - /* RefreshTokenInterface */ - public function getRefreshToken($refresh_token) - { - $token = $this->getObjectByType('refresh_token_table',$refresh_token); - - return is_null($token) ? false : $token; - } - - public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) - { - $this->setObjectByType('refresh_token_table',$refresh_token, array( - 'refresh_token' => $refresh_token, - 'client_id' => $client_id, - 'user_id' => $user_id, - 'expires' => $expires, - 'scope' => $scope - )); - - return true; - } - - public function unsetRefreshToken($refresh_token) - { - $this->deleteObjectByType('refresh_token_table',$refresh_token); - - return true; - } - - // plaintext passwords are bad! Override this for your application - protected function checkPassword($user, $password) - { - return $user['password'] == $password; - } - - public function getUser($username) - { - $result = $this->getObjectByType('user_table',$username); - - return is_null($result) ? false : $result; - } - - public function setUser($username, $password, $firstName = null, $lastName = null) - { - if ($this->getUser($username)) { - $this->setObjectByType('user_table',$username, array( - 'username' => $username, - 'password' => $password, - 'first_name' => $firstName, - 'last_name' => $lastName - )); - - } else { - $this->setObjectByType('user_table',$username, array( - 'username' => $username, - 'password' => $password, - 'first_name' => $firstName, - 'last_name' => $lastName - )); - - } - - return true; - } - - public function getClientKey($client_id, $subject) - { - if (!$jwt = $this->getObjectByType('jwt_table',$client_id)) { - return false; - } - - if (isset($jwt['subject']) && $jwt['subject'] == $subject) { - return $jwt['key']; - } - - return false; - } - - public function getClientScope($client_id) - { - if (!$clientDetails = $this->getClientDetails($client_id)) { - return false; - } - - if (isset($clientDetails['scope'])) { - return $clientDetails['scope']; - } - - return null; - } - - public function getJti($client_id, $subject, $audience, $expiration, $jti) - { - //TODO: Needs couchbase implementation. - throw new \Exception('getJti() for the Couchbase driver is currently unimplemented.'); - } - - public function setJti($client_id, $subject, $audience, $expiration, $jti) + + public function delete($table, $key) { - //TODO: Needs couchbase implementation. - throw new \Exception('setJti() for the Couchbase driver is currently unimplemented.'); + return $this->db->delete($this->_makeKey($table, $key), '', 1); } } diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php new file mode 100644 index 000000000..b0bc9d0a6 --- /dev/null +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -0,0 +1,424 @@ + 'oauth_clients', + 'access_token_table' => 'oauth_access_tokens', + 'refresh_token_table' => 'oauth_refresh_tokens', + 'code_table' => 'oauth_authorization_codes', + 'user_table' => 'oauth_users', + 'jwt_table' => 'oauth_jwt', + 'jti_table' => 'oauth_jti', + 'scope_table' => 'oauth_scopes', + 'public_key_table' => 'oauth_public_keys' + ); + + /** + * + * @param string $table + * @param string $key + * @return mixed + */ + abstract protected function get($table, $key); + + /** + * + * @param string $table + * @param string $key + * @param mixed $value + */ + abstract protected function set($table, $key, $value); + + /** + * + * @param string $table + * @param string $key + */ + abstract protected function delete($table, $key); + + protected static function _hash($data) + { + return hash('sha256', json_encode($data)); + } + + // AuthorizationCodeInterface + public function getAuthorizationCode($authorization_code) + { + $result = $this->get($this->config['code_table'], $authorization_code); + if (is_array($result)) { + return $result; + } + + //return null; + return false; + } + + public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + return $this->set($this->config['code_table'], $authorization_code, compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); + } + + public function expireAuthorizationCode($authorization_code) + { + $this->delete($this->config['code_table'], $authorization_code); + } + + // AccessTokenInterface + public function getAccessToken($access_token) + { + $result = $this->get($this->config['access_token_table'], $access_token); + + if (is_array($result)) { + return $result; + } + + //return null; + return false; + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + return $this->set($this->config['access_token_table'], $access_token, compact('access_token', 'client_id', 'user_id', 'expires', 'scope')); + } + + public function unsetAccessToken($access_token) + { + $this->delete($this->config['access_token_table'], $access_token); + } + + // ClientCredentialsInterface + public function getClientDetails($client_id) + { + $client = $this->get($this->config['client_table'], $client_id); + + if (is_array($client)) { + return $client; + } + + return false; + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + return $this->set($this->config['client_table'], $client_id, compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id')); + } + + public function getClientScope($client_id) + { + $client = $this->getClientDetails($client_id); + + if (isset($client['scope'])) { + return $client['scope']; + } + + return false; + } + + public function checkRestrictedGrantType($client_id, $grant_type) + { + if (!$client = $this->getClientDetails($client_id)) { + return false; + } + + if (isset($client['grant_types'])) { + return in_array($grant_type, explode(' ', $client['grant_types'])); + } + + // if grant_types are not defined, then none are restricted + return true; + } + + public function checkClientCredentials($client_id, $client_secret = null) + { + $client = $this->getClientDetails($client_id); + return (isset($client['client_secret']) && $client['client_secret'] === $client_secret); + } + + public function isPublicClient($client_id) + { + $client = $this->getClientDetails($client_id); + return empty($client['client_secret']); + } + + // UserCredentialsInterface + public function getUserDetails($user_id) + { + $user = $this->get($this->config['user_table'], $user_id); + + if (is_array($user)) { + // the default behavior is to use "username" as the user_id + return array_merge(array( + 'user_id' => $user_id, + ), $user); + } + + return false; + } + + public function setUser($username, $password, $first_name = null, $last_name = null) + { + $password = $this->passwordHash($password); + return $this->set($this->config['user_table'], $username, compact('username', 'password', 'first_name', 'last_name')); + } + + // Override this for your application + protected function passwordVerify($password, $hash) + { + return sha1($password) === $hash; + // return password_verify($password, $hash); + } + + // Override this for your application + protected function passwordHash($password) + { + return sha1($password); + // return password_hash($password, PASSWORD_BCRYPT); + } + + public function checkUserCredentials($user_id, $password) + { + if (!$user = $this->getUserDetails($user_id)) { + return false; + } + + return $this->passwordVerify($password, $user['password']); + } + + // RefreshTokenInterface + public function getRefreshToken($refresh_token) + { + $result = $this->get($this->config['refresh_token_table'], $refresh_token); + + if (is_array($result)) { + return $result; + } + + // return null; + return false; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + return $this->set($this->config['refresh_token_table'], $refresh_token, compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope')); + } + + public function unsetRefreshToken($refresh_token) + { + $this->delete($this->config['refresh_token_table'], $refresh_token); + } + + // JwtBearerInterface + public function getClientKey($client_id, $subject) + { + $keydata = compact('client_id', 'subject'); + $keystring = self::_hash($keydata); + + $result = $this->get($this->config['jwt_table'], $keystring); + + if (is_string($result)) { + return $result; + } + + return false; + } + + public function setClientKey($client_id, $key, $subject = null) + { + $keydata = compact('client_id', 'subject'); + $keystring = self::_hash($keydata); + + return $this->set($this->config['jwt_table'], $keystring, $key); + } + + public function getJti($client_id, $subject, $audience, $expiration, $jti) + { + $data = array( + 'issuer' => $client_id, + 'subject' => $subject, + 'audience' => $audience, + 'expires' => $expiration, + 'jti' => $jti + ); + + $key = self::_hash($data); + + $result = $this->get($this->config['jti_table'], $key); + + if (is_array($result)) { + return $result; + } + + return null; + } + + public function setJti($client_id, $subject, $audience, $expiration, $jti) + { + $data = array( + 'issuer' => $client_id, + 'subject' => $subject, + 'audience' => $audience, + 'expires' => $expiration, + 'jti' => $jti + ); + + $key = self::_hash($data); + + $this->set($this->config['jti_table'], $key, $data); + } + + // ScopeInterface + public function scopeExists($scope) + { + $supportedScopes = $this->get($this->config['scope_table'], 'supported' . ':' . self::KEY_GLOBAL); + if (is_string($supportedScopes)) { + $supportedScopes = explode(' ', $supportedScopes); + $scope = explode(' ', $scope); + return (count(array_diff($scope, $supportedScopes)) === 0); + } + + return false; + } + + public function getDefaultScope($client_id = null) + { + if (is_null($client_id) || !$result = $this->get($this->config['scope_table'], 'default' . ':' . $client_id)) { + $result = $this->get($this->config['scope_table'], 'default' . ':' . self::KEY_GLOBAL); + } + + if (is_string($result)) { + return $result; + } + + return false; + } + + public function setScope($scope, $client_id = null, $type = 'supported') + { + if (!in_array($type, array('default', 'supported'), true)) { + throw new \InvalidArgumentException('"$type" must be one of "default", "supported"'); + } + + if (is_null($client_id)) { + $key = $type . ':' . self::KEY_GLOBAL; + } else { + $key = $type . ':' . $client_id; + } + + return $this->set($this->config['scope_table'], $key, $scope); + } + + // PublicKeyInterface + public function getPublicKey($client_id = null) + { + if (is_null($client_id) || !$result = $this->get($this->config['public_key_table'], $client_id)) { + $result = $this->get($this->config['public_key_table'], ''); + } + + if (is_array($result)) { + return $result['public_key']; + } + + return null; + } + + public function getPrivateKey($client_id = null) + { + if (is_null($client_id) || !$result = $this->get($this->config['public_key_table'], $client_id)) { + $result = $this->get($this->config['public_key_table'], ''); + } + + if (is_array($result)) { + return $result['private_key']; + } + + return null; + } + + public function getEncryptionAlgorithm($client_id = null) + { + if (is_null($client_id) || !$result = $this->get($this->config['public_key_table'], $client_id)) { + $result = $this->get($this->config['public_key_table'], ''); + } + + if (is_array($result)) { + return $result['encryption_algorithm']; + } + + return 'RS256'; + } + + // UserClaimsInterface + public function getUserClaims($user_id, $scope) + { + $userDetails = $this->getUserDetails($user_id); + if (!is_array($userDetails)) { + return false; + } + + $userClaims = array(); + $scopeValues = array_intersect(explode(' ', self::VALID_CLAIMS), explode(' ', $scope)); + foreach ($scopeValues as $scopeValue) { + $userClaims = array_merge($userClaims, $this->getUserClaim($scopeValue, $userDetails)); + } + + return $userClaims; + } + + protected function getUserClaim($scopeValue, $userDetails) + { + $SCOPE_ADDRESS = 'address'; // const + + $userClaims = array(); + $claimValuesString = constant(sprintf('self::%s_CLAIM_VALUES', strtoupper($scopeValue))); + $claimValues = explode(' ', $claimValuesString); + if ($scopeValue === $SCOPE_ADDRESS) { + if (isset($userDetails[$SCOPE_ADDRESS]) && is_array($userDetails[$SCOPE_ADDRESS])) { + $userDetails = $userDetails[$SCOPE_ADDRESS]; + } + $addressClaims = array(); + foreach ($claimValues as $claimValue) { + if (isset($userDetails[$claimValue])) { + $addressClaims[$claimValue] = $userDetails[$claimValue]; + } else { + $addressClaims[$claimValue] = null; // we should not return claims with null values, but this is for BC + } + } + if (count($addressClaims)) { + $userClaims[$SCOPE_ADDRESS] = $addressClaims; + } + } else { + foreach ($claimValues as $claimValue) { + if (isset($userDetails[$claimValue])) { + if (in_array($claimValue, array( + 'email_verified', + 'phone_number_verified' + ), true)) { + $userDetails[$claimValue] = (is_string($userDetails[$claimValue]) && !strcasecmp($userDetails[$claimValue], 'true')) || ((is_numeric($userDetails[$claimValue]) || is_bool($userDetails[$claimValue])) && (bool) $userDetails[$claimValue]); + } + $userClaims[$claimValue] = $userDetails[$claimValue]; + } else { + $userClaims[$claimValue] = null; // we should not return claims with null values, but this is for BC + } + } + } + return $userClaims; + } +} diff --git a/src/OAuth2/Storage/Memcached.php b/src/OAuth2/Storage/Memcached.php new file mode 100644 index 000000000..fe0fbd849 --- /dev/null +++ b/src/OAuth2/Storage/Memcached.php @@ -0,0 +1,49 @@ +db = $connection; + } else if (is_array($connection) && isset($connection['servers']) && is_array($connection['servers'])) { + $this->db = new \Memcached(); + $this->db->addServers($connection['servers']); + if (isset($connection['options']) && is_array($connection['options'])) { + $this->db->setOptions($connection['options']); + } + } else { + throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of Memcached or a configuration array containing a servers array'); + } + + $this->config = array_merge($this->config, $config); + } + + protected function _makeKey($table, $key) + { + return $table . '-' . $key; + } + + public function get($table, $key) + { + return $this->db->get($this->_makeKey($table, $key)); + } + + public function set($table, $key, $value) + { + return $this->db->set($this->_makeKey($table, $key), $value); + } + + public function delete($table, $key) + { + return $this->db->delete($this->_makeKey($table, $key)); + } +} diff --git a/src/OAuth2/Storage/MongoDB.php b/src/OAuth2/Storage/MongoDB.php new file mode 100644 index 000000000..3423bd4c9 --- /dev/null +++ b/src/OAuth2/Storage/MongoDB.php @@ -0,0 +1,70 @@ +db = $connection; + } else + if (is_array($connection)) { + $server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']); + $this->db = new \MongoDB\Driver\Manager($server); + } else { + throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of MongoDB\Driver\Manager or a configuration array'); + } + + $this->config = array_merge($this->config, array( + 'database' => $connection['database'] + ), $config); + } + + public function get($table, $key) + { + try { + $query = new \MongoDB\Driver\Query(array('_id' => $key), array('limit' => 1)); + $cursor = $this->db->executeQuery("{$this->config['database']}.{$this->config[$table]}", $query); + $data = $cursor->toArray(); + + if (isset($data[0])) { + return $data[0]->value; + } + + return null; + } catch (\MongoDB\Driver\Exception\Exception $ex) {} + + return false; + } + + public function set($table, $key, $value) + { + try { + $bulk = new \MongoDB\Driver\BulkWrite(); + $bulk->update(array('_id' => $key), array('$set' => array('value' => $value)), array('upsert' => true, 'limit' => 1)); + $result = $this->db->executeBulkWrite("{$this->config['database']}.{$this->config[$table]}", $bulk); + return true; + } catch (\MongoDB\Driver\Exception\Exception $ex) {} + + return false; + } + + public function delete($table, $key) + { + try { + $bulk = new \MongoDB\Driver\BulkWrite(); + $bulk->delete(array('_id' => $key), array('limit' => 1)); + $result = $this->db->executeBulkWrite("{$this->config['database']}.{$this->config[$table]}", $bulk); + return true; + } catch (\MongoDB\Driver\Exception\Exception $ex) {} + + return false; + } +} From 1cb1a360c1673e1b412546d89e1d7cd8760633aa Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Tue, 12 Jan 2016 13:10:08 +0400 Subject: [PATCH 02/16] Add setServerKey() function to setup test data --- src/OAuth2/Storage/KeyValueAbstract.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php index b0bc9d0a6..073d78057 100644 --- a/src/OAuth2/Storage/KeyValueAbstract.php +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -365,6 +365,15 @@ public function getEncryptionAlgorithm($client_id = null) return 'RS256'; } + public function setServerKey($client_id, $public_key, $private_key, $encryption_algorithm) + { + if (is_null($client_id)) { + $client_id = ''; + } + + return $this->set($this->config['public_key_table'], $client_id, compact('client_id', 'public_key', 'private_key', 'encryption_algorithm')); + } + // UserClaimsInterface public function getUserClaims($user_id, $scope) { From 2bed8b9f4796627bd08be13391135294ab70b232 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Tue, 12 Jan 2016 13:12:17 +0400 Subject: [PATCH 03/16] Add email, email_verified params to function setUser --- src/OAuth2/Storage/KeyValueAbstract.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php index 073d78057..cb2d3b5bf 100644 --- a/src/OAuth2/Storage/KeyValueAbstract.php +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -173,19 +173,19 @@ public function getUserDetails($user_id) return false; } - public function setUser($username, $password, $first_name = null, $last_name = null) + public function setUser($username, $password, $first_name = null, $last_name = null, $email = null, $email_verified = null) { $password = $this->passwordHash($password); - return $this->set($this->config['user_table'], $username, compact('username', 'password', 'first_name', 'last_name')); + return $this->set($this->config['user_table'], $username, compact('username', 'password', 'first_name', 'last_name', 'email', 'email_verified')); } - + // Override this for your application protected function passwordVerify($password, $hash) { return sha1($password) === $hash; // return password_verify($password, $hash); } - + // Override this for your application protected function passwordHash($password) { From c97bcbd5e29cae2e5485d7c1239289086495ffe5 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Tue, 12 Jan 2016 13:14:39 +0400 Subject: [PATCH 04/16] Redis, added tests --- src/OAuth2/Storage/Redis.php | 338 ++++---------------------- test/lib/OAuth2/Storage/Bootstrap.php | 4 +- 2 files changed, 51 insertions(+), 291 deletions(-) diff --git a/src/OAuth2/Storage/Redis.php b/src/OAuth2/Storage/Redis.php index b9a6939b2..e65d1d722 100644 --- a/src/OAuth2/Storage/Redis.php +++ b/src/OAuth2/Storage/Redis.php @@ -1,317 +1,75 @@ - * $storage = new OAuth2\Storage\Redis($redis); - * $storage->setClientDetails($client_id, $client_secret, $redirect_uri); - * - */ -class Redis implements AuthorizationCodeInterface, - AccessTokenInterface, - ClientCredentialsInterface, - UserCredentialsInterface, - RefreshTokenInterface, - JwtBearerInterface, - ScopeInterface, - OpenIDAuthorizationCodeInterface +class Redis extends KeyValueAbstract { - private $cache; + protected $db; + + public function __construct($connection = array(), array $config = array()) + { + if ($connection instanceof \Predis\Client) { + $this->db = $connection; + } else if (is_array($connection)) { + $connection = array_merge(array( + 'parameters' => null, + 'options' => null, + ), $connection); + + $this->db = new \Predis\Client($connection['parameters'], $connection['options']); + } else { + throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of Predis\Client or a configuration array'); + } + + $this->config = array_merge($this->config, array( + 'expire' => 0, + ), $config); + } - /* The redis client */ - protected $redis; - - /* Configuration array */ - protected $config; - - /** - * Redis Storage! - * - * @param \Predis\Client $redis - * @param array $config - */ - public function __construct($redis, $config=array()) + protected function _makeKey($table, $key) { - $this->redis = $redis; - $this->config = array_merge(array( - 'client_key' => 'oauth_clients:', - 'access_token_key' => 'oauth_access_tokens:', - 'refresh_token_key' => 'oauth_refresh_tokens:', - 'code_key' => 'oauth_authorization_codes:', - 'user_key' => 'oauth_users:', - 'jwt_key' => 'oauth_jwt:', - 'scope_key' => 'oauth_scopes:', - ), $config); + return $table . ':' . $key; } - protected function getValue($key) + protected function get($table, $key) { - if ( isset($this->cache[$key]) ) { + $key = $this->_makeKey($table, $key); + + if (isset($this->cache[$key])) { return $this->cache[$key]; } - $value = $this->redis->get($key); - if ( isset($value) ) { + + $value = $this->db->get($key); + if (isset($value)) { return json_decode($value, true); - } else { - return false; } + + return false; } - protected function setValue($key, $value, $expire=0) + protected function set($table, $key, $value) { + $key = $this->_makeKey($table, $key); + $this->cache[$key] = $value; + $str = json_encode($value); - if ($expire > 0) { - $seconds = $expire - time(); - $ret = $this->redis->setex($key, $seconds, $str); + if ($this->config['expire'] > 0) { + $seconds = $this->config['expire'] - time(); + return (bool)$this->db->setex($key, $seconds, $str); } else { - $ret = $this->redis->set($key, $str); + return (bool)$this->db->set($key, $str); } - - // check that the key was set properly - // if this fails, an exception will usually thrown, so this step isn't strictly necessary - return is_bool($ret) ? $ret : $ret->getPayload() == 'OK'; + + return false; } - protected function expireValue($key) + protected function delete($table, $key) { + $key = $this->_makeKey($table, $key); + unset($this->cache[$key]); - - return $this->redis->del($key); - } - - /* AuthorizationCodeInterface */ - public function getAuthorizationCode($code) - { - return $this->getValue($this->config['code_key'] . $code); - } - - public function setAuthorizationCode($authorization_code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) - { - return $this->setValue( - $this->config['code_key'] . $authorization_code, - compact('authorization_code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'), - $expires - ); - } - - public function expireAuthorizationCode($code) - { - $key = $this->config['code_key'] . $code; - unset($this->cache[$key]); - - return $this->expireValue($key); - } - - /* UserCredentialsInterface */ - public function checkUserCredentials($username, $password) - { - $user = $this->getUserDetails($username); - - return $user && $user['password'] === $password; - } - - public function getUserDetails($username) - { - return $this->getUser($username); - } - - public function getUser($username) - { - if (!$userInfo = $this->getValue($this->config['user_key'] . $username)) { - return false; - } - - // the default behavior is to use "username" as the user_id - return array_merge(array( - 'user_id' => $username, - ), $userInfo); - } - - public function setUser($username, $password, $first_name = null, $last_name = null) - { - return $this->setValue( - $this->config['user_key'] . $username, - compact('username', 'password', 'first_name', 'last_name') - ); - } - - /* ClientCredentialsInterface */ - public function checkClientCredentials($client_id, $client_secret = null) - { - if (!$client = $this->getClientDetails($client_id)) { - return false; - } - - return isset($client['client_secret']) - && $client['client_secret'] == $client_secret; - } - - public function isPublicClient($client_id) - { - if (!$client = $this->getClientDetails($client_id)) { - return false; - } - - return empty($client['client_secret']); - } - - /* ClientInterface */ - public function getClientDetails($client_id) - { - return $this->getValue($this->config['client_key'] . $client_id); - } - - public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) - { - return $this->setValue( - $this->config['client_key'] . $client_id, - compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id') - ); - } - - public function checkRestrictedGrantType($client_id, $grant_type) - { - $details = $this->getClientDetails($client_id); - if (isset($details['grant_types'])) { - $grant_types = explode(' ', $details['grant_types']); - - return in_array($grant_type, (array) $grant_types); - } - - // if grant_types are not defined, then none are restricted - return true; - } - - /* RefreshTokenInterface */ - public function getRefreshToken($refresh_token) - { - return $this->getValue($this->config['refresh_token_key'] . $refresh_token); - } - - public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) - { - return $this->setValue( - $this->config['refresh_token_key'] . $refresh_token, - compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'), - $expires - ); - } - - public function unsetRefreshToken($refresh_token) - { - return $this->expireValue($this->config['refresh_token_key'] . $refresh_token); - } - - /* AccessTokenInterface */ - public function getAccessToken($access_token) - { - return $this->getValue($this->config['access_token_key'].$access_token); - } - - public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) - { - return $this->setValue( - $this->config['access_token_key'].$access_token, - compact('access_token', 'client_id', 'user_id', 'expires', 'scope'), - $expires - ); - } - - public function unsetAccessToken($access_token) - { - return $this->expireValue($this->config['access_token_key'] . $access_token); - } - - /* ScopeInterface */ - public function scopeExists($scope) - { - $scope = explode(' ', $scope); - - $result = $this->getValue($this->config['scope_key'].'supported:global'); - - $supportedScope = explode(' ', (string) $result); - - return (count(array_diff($scope, $supportedScope)) == 0); - } - - public function getDefaultScope($client_id = null) - { - if (is_null($client_id) || !$result = $this->getValue($this->config['scope_key'].'default:'.$client_id)) { - $result = $this->getValue($this->config['scope_key'].'default:global'); - } - - return $result; - } - - public function setScope($scope, $client_id = null, $type = 'supported') - { - if (!in_array($type, array('default', 'supported'))) { - throw new \InvalidArgumentException('"$type" must be one of "default", "supported"'); - } - - if (is_null($client_id)) { - $key = $this->config['scope_key'].$type.':global'; - } else { - $key = $this->config['scope_key'].$type.':'.$client_id; - } - - return $this->setValue($key, $scope); - } - - /*JWTBearerInterface */ - public function getClientKey($client_id, $subject) - { - if (!$jwt = $this->getValue($this->config['jwt_key'] . $client_id)) { - return false; - } - - if (isset($jwt['subject']) && $jwt['subject'] == $subject) { - return $jwt['key']; - } - - return null; - } - - public function setClientKey($client_id, $key, $subject = null) - { - return $this->setValue($this->config['jwt_key'] . $client_id, array( - 'key' => $key, - 'subject' => $subject - )); - } - - public function getClientScope($client_id) - { - if (!$clientDetails = $this->getClientDetails($client_id)) { - return false; - } - - if (isset($clientDetails['scope'])) { - return $clientDetails['scope']; - } - - return null; - } - - public function getJti($client_id, $subject, $audience, $expiration, $jti) - { - //TODO: Needs redis implementation. - throw new \Exception('getJti() for the Redis driver is currently unimplemented.'); - } - - public function setJti($client_id, $subject, $audience, $expiration, $jti) - { - //TODO: Needs redis implementation. - throw new \Exception('setJti() for the Redis driver is currently unimplemented.'); + + return $this->db->del($key); } } diff --git a/test/lib/OAuth2/Storage/Bootstrap.php b/test/lib/OAuth2/Storage/Bootstrap.php index 4ac9022b1..3faafdce5 100755 --- a/test/lib/OAuth2/Storage/Bootstrap.php +++ b/test/lib/OAuth2/Storage/Bootstrap.php @@ -480,7 +480,7 @@ private function createRedisDb(Redis $storage) $storage->setClientDetails("oauth_test_client", "testpass", "http://example.com", 'implicit password'); $storage->setAccessToken("testtoken", "Some Client", '', time() + 1000); $storage->setAuthorizationCode("testcode", "Some Client", '', '', time() + 1000); - $storage->setUser("testuser", "password"); + $storage->setUser('testuser', 'password', null, null, 'testuser@test.com', true); $storage->setScope('supportedscope1 supportedscope2 supportedscope3 supportedscope4'); $storage->setScope('defaultscope1 defaultscope2', null, 'default'); @@ -498,6 +498,8 @@ private function createRedisDb(Redis $storage) $storage->setScope('clientscope3', 'Test Default Scope Client ID 2', 'default'); $storage->setClientKey('oauth_test_client', $this->getTestPublicKey(), 'test_subject'); + + $storage->setServerKey(null, $this->getTestPublicKey(), $this->getTestPrivateKey(), 'RS256'); } public function removeMongoDb(\MongoDB $db) From c00852782d9fa218029d77cd4bd5f9625a85db86 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Wed, 13 Jan 2016 14:34:26 +0400 Subject: [PATCH 05/16] PSR-2 code style --- src/OAuth2/Storage/Cassandra.php | 12 ++++++------ src/OAuth2/Storage/CouchbaseDB.php | 11 ++++++----- src/OAuth2/Storage/KeyValueAbstract.php | 8 +++++--- src/OAuth2/Storage/MongoDB.php | 22 ++++++++++++---------- src/OAuth2/Storage/Redis.php | 6 ++++-- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/OAuth2/Storage/Cassandra.php b/src/OAuth2/Storage/Cassandra.php index a5341cd50..2972d32d7 100644 --- a/src/OAuth2/Storage/Cassandra.php +++ b/src/OAuth2/Storage/Cassandra.php @@ -1,5 +1,4 @@ 'oauth2', - 'servers' => null, + 'servers' => null ), $connection); $this->db = new ConnectionPool($connection['keyspace'], $connection['servers']); @@ -28,15 +29,15 @@ public function __construct($connection = array(), array $config = array()) $this->config = array_merge($this->config, array( 'column_family' => 'auth', - 'expire' => 0, + 'expire' => 0 ), $config); } - + protected function _makeKey($table, $key) { return $table . ':' . $key; } - + protected function get($table, $key) { $key = $this->_makeKey($table, $key); @@ -50,7 +51,6 @@ protected function get($table, $key) $value = $cf->get($key, new ColumnSlice("", "")); return json_decode(current($value), true); } catch (\Exception $e) { - } return false; diff --git a/src/OAuth2/Storage/CouchbaseDB.php b/src/OAuth2/Storage/CouchbaseDB.php index f29525dbc..080472244 100755 --- a/src/OAuth2/Storage/CouchbaseDB.php +++ b/src/OAuth2/Storage/CouchbaseDB.php @@ -3,8 +3,9 @@ class CouchbaseDB extends KeyValueAbstract { + protected $db; - + public function __construct($connection, $config = array()) { if ($connection instanceof \Couchbase) { @@ -17,22 +18,22 @@ public function __construct($connection, $config = array()) $this->config = array_merge($this->config, $config); } - + protected function _makeKey($table, $key) { return $table . '-' . $key; } - + public function get($table, $key) { return json_decode($this->db->get($this->_makeKey($table, $key)), true); } - + public function set($table, $key, $value) { return $this->db->set($this->_makeKey($table, $key), json_encode($value)); } - + public function delete($table, $key) { return $this->db->delete($this->_makeKey($table, $key), '', 1); diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php index cb2d3b5bf..13a199b18 100644 --- a/src/OAuth2/Storage/KeyValueAbstract.php +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -44,6 +44,7 @@ abstract protected function get($table, $key); * @param string $table * @param string $key * @param mixed $value + * @return bool */ abstract protected function set($table, $key, $value); @@ -51,6 +52,7 @@ abstract protected function set($table, $key, $value); * * @param string $table * @param string $key + * @return bool */ abstract protected function delete($table, $key); @@ -58,7 +60,7 @@ protected static function _hash($data) { return hash('sha256', json_encode($data)); } - + // AuthorizationCodeInterface public function getAuthorizationCode($authorization_code) { @@ -166,7 +168,7 @@ public function getUserDetails($user_id) if (is_array($user)) { // the default behavior is to use "username" as the user_id return array_merge(array( - 'user_id' => $user_id, + 'user_id' => $user_id ), $user); } @@ -309,7 +311,7 @@ public function getDefaultScope($client_id = null) return false; } - + public function setScope($scope, $client_id = null, $type = 'supported') { if (!in_array($type, array('default', 'supported'), true)) { diff --git a/src/OAuth2/Storage/MongoDB.php b/src/OAuth2/Storage/MongoDB.php index 3423bd4c9..bb80d0563 100644 --- a/src/OAuth2/Storage/MongoDB.php +++ b/src/OAuth2/Storage/MongoDB.php @@ -14,13 +14,12 @@ public function __construct($connection, $config = array()) if ($connection instanceof \MongoDB\Driver\Manager) { $this->db = $connection; - } else - if (is_array($connection)) { - $server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']); - $this->db = new \MongoDB\Driver\Manager($server); - } else { - throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of MongoDB\Driver\Manager or a configuration array'); - } + } else if (is_array($connection)) { + $server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']); + $this->db = new \MongoDB\Driver\Manager($server); + } else { + throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of MongoDB\Driver\Manager or a configuration array'); + } $this->config = array_merge($this->config, array( 'database' => $connection['database'] @@ -39,7 +38,8 @@ public function get($table, $key) } return null; - } catch (\MongoDB\Driver\Exception\Exception $ex) {} + } catch (\MongoDB\Driver\Exception\Exception $ex) { + } return false; } @@ -51,7 +51,8 @@ public function set($table, $key, $value) $bulk->update(array('_id' => $key), array('$set' => array('value' => $value)), array('upsert' => true, 'limit' => 1)); $result = $this->db->executeBulkWrite("{$this->config['database']}.{$this->config[$table]}", $bulk); return true; - } catch (\MongoDB\Driver\Exception\Exception $ex) {} + } catch (\MongoDB\Driver\Exception\Exception $ex) { + } return false; } @@ -63,7 +64,8 @@ public function delete($table, $key) $bulk->delete(array('_id' => $key), array('limit' => 1)); $result = $this->db->executeBulkWrite("{$this->config['database']}.{$this->config[$table]}", $bulk); return true; - } catch (\MongoDB\Driver\Exception\Exception $ex) {} + } catch (\MongoDB\Driver\Exception\Exception $ex) { + } return false; } diff --git a/src/OAuth2/Storage/Redis.php b/src/OAuth2/Storage/Redis.php index e65d1d722..f2db0fafb 100644 --- a/src/OAuth2/Storage/Redis.php +++ b/src/OAuth2/Storage/Redis.php @@ -3,7 +3,9 @@ class Redis extends KeyValueAbstract { + private $cache; + protected $db; public function __construct($connection = array(), array $config = array()) @@ -13,7 +15,7 @@ public function __construct($connection = array(), array $config = array()) } else if (is_array($connection)) { $connection = array_merge(array( 'parameters' => null, - 'options' => null, + 'options' => null ), $connection); $this->db = new \Predis\Client($connection['parameters'], $connection['options']); @@ -22,7 +24,7 @@ public function __construct($connection = array(), array $config = array()) } $this->config = array_merge($this->config, array( - 'expire' => 0, + 'expire' => 0 ), $config); } From 1175bc4cb1cfd6311c656a7e96347abd5a58185d Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Wed, 13 Jan 2016 14:37:43 +0400 Subject: [PATCH 06/16] added parseBool() --- src/OAuth2/Storage/KeyValueAbstract.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php index 13a199b18..18b42177a 100644 --- a/src/OAuth2/Storage/KeyValueAbstract.php +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -399,30 +399,27 @@ protected function getUserClaim($scopeValue, $userDetails) $userClaims = array(); $claimValuesString = constant(sprintf('self::%s_CLAIM_VALUES', strtoupper($scopeValue))); - $claimValues = explode(' ', $claimValuesString); + $claimValues = explode(' ', $claimValuesString); // claims to get from user details if ($scopeValue === $SCOPE_ADDRESS) { if (isset($userDetails[$SCOPE_ADDRESS]) && is_array($userDetails[$SCOPE_ADDRESS])) { $userDetails = $userDetails[$SCOPE_ADDRESS]; } $addressClaims = array(); foreach ($claimValues as $claimValue) { - if (isset($userDetails[$claimValue])) { + if (isset($userDetails[$claimValue])) { // if claim exists in user details add it to address claim $addressClaims[$claimValue] = $userDetails[$claimValue]; } else { $addressClaims[$claimValue] = null; // we should not return claims with null values, but this is for BC } } - if (count($addressClaims)) { + if (count($addressClaims)) { // if address claim not empty add it to return claims $userClaims[$SCOPE_ADDRESS] = $addressClaims; } } else { foreach ($claimValues as $claimValue) { - if (isset($userDetails[$claimValue])) { - if (in_array($claimValue, array( - 'email_verified', - 'phone_number_verified' - ), true)) { - $userDetails[$claimValue] = (is_string($userDetails[$claimValue]) && !strcasecmp($userDetails[$claimValue], 'true')) || ((is_numeric($userDetails[$claimValue]) || is_bool($userDetails[$claimValue])) && (bool) $userDetails[$claimValue]); + if (isset($userDetails[$claimValue])) { // if claim exists in user details add it to return claims + if (in_array($claimValue, array('email_verified', 'phone_number_verified'), true)) { + $userDetails[$claimValue] = self::parseBool($userDetails[$claimValue]); } $userClaims[$claimValue] = $userDetails[$claimValue]; } else { @@ -432,4 +429,9 @@ protected function getUserClaim($scopeValue, $userDetails) } return $userClaims; } + + protected static function parseBool($value) + { + return is_string($value) ? filter_var($value, FILTER_VALIDATE_BOOLEAN) : (bool)$value; + } } From c1dc98dbdac859472bac16d1f957805b470d60c8 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Thu, 14 Jan 2016 15:34:00 +0400 Subject: [PATCH 07/16] constants instead of magic strings --- src/OAuth2/Storage/KeyValueAbstract.php | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php index 18b42177a..78934310f 100644 --- a/src/OAuth2/Storage/KeyValueAbstract.php +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -17,6 +17,10 @@ abstract class KeyValueAbstract implements OpenIDAuthorizationCodeInterface { + const KEY_NULL = ''; // TODO we should replace it to KEY_GLOBAL, it is only for backward compatibility with old Cassandra storage adapter (PublicKeyInterface functions) + const KEY_DELIMITER = ':'; + const KEY_DEFAULT = 'default'; + const KEY_SUPPORTED = 'supported'; const KEY_GLOBAL = 'global'; protected $config = array( @@ -289,7 +293,7 @@ public function setJti($client_id, $subject, $audience, $expiration, $jti) // ScopeInterface public function scopeExists($scope) { - $supportedScopes = $this->get($this->config['scope_table'], 'supported' . ':' . self::KEY_GLOBAL); + $supportedScopes = $this->get($this->config['scope_table'], self::KEY_SUPPORTED . self::KEY_DELIMITER . self::KEY_GLOBAL); if (is_string($supportedScopes)) { $supportedScopes = explode(' ', $supportedScopes); $scope = explode(' ', $scope); @@ -301,8 +305,8 @@ public function scopeExists($scope) public function getDefaultScope($client_id = null) { - if (is_null($client_id) || !$result = $this->get($this->config['scope_table'], 'default' . ':' . $client_id)) { - $result = $this->get($this->config['scope_table'], 'default' . ':' . self::KEY_GLOBAL); + if (is_null($client_id) || !$result = $this->get($this->config['scope_table'], self::KEY_DEFAULT . self::KEY_DELIMITER . $client_id)) { + $result = $this->get($this->config['scope_table'], self::KEY_DEFAULT . self::KEY_DELIMITER . self::KEY_GLOBAL); } if (is_string($result)) { @@ -312,16 +316,16 @@ public function getDefaultScope($client_id = null) return false; } - public function setScope($scope, $client_id = null, $type = 'supported') + public function setScope($scope, $client_id = null, $type = self::KEY_SUPPORTED) { - if (!in_array($type, array('default', 'supported'), true)) { - throw new \InvalidArgumentException('"$type" must be one of "default", "supported"'); + if (!in_array($type, array(self::KEY_DEFAULT, self::KEY_SUPPORTED), true)) { + throw new \InvalidArgumentException('"$type" must be one of ("' . self::KEY_DEFAULT . '", "' . self::KEY_SUPPORTED . '")'); } if (is_null($client_id)) { - $key = $type . ':' . self::KEY_GLOBAL; + $key = $type . self::KEY_DELIMITER . self::KEY_GLOBAL; } else { - $key = $type . ':' . $client_id; + $key = $type . self::KEY_DELIMITER . $client_id; } return $this->set($this->config['scope_table'], $key, $scope); @@ -331,7 +335,7 @@ public function setScope($scope, $client_id = null, $type = 'supported') public function getPublicKey($client_id = null) { if (is_null($client_id) || !$result = $this->get($this->config['public_key_table'], $client_id)) { - $result = $this->get($this->config['public_key_table'], ''); + $result = $this->get($this->config['public_key_table'], self::KEY_NULL); } if (is_array($result)) { @@ -344,7 +348,7 @@ public function getPublicKey($client_id = null) public function getPrivateKey($client_id = null) { if (is_null($client_id) || !$result = $this->get($this->config['public_key_table'], $client_id)) { - $result = $this->get($this->config['public_key_table'], ''); + $result = $this->get($this->config['public_key_table'], self::KEY_NULL); } if (is_array($result)) { @@ -357,7 +361,7 @@ public function getPrivateKey($client_id = null) public function getEncryptionAlgorithm($client_id = null) { if (is_null($client_id) || !$result = $this->get($this->config['public_key_table'], $client_id)) { - $result = $this->get($this->config['public_key_table'], ''); + $result = $this->get($this->config['public_key_table'], self::KEY_NULL); } if (is_array($result)) { @@ -370,7 +374,7 @@ public function getEncryptionAlgorithm($client_id = null) public function setServerKey($client_id, $public_key, $private_key, $encryption_algorithm) { if (is_null($client_id)) { - $client_id = ''; + $client_id = self::KEY_NULL; } return $this->set($this->config['public_key_table'], $client_id, compact('client_id', 'public_key', 'private_key', 'encryption_algorithm')); From 21713e56a307139d22111feb89cec57e6bb91137 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Thu, 14 Jan 2016 16:18:09 +0400 Subject: [PATCH 08/16] Memory --- src/OAuth2/Storage/Memory.php | 405 +++++---------------------- test/config/storage.json | 1 + test/lib/OAuth2/Storage/BaseTest.php | 3 - 3 files changed, 76 insertions(+), 333 deletions(-) diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 4f0859deb..1ea373893 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -1,369 +1,114 @@ - */ -class Memory implements AuthorizationCodeInterface, - UserCredentialsInterface, - UserClaimsInterface, - AccessTokenInterface, - ClientCredentialsInterface, - RefreshTokenInterface, - JwtBearerInterface, - ScopeInterface, - PublicKeyInterface, - OpenIDAuthorizationCodeInterface +class Memory extends KeyValueAbstract { - public $authorizationCodes; - public $userCredentials; - public $clientCredentials; - public $refreshTokens; - public $accessTokens; - public $jwt; - public $jti; - public $supportedScopes; - public $defaultScope; - public $keys; - - public function __construct($params = array()) - { - $params = array_merge(array( - 'authorization_codes' => array(), - 'user_credentials' => array(), - 'client_credentials' => array(), - 'refresh_tokens' => array(), - 'access_tokens' => array(), - 'jwt' => array(), - 'jti' => array(), - 'default_scope' => null, - 'supported_scopes' => array(), - 'keys' => array(), - ), $params); - - $this->authorizationCodes = $params['authorization_codes']; - $this->userCredentials = $params['user_credentials']; - $this->clientCredentials = $params['client_credentials']; - $this->refreshTokens = $params['refresh_tokens']; - $this->accessTokens = $params['access_tokens']; - $this->jwt = $params['jwt']; - $this->jti = $params['jti']; - $this->supportedScopes = $params['supported_scopes']; - $this->defaultScope = $params['default_scope']; - $this->keys = $params['keys']; - } - - /* AuthorizationCodeInterface */ - public function getAuthorizationCode($code) - { - if (!isset($this->authorizationCodes[$code])) { - return false; - } - - return array_merge(array( - 'authorization_code' => $code, - ), $this->authorizationCodes[$code]); - } - public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) - { - $this->authorizationCodes[$code] = compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token'); - - return true; - } + protected $oauth_clients = array(); - public function setAuthorizationCodes($authorization_codes) - { - $this->authorizationCodes = $authorization_codes; - } + protected $oauth_access_tokens = array(); - public function expireAuthorizationCode($code) - { - unset($this->authorizationCodes[$code]); - } + protected $oauth_refresh_tokens = array(); - /* UserCredentialsInterface */ - public function checkUserCredentials($username, $password) - { - $userDetails = $this->getUserDetails($username); + protected $oauth_authorization_codes = array(); - return $userDetails && $userDetails['password'] && $userDetails['password'] === $password; - } + protected $oauth_users = array(); - public function setUser($username, $password, $firstName = null, $lastName = null) - { - $this->userCredentials[$username] = array( - 'password' => $password, - 'first_name' => $firstName, - 'last_name' => $lastName, - ); + protected $oauth_jwt = array(); - return true; - } + protected $oauth_jti = array(); - public function getUserDetails($username) - { - if (!isset($this->userCredentials[$username])) { - return false; - } + protected $oauth_scopes = array(); - return array_merge(array( - 'user_id' => $username, - 'password' => null, - 'first_name' => null, - 'last_name' => null, - ), $this->userCredentials[$username]); - } + protected $oauth_public_keys = array(); - /* UserClaimsInterface */ - public function getUserClaims($user_id, $claims) + public function __construct($params = array()) { - if (!$userDetails = $this->getUserDetails($user_id)) { - return false; + if (isset($params['authorization_codes']) && is_array($params['authorization_codes'])) { + foreach ($params['authorization_codes'] as $key => $val) { + $val['authorization_code'] = $key; + $this->oauth_authorization_codes[$key] = $val; + } } - - $claims = explode(' ', trim($claims)); - $userClaims = array(); - - // for each requested claim, if the user has the claim, set it in the response - $validClaims = explode(' ', self::VALID_CLAIMS); - foreach ($validClaims as $validClaim) { - if (in_array($validClaim, $claims)) { - if ($validClaim == 'address') { - // address is an object with subfields - $userClaims['address'] = $this->getUserClaim($validClaim, $userDetails['address'] ?: $userDetails); - } else { - $userClaims = array_merge($this->getUserClaim($validClaim, $userDetails)); - } + + if (isset($params['client_credentials']) && is_array($params['client_credentials'])) { + foreach ($params['client_credentials'] as $key => $val) { + $val['client_id'] = $key; + $this->oauth_clients[$key] = $val; } } - - return $userClaims; - } - - protected function getUserClaim($claim, $userDetails) - { - $userClaims = array(); - $claimValuesString = constant(sprintf('self::%s_CLAIM_VALUES', strtoupper($claim))); - $claimValues = explode(' ', $claimValuesString); - - foreach ($claimValues as $value) { - $userClaims[$value] = isset($userDetails[$value]) ? $userDetails[$value] : null; + + if (isset($params['user_credentials']) && is_array($params['user_credentials'])) { + foreach ($params['user_credentials'] as $key => $val) { + $val['username'] = $key; + $this->oauth_users[$key] = $val; + } } - - return $userClaims; - } - - /* ClientCredentialsInterface */ - public function checkClientCredentials($client_id, $client_secret = null) - { - return isset($this->clientCredentials[$client_id]['client_secret']) && $this->clientCredentials[$client_id]['client_secret'] === $client_secret; - } - - public function isPublicClient($client_id) - { - if (!isset($this->clientCredentials[$client_id])) { - return false; + + if (isset($params['refresh_tokens']) && is_array($params['refresh_tokens'])) { + foreach ($params['refresh_tokens'] as $key => $val) { + $val['refresh_token'] = $key; + $this->oauth_refresh_tokens[$key] = $val; + } } - - return empty($this->clientCredentials[$client_id]['client_secret']); - } - - /* ClientInterface */ - public function getClientDetails($client_id) - { - if (!isset($this->clientCredentials[$client_id])) { - return false; + + if (isset($params['access_tokens']) && is_array($params['access_tokens'])) { + foreach ($params['access_tokens'] as $key => $val) { + $val['access_token'] = $key; + $this->oauth_access_tokens[$key] = $val; + } } - - $clientDetails = array_merge(array( - 'client_id' => $client_id, - 'client_secret' => null, - 'redirect_uri' => null, - 'scope' => null, - ), $this->clientCredentials[$client_id]); - - return $clientDetails; - } - - public function checkRestrictedGrantType($client_id, $grant_type) - { - if (isset($this->clientCredentials[$client_id]['grant_types'])) { - $grant_types = explode(' ', $this->clientCredentials[$client_id]['grant_types']); - - return in_array($grant_type, $grant_types); + + if (isset($params['jwt']) && is_array($params['jwt'])) { + foreach ($params['jwt'] as $key => $val) { + $this->setClientKey($key, $val['key'], $val['subject']); + } } - - // if grant_types are not defined, then none are restricted - return true; - } - - public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) - { - $this->clientCredentials[$client_id] = array( - 'client_id' => $client_id, - 'client_secret' => $client_secret, - 'redirect_uri' => $redirect_uri, - 'grant_types' => $grant_types, - 'scope' => $scope, - 'user_id' => $user_id, - ); - - return true; - } - - /* RefreshTokenInterface */ - public function getRefreshToken($refresh_token) - { - return isset($this->refreshTokens[$refresh_token]) ? $this->refreshTokens[$refresh_token] : false; - } - - public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) - { - $this->refreshTokens[$refresh_token] = compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope'); - - return true; - } - - public function unsetRefreshToken($refresh_token) - { - unset($this->refreshTokens[$refresh_token]); - } - - public function setRefreshTokens($refresh_tokens) - { - $this->refreshTokens = $refresh_tokens; - } - - /* AccessTokenInterface */ - public function getAccessToken($access_token) - { - return isset($this->accessTokens[$access_token]) ? $this->accessTokens[$access_token] : false; - } - - public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null, $id_token = null) - { - $this->accessTokens[$access_token] = compact('access_token', 'client_id', 'user_id', 'expires', 'scope', 'id_token'); - - return true; - } - - public function unsetAccessToken($access_token) - { - unset($this->accessTokens[$access_token]); - } - - public function scopeExists($scope) - { - $scope = explode(' ', trim($scope)); - - return (count(array_diff($scope, $this->supportedScopes)) == 0); - } - - public function getDefaultScope($client_id = null) - { - return $this->defaultScope; - } - - /*JWTBearerInterface */ - public function getClientKey($client_id, $subject) - { - if (isset($this->jwt[$client_id])) { - $jwt = $this->jwt[$client_id]; - if ($jwt) { - if ($jwt["subject"] == $subject) { - return $jwt["key"]; - } + + if (isset($params['jti']) && is_array($params['jti'])) { + foreach ($params['jti'] as $key => $val) { + $this->setJti($val['issuer'], $val['subject'], $val['audience'], $val['expires'], $val['jti']); } } - - return false; - } - - public function getClientScope($client_id) - { - if (!$clientDetails = $this->getClientDetails($client_id)) { - return false; + + if (isset($params['supported_scopes']) && is_array($params['supported_scopes'])) { + $this->setScope(implode(' ', $params['supported_scopes']), null, self::KEY_SUPPORTED); } - - if (isset($clientDetails['scope'])) { - return $clientDetails['scope']; + if (isset($params['default_scope']) && is_string($params['default_scope'])) { + $this->setScope($params['default_scope'], null, self::KEY_DEFAULT); } - - return null; - } - - public function getJti($client_id, $subject, $audience, $expires, $jti) - { - foreach ($this->jti as $storedJti) { - if ($storedJti['issuer'] == $client_id && $storedJti['subject'] == $subject && $storedJti['audience'] == $audience && $storedJti['expires'] == $expires && $storedJti['jti'] == $jti) { - return array( - 'issuer' => $storedJti['issuer'], - 'subject' => $storedJti['subject'], - 'audience' => $storedJti['audience'], - 'expires' => $storedJti['expires'], - 'jti' => $storedJti['jti'] - ); + + if (isset($params['keys']) && is_array($params['keys'])) { + if (isset($params['keys']['private_key'])) { + $private_key = $params['keys']['private_key']; + $public_key = isset($params['keys']['public_key']) ? $params['keys']['public_key'] : null; + $encryption_algorithm = isset($params['keys']['encryption_algorithm']) ? $params['keys']['encryption_algorithm'] : 'RS256'; + $this->setServerKey(null, $public_key, $private_key, $encryption_algorithm); + } + unset($params['keys']['private_key']); + unset($params['keys']['public_key']); + unset($params['keys']['encryption_algorithm']); + + foreach ($params['keys'] as $key => $val) { + $this->setServerKey($key, $val['public_key'], $val['private_key'], $val['encryption_algorithm']); } } - - return null; - } - - public function setJti($client_id, $subject, $audience, $expires, $jti) - { - $this->jti[] = array('issuer' => $client_id, 'subject' => $subject, 'audience' => $audience, 'expires' => $expires, 'jti' => $jti); } - /*PublicKeyInterface */ - public function getPublicKey($client_id = null) + public function get($table, $key) { - if (isset($this->keys[$client_id])) { - return $this->keys[$client_id]['public_key']; - } - - // use a global encryption pair - if (isset($this->keys['public_key'])) { - return $this->keys['public_key']; - } - - return false; + return isset($this->$table[$key]) ? $this->$table[$key] : null; } - public function getPrivateKey($client_id = null) + public function set($table, $key, $value) { - if (isset($this->keys[$client_id])) { - return $this->keys[$client_id]['private_key']; - } - - // use a global encryption pair - if (isset($this->keys['private_key'])) { - return $this->keys['private_key']; - } - - return false; + $this->$table[$key] = $value; + return true; } - public function getEncryptionAlgorithm($client_id = null) + public function delete($table, $key) { - if (isset($this->keys[$client_id]['encryption_algorithm'])) { - return $this->keys[$client_id]['encryption_algorithm']; - } - - // use a global encryption algorithm - if (isset($this->keys['encryption_algorithm'])) { - return $this->keys['encryption_algorithm']; - } - - return 'RS256'; + unset($this->$table[$key]); + return true; } } diff --git a/test/config/storage.json b/test/config/storage.json index a31d3bca2..1fa54c00f 100644 --- a/test/config/storage.json +++ b/test/config/storage.json @@ -174,6 +174,7 @@ "supportedscope3", "supportedscope4" ], + "default_scope": "defaultscope1 defaultscope2", "keys": { "public_key": "-----BEGIN CERTIFICATE-----\nMIICiDCCAfGgAwIBAgIBADANBgkqhkiG9w0BAQQFADA9MQswCQYDVQQGEwJVUzEL\nMAkGA1UECBMCVVQxITAfBgNVBAoTGFZpZ25ldHRlIENvcnBvcmF0aW9uIFNCWDAe\nFw0xMTEwMTUwMzE4MjdaFw0zMTEwMTAwMzE4MjdaMD0xCzAJBgNVBAYTAlVTMQsw\nCQYDVQQIEwJVVDEhMB8GA1UEChMYVmlnbmV0dGUgQ29ycG9yYXRpb24gU0JYMIGf\nMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8fpi06NfVYHAOAnxNMVnTXr/ptsLs\nNjP+uAt2eO0cc5J9H5XV8lFVujOrRu/JWi1TDmAvOaf/6A3BphIA1Pwp0AAqlZdw\nizIum8j0KzpsGYH5qReNQDwF3oUSKMsQCCGCDHrDYifG/pRi9bN1ZVjEXPr35HJu\nBe+FQpZTs8DewwIDAQABo4GXMIGUMB0GA1UdDgQWBBRe8hrEXm+Yim4YlD5Nx+1K\nvCYs9DBlBgNVHSMEXjBcgBRe8hrEXm+Yim4YlD5Nx+1KvCYs9KFBpD8wPTELMAkG\nA1UEBhMCVVMxCzAJBgNVBAgTAlVUMSEwHwYDVQQKExhWaWduZXR0ZSBDb3Jwb3Jh\ndGlvbiBTQliCAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQBjhyRD\nlM7vnLn6drgQVftW5V9nDFAyPAuiGvMIKFSbiAf1PxXCRn5sfJquwWKsJUi4ZGNl\naViXdFmN6/F13PSM+yg63tpKy0fYqMbTM+Oe5WuSHkSW1VuYNHV+24adgNk/FRDL\nFRrlM1f6s9VTLWvwGItjfrof0Ba8Uq7ZDSb9Xg==\n-----END CERTIFICATE-----", "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQC8fpi06NfVYHAOAnxNMVnTXr/ptsLsNjP+uAt2eO0cc5J9H5XV\n8lFVujOrRu/JWi1TDmAvOaf/6A3BphIA1Pwp0AAqlZdwizIum8j0KzpsGYH5qReN\nQDwF3oUSKMsQCCGCDHrDYifG/pRi9bN1ZVjEXPr35HJuBe+FQpZTs8DewwIDAQAB\nAoGARfNxNknmtx/n1bskZ/01iZRzAge6BLEE0LV6Q4gS7mkRZu/Oyiv39Sl5vUlA\n+WdGxLjkBwKNjxGN8Vxw9/ASd8rSsqeAUYIwAeifXrHhj5DBPQT/pDPkeFnp9B1w\nC6jo+3AbBQ4/b0ONSIEnCL2xGGglSIAxO17T1ViXp7lzXPECQQDe63nkRdWM0OCb\noaHQPT3E26224maIstrGFUdt9yw3yJf4bOF7TtiPLlLuHsTTge3z+fG6ntC0xG56\n1cl37C3ZAkEA2HdVcRGugNp/qmVz4LJTpD+WZKi73PLAO47wDOrYh9Pn2I6fcEH0\nCPnggt1ko4ujvGzFTvRH64HXa6aPCv1j+wJBAMQMah3VQPNf/DlDVFEUmw9XeBZg\nVHaifX851aEjgXLp6qVj9IYCmLiLsAmVa9rr6P7p8asD418nZlaHUHE0eDkCQQCr\nuxis6GMx1Ka971jcJX2X696LoxXPd0KsvXySMupv79yagKPa8mgBiwPjrnK+EPVo\ncj6iochA/bSCshP/mwFrAkBHEKPi6V6gb94JinCT7x3weahbdp6bJ6/nzBH/p9VA\nHoT1JtwNFhGv9BCjmDydshQHfSWpY9NxlccBKL7ITm8R\n-----END RSA PRIVATE KEY-----" diff --git a/test/lib/OAuth2/Storage/BaseTest.php b/test/lib/OAuth2/Storage/BaseTest.php index 921d52500..80257323d 100755 --- a/test/lib/OAuth2/Storage/BaseTest.php +++ b/test/lib/OAuth2/Storage/BaseTest.php @@ -16,9 +16,6 @@ public function provideStorage() $dynamodb = Bootstrap::getInstance()->getDynamoDbStorage(); $couchbase = Bootstrap::getInstance()->getCouchbase(); - /* hack until we can fix "default_scope" dependencies in other tests */ - $memory->defaultScope = 'defaultscope1 defaultscope2'; - return array( array($memory), array($sqlite), From 18ac1250d126a12399b6382f1966b024e14b08ed Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Mon, 18 Jan 2016 10:13:51 +0400 Subject: [PATCH 09/16] setting scopes by array in Memory storage --- src/OAuth2/Storage/Memory.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 1ea373893..a6d9ff9b5 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -71,6 +71,9 @@ public function __construct($params = array()) } } + if (isset($params['scopes']) && is_array($params['scopes'])) { + $this->oauth_scopes = $params['scopes']; + } if (isset($params['supported_scopes']) && is_array($params['supported_scopes'])) { $this->setScope(implode(' ', $params['supported_scopes']), null, self::KEY_SUPPORTED); } From e7f2baab4a96f5256207699fcbb5e3c03ab4ae46 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Wed, 27 Jan 2016 13:33:32 +0400 Subject: [PATCH 10/16] fix --- src/OAuth2/Storage/Memory.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index a6d9ff9b5..1d9822336 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -100,18 +100,18 @@ public function __construct($params = array()) public function get($table, $key) { - return isset($this->$table[$key]) ? $this->$table[$key] : null; + return isset($this->{$table}[$key]) ? $this->{$table}[$key] : null; } public function set($table, $key, $value) { - $this->$table[$key] = $value; + $this->{$table}[$key] = $value; return true; } public function delete($table, $key) { - unset($this->$table[$key]); + unset($this->{$table}[$key]); return true; } } From d513c1587e36960cba5c70b1f8ed40103185437f Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Wed, 27 Jan 2016 14:25:42 +0400 Subject: [PATCH 11/16] override Memory::passwordVerify --- src/OAuth2/Storage/Memory.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 1d9822336..5faf7a848 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -98,6 +98,12 @@ public function __construct($params = array()) } } + // Override this for your application + protected function passwordVerify($password, $hash) + { + return $password === $hash; + } + public function get($table, $key) { return isset($this->{$table}[$key]) ? $this->{$table}[$key] : null; From f114f8eea56aa329ddc3710022d9bd44c48da57d Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Wed, 27 Jan 2016 15:39:27 +0400 Subject: [PATCH 12/16] override Memory::passwordHash --- src/OAuth2/Storage/Memory.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 5faf7a848..85c44b509 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -104,6 +104,12 @@ protected function passwordVerify($password, $hash) return $password === $hash; } + // Override this for your application + protected function passwordHash($password) + { + return $password; + } + public function get($table, $key) { return isset($this->{$table}[$key]) ? $this->{$table}[$key] : null; From 6e3bb37e53ca2ede4d71d672d71b911353b802fe Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Wed, 27 Jan 2016 16:12:08 +0400 Subject: [PATCH 13/16] Memory: `redirect_uri` and `scope` defaults to null --- src/OAuth2/Storage/Memory.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 85c44b509..25dc1c454 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -34,6 +34,10 @@ public function __construct($params = array()) if (isset($params['client_credentials']) && is_array($params['client_credentials'])) { foreach ($params['client_credentials'] as $key => $val) { $val['client_id'] = $key; + $val = array_merge(array( + 'redirect_uri' => null, + 'scope' => null, + ), $val); $this->oauth_clients[$key] = $val; } } From a7540a16b02a454849a07ea55f869e7c7d48f9c8 Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Thu, 28 Jan 2016 14:52:54 +0400 Subject: [PATCH 14/16] Allow `false` value for default scope --- src/OAuth2/Storage/KeyValueAbstract.php | 4 ++-- src/OAuth2/Storage/Memory.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php index 78934310f..f36437501 100644 --- a/src/OAuth2/Storage/KeyValueAbstract.php +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -309,11 +309,11 @@ public function getDefaultScope($client_id = null) $result = $this->get($this->config['scope_table'], self::KEY_DEFAULT . self::KEY_DELIMITER . self::KEY_GLOBAL); } - if (is_string($result)) { + if (is_string($result) || $result === false) { return $result; } - return false; + return null; } public function setScope($scope, $client_id = null, $type = self::KEY_SUPPORTED) diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index 25dc1c454..f2d27fa6d 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -81,7 +81,7 @@ public function __construct($params = array()) if (isset($params['supported_scopes']) && is_array($params['supported_scopes'])) { $this->setScope(implode(' ', $params['supported_scopes']), null, self::KEY_SUPPORTED); } - if (isset($params['default_scope']) && is_string($params['default_scope'])) { + if (isset($params['default_scope']) && (is_string($params['default_scope']) || $params['default_scope'] === false)) { $this->setScope($params['default_scope'], null, self::KEY_DEFAULT); } From e96004a0906cfcdb2af11a4b3f5b156c8b487d3e Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Tue, 19 Sep 2017 14:21:25 +0400 Subject: [PATCH 15/16] Fix test. Correct setting of supported scopes --- test/OAuth2/OpenID/ResponseType/IdTokenTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/OAuth2/OpenID/ResponseType/IdTokenTest.php b/test/OAuth2/OpenID/ResponseType/IdTokenTest.php index e772f6be4..fb577d138 100644 --- a/test/OAuth2/OpenID/ResponseType/IdTokenTest.php +++ b/test/OAuth2/OpenID/ResponseType/IdTokenTest.php @@ -167,7 +167,7 @@ private function getTestServer($config = array()) ); $memoryStorage = Bootstrap::getInstance()->getMemoryStorage(); - $memoryStorage->supportedScopes[] = 'email'; + $memoryStorage->setScope('email'); $storage = array( 'client' => $memoryStorage, 'scope' => $memoryStorage, From 9c91578455b0db53d453e76e39759da97c16c5ae Mon Sep 17 00:00:00 2001 From: Andrey Filippov Date: Wed, 20 Sep 2017 09:17:39 +0400 Subject: [PATCH 16/16] PSR-2 code style and some small fixes --- src/OAuth2/Storage/Cassandra.php | 14 +++++----- src/OAuth2/Storage/CouchbaseDB.php | 14 +++++----- src/OAuth2/Storage/KeyValueAbstract.php | 35 ++++++++++++++----------- src/OAuth2/Storage/Memcached.php | 12 ++++----- src/OAuth2/Storage/Memory.php | 2 +- src/OAuth2/Storage/MongoDB.php | 6 ++--- src/OAuth2/Storage/Redis.php | 14 +++++----- 7 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/OAuth2/Storage/Cassandra.php b/src/OAuth2/Storage/Cassandra.php index 2972d32d7..8e4ae642e 100644 --- a/src/OAuth2/Storage/Cassandra.php +++ b/src/OAuth2/Storage/Cassandra.php @@ -16,10 +16,10 @@ public function __construct($connection = array(), array $config = array()) { if ($connection instanceof ConnectionPool) { $this->db = $connection; - } else if (is_array($connection)) { + } elseif (is_array($connection)) { $connection = array_merge(array( 'keyspace' => 'oauth2', - 'servers' => null + 'servers' => null, ), $connection); $this->db = new ConnectionPool($connection['keyspace'], $connection['servers']); @@ -29,18 +29,18 @@ public function __construct($connection = array(), array $config = array()) $this->config = array_merge($this->config, array( 'column_family' => 'auth', - 'expire' => 0 + 'expire' => 0, ), $config); } - protected function _makeKey($table, $key) + protected function makeKey($table, $key) { return $table . ':' . $key; } protected function get($table, $key) { - $key = $this->_makeKey($table, $key); + $key = $this->makeKey($table, $key); if (isset($this->cache[$key])) { return $this->cache[$key]; @@ -58,7 +58,7 @@ protected function get($table, $key) protected function set($table, $key, $value) { - $key = $this->_makeKey($table, $key); + $key = $this->makeKey($table, $key); $this->cache[$key] = $value; @@ -76,7 +76,7 @@ protected function set($table, $key, $value) protected function delete($table, $key) { - $key = $this->_makeKey($table, $key); + $key = $this->makeKey($table, $key); unset($this->cache[$key]); diff --git a/src/OAuth2/Storage/CouchbaseDB.php b/src/OAuth2/Storage/CouchbaseDB.php index 080472244..1d7114067 100755 --- a/src/OAuth2/Storage/CouchbaseDB.php +++ b/src/OAuth2/Storage/CouchbaseDB.php @@ -6,12 +6,12 @@ class CouchbaseDB extends KeyValueAbstract protected $db; - public function __construct($connection, $config = array()) + public function __construct($connection, array $config = array()) { if ($connection instanceof \Couchbase) { $this->db = $connection; - } else if (is_array($connection) && isset($connection['servers']) && is_array($connection['servers'])) { - $this->db = new \Couchbase($connection['servers'], (isset($connection['username']) ? $connection['username'] : ''), (isset($connection['password']) ? $connection['password'] : ''), $connection['bucket'], false); + } elseif (is_array($connection) && isset($connection['servers']) && is_array($connection['servers'])) { + $this->db = new \Couchbase($connection['servers'], (isset($connection['username']) ? $connection['username'] : ''), (isset($connection['password']) ? $connection['password'] : ''), (isset($connection['bucket']) ? $connection['bucket'] : 'default'), false); } else { throw new \InvalidArgumentException('First argument to ' . __CLASS__ . ' must be an instance of Couchbase or a configuration array containing a servers array'); } @@ -19,23 +19,23 @@ public function __construct($connection, $config = array()) $this->config = array_merge($this->config, $config); } - protected function _makeKey($table, $key) + protected function makeKey($table, $key) { return $table . '-' . $key; } public function get($table, $key) { - return json_decode($this->db->get($this->_makeKey($table, $key)), true); + return json_decode($this->db->get($this->makeKey($table, $key)), true); } public function set($table, $key, $value) { - return $this->db->set($this->_makeKey($table, $key), json_encode($value)); + return $this->db->set($this->makeKey($table, $key), json_encode($value)); } public function delete($table, $key) { - return $this->db->delete($this->_makeKey($table, $key), '', 1); + return $this->db->delete($this->makeKey($table, $key), '', 1); } } diff --git a/src/OAuth2/Storage/KeyValueAbstract.php b/src/OAuth2/Storage/KeyValueAbstract.php index f36437501..d31d5416d 100644 --- a/src/OAuth2/Storage/KeyValueAbstract.php +++ b/src/OAuth2/Storage/KeyValueAbstract.php @@ -17,7 +17,7 @@ abstract class KeyValueAbstract implements OpenIDAuthorizationCodeInterface { - const KEY_NULL = ''; // TODO we should replace it to KEY_GLOBAL, it is only for backward compatibility with old Cassandra storage adapter (PublicKeyInterface functions) + const KEY_NULL = ''; // TODO we should replace it by KEY_GLOBAL, it is only for backward compatibility with old Cassandra storage adapter (PublicKeyInterface functions) const KEY_DELIMITER = ':'; const KEY_DEFAULT = 'default'; const KEY_SUPPORTED = 'supported'; @@ -32,10 +32,11 @@ abstract class KeyValueAbstract implements 'jwt_table' => 'oauth_jwt', 'jti_table' => 'oauth_jti', 'scope_table' => 'oauth_scopes', - 'public_key_table' => 'oauth_public_keys' + 'public_key_table' => 'oauth_public_keys', ); /** + * Get from storage * * @param string $table * @param string $key @@ -44,6 +45,7 @@ abstract class KeyValueAbstract implements abstract protected function get($table, $key); /** + * Write to storage * * @param string $table * @param string $key @@ -53,6 +55,7 @@ abstract protected function get($table, $key); abstract protected function set($table, $key, $value); /** + * Delete from storage * * @param string $table * @param string $key @@ -60,7 +63,7 @@ abstract protected function set($table, $key, $value); */ abstract protected function delete($table, $key); - protected static function _hash($data) + protected static function hash($data) { return hash('sha256', json_encode($data)); } @@ -234,10 +237,10 @@ public function unsetRefreshToken($refresh_token) // JwtBearerInterface public function getClientKey($client_id, $subject) { - $keydata = compact('client_id', 'subject'); - $keystring = self::_hash($keydata); + $storageKey = compact('client_id', 'subject'); + $storageKey = self::hash($storageKey); - $result = $this->get($this->config['jwt_table'], $keystring); + $result = $this->get($this->config['jwt_table'], $storageKey); if (is_string($result)) { return $result; @@ -248,10 +251,10 @@ public function getClientKey($client_id, $subject) public function setClientKey($client_id, $key, $subject = null) { - $keydata = compact('client_id', 'subject'); - $keystring = self::_hash($keydata); + $storageKey = compact('client_id', 'subject'); + $storageKey = self::hash($storageKey); - return $this->set($this->config['jwt_table'], $keystring, $key); + return $this->set($this->config['jwt_table'], $storageKey, $key); } public function getJti($client_id, $subject, $audience, $expiration, $jti) @@ -261,12 +264,12 @@ public function getJti($client_id, $subject, $audience, $expiration, $jti) 'subject' => $subject, 'audience' => $audience, 'expires' => $expiration, - 'jti' => $jti + 'jti' => $jti, ); - $key = self::_hash($data); + $storageKey = self::hash($data); - $result = $this->get($this->config['jti_table'], $key); + $result = $this->get($this->config['jti_table'], $storageKey); if (is_array($result)) { return $result; @@ -282,12 +285,12 @@ public function setJti($client_id, $subject, $audience, $expiration, $jti) 'subject' => $subject, 'audience' => $audience, 'expires' => $expiration, - 'jti' => $jti + 'jti' => $jti, ); - $key = self::_hash($data); + $storageKey = self::hash($data); - $this->set($this->config['jti_table'], $key, $data); + $this->set($this->config['jti_table'], $storageKey, $data); } // ScopeInterface @@ -434,7 +437,7 @@ protected function getUserClaim($scopeValue, $userDetails) return $userClaims; } - protected static function parseBool($value) + private static function parseBool($value) { return is_string($value) ? filter_var($value, FILTER_VALIDATE_BOOLEAN) : (bool)$value; } diff --git a/src/OAuth2/Storage/Memcached.php b/src/OAuth2/Storage/Memcached.php index fe0fbd849..10209037f 100644 --- a/src/OAuth2/Storage/Memcached.php +++ b/src/OAuth2/Storage/Memcached.php @@ -6,7 +6,7 @@ class Memcached extends KeyValueAbstract protected $db; - public function __construct($connection, $config = array()) + public function __construct($connection, array $config = array()) { if (!extension_loaded('memcached')) { throw new \LogicException('memcached extension not loaded'); @@ -14,7 +14,7 @@ public function __construct($connection, $config = array()) if ($connection instanceof \Memcached) { $this->db = $connection; - } else if (is_array($connection) && isset($connection['servers']) && is_array($connection['servers'])) { + } elseif (is_array($connection) && isset($connection['servers']) && is_array($connection['servers'])) { $this->db = new \Memcached(); $this->db->addServers($connection['servers']); if (isset($connection['options']) && is_array($connection['options'])) { @@ -27,23 +27,23 @@ public function __construct($connection, $config = array()) $this->config = array_merge($this->config, $config); } - protected function _makeKey($table, $key) + protected function makeKey($table, $key) { return $table . '-' . $key; } public function get($table, $key) { - return $this->db->get($this->_makeKey($table, $key)); + return $this->db->get($this->makeKey($table, $key)); } public function set($table, $key, $value) { - return $this->db->set($this->_makeKey($table, $key), $value); + return $this->db->set($this->makeKey($table, $key), $value); } public function delete($table, $key) { - return $this->db->delete($this->_makeKey($table, $key)); + return $this->db->delete($this->makeKey($table, $key)); } } diff --git a/src/OAuth2/Storage/Memory.php b/src/OAuth2/Storage/Memory.php index f2d27fa6d..4bb245012 100644 --- a/src/OAuth2/Storage/Memory.php +++ b/src/OAuth2/Storage/Memory.php @@ -22,7 +22,7 @@ class Memory extends KeyValueAbstract protected $oauth_public_keys = array(); - public function __construct($params = array()) + public function __construct(array $params = array()) { if (isset($params['authorization_codes']) && is_array($params['authorization_codes'])) { foreach ($params['authorization_codes'] as $key => $val) { diff --git a/src/OAuth2/Storage/MongoDB.php b/src/OAuth2/Storage/MongoDB.php index bb80d0563..b1ae2827f 100644 --- a/src/OAuth2/Storage/MongoDB.php +++ b/src/OAuth2/Storage/MongoDB.php @@ -6,7 +6,7 @@ class MongoDB extends KeyValueAbstract protected $db; - public function __construct($connection, $config = array()) + public function __construct($connection, array $config = array()) { if (!extension_loaded('mongodb')) { throw new \LogicException('mongodb extension not loaded'); @@ -14,7 +14,7 @@ public function __construct($connection, $config = array()) if ($connection instanceof \MongoDB\Driver\Manager) { $this->db = $connection; - } else if (is_array($connection)) { + } elseif (is_array($connection)) { $server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']); $this->db = new \MongoDB\Driver\Manager($server); } else { @@ -22,7 +22,7 @@ public function __construct($connection, $config = array()) } $this->config = array_merge($this->config, array( - 'database' => $connection['database'] + 'database' => $connection['database'], ), $config); } diff --git a/src/OAuth2/Storage/Redis.php b/src/OAuth2/Storage/Redis.php index f2db0fafb..f1a393c85 100644 --- a/src/OAuth2/Storage/Redis.php +++ b/src/OAuth2/Storage/Redis.php @@ -12,10 +12,10 @@ public function __construct($connection = array(), array $config = array()) { if ($connection instanceof \Predis\Client) { $this->db = $connection; - } else if (is_array($connection)) { + } elseif (is_array($connection)) { $connection = array_merge(array( 'parameters' => null, - 'options' => null + 'options' => null, ), $connection); $this->db = new \Predis\Client($connection['parameters'], $connection['options']); @@ -24,18 +24,18 @@ public function __construct($connection = array(), array $config = array()) } $this->config = array_merge($this->config, array( - 'expire' => 0 + 'expire' => 0, ), $config); } - protected function _makeKey($table, $key) + protected function makeKey($table, $key) { return $table . ':' . $key; } protected function get($table, $key) { - $key = $this->_makeKey($table, $key); + $key = $this->makeKey($table, $key); if (isset($this->cache[$key])) { return $this->cache[$key]; @@ -51,7 +51,7 @@ protected function get($table, $key) protected function set($table, $key, $value) { - $key = $this->_makeKey($table, $key); + $key = $this->makeKey($table, $key); $this->cache[$key] = $value; @@ -68,7 +68,7 @@ protected function set($table, $key, $value) protected function delete($table, $key) { - $key = $this->_makeKey($table, $key); + $key = $this->makeKey($table, $key); unset($this->cache[$key]);