From 7737d08905dcaeb1506c210e0292c54171410705 Mon Sep 17 00:00:00 2001 From: Nigel Lundsten Date: Fri, 27 Mar 2015 11:16:47 -0700 Subject: [PATCH 01/13] copy of pdo adapter --- src/OAuth2/Storage/IbmDb2.php | 536 ++++++++++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 src/OAuth2/Storage/IbmDb2.php diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php new file mode 100644 index 000000000..b5097b03d --- /dev/null +++ b/src/OAuth2/Storage/IbmDb2.php @@ -0,0 +1,536 @@ + + */ +class IbmDb2 implements + AuthorizationCodeInterface, + AccessTokenInterface, + ClientCredentialsInterface, + UserCredentialsInterface, + RefreshTokenInterface, + JwtBearerInterface, + ScopeInterface, + PublicKeyInterface, + UserClaimsInterface, + OpenIDAuthorizationCodeInterface +{ + protected $db; + protected $config; + + public function __construct($connection, $config = array()) + { + if (!$connection instanceof \PDO) { + if (is_string($connection)) { + $connection = array('dsn' => $connection); + } + if (!is_array($connection)) { + throw new \InvalidArgumentException('First argument to OAuth2\Storage\Pdo must be an instance of PDO, a DSN string, or a configuration array'); + } + if (!isset($connection['dsn'])) { + throw new \InvalidArgumentException('configuration array must contain "dsn"'); + } + // merge optional parameters + $connection = array_merge(array( + 'username' => null, + 'password' => null, + 'options' => array(), + ), $connection); + $connection = new \PDO($connection['dsn'], $connection['username'], $connection['password'], $connection['options']); + } + $this->db = $connection; + + // debugging + $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + $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', + 'jti_table' => 'oauth_jti', + 'scope_table' => 'oauth_scopes', + 'public_key_table' => 'oauth_public_keys', + ), $config); + } + + /* OAuth2\Storage\ClientCredentialsInterface */ + public function checkClientCredentials($client_id, $client_secret = null) + { + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); + $stmt->execute(compact('client_id')); + $result = $stmt->fetch(\PDO::FETCH_ASSOC); + + // make this extensible + return $result && $result['client_secret'] == $client_secret; + } + + public function isPublicClient($client_id) + { + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); + $stmt->execute(compact('client_id')); + + if (!$result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return false; + } + + return empty($result['client_secret']); + } + + /* OAuth2\Storage\ClientInterface */ + public function getClientDetails($client_id) + { + $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); + $stmt->execute(compact('client_id')); + + return $stmt->fetch(\PDO::FETCH_ASSOC); + } + + public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) + { + // if it exists, update it. + if ($this->getClientDetails($client_id)) { + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_secret=:client_secret, redirect_uri=:redirect_uri, grant_types=:grant_types, scope=:scope, user_id=:user_id where client_id=:client_id', $this->config['client_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (client_id, client_secret, redirect_uri, grant_types, scope, user_id) VALUES (:client_id, :client_secret, :redirect_uri, :grant_types, :scope, :user_id)', $this->config['client_table'])); + } + + return $stmt->execute(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; + } + + /* OAuth2\Storage\AccessTokenInterface */ + public function getAccessToken($access_token) + { + $stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = :access_token', $this->config['access_token_table'])); + + $token = $stmt->execute(compact('access_token')); + if ($token = $stmt->fetch(\PDO::FETCH_ASSOC)) { + // convert date string back to timestamp + $token['expires'] = strtotime($token['expires']); + } + + return $token; + } + + public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) + { + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + // if it exists, update it. + if ($this->getAccessToken($access_token)) { + $stmt = $this->db->prepare(sprintf('UPDATE %s SET client_id=:client_id, expires=:expires, user_id=:user_id, scope=:scope where access_token=:access_token', $this->config['access_token_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (access_token, client_id, expires, user_id, scope) VALUES (:access_token, :client_id, :expires, :user_id, :scope)', $this->config['access_token_table'])); + } + + return $stmt->execute(compact('access_token', 'client_id', 'user_id', 'expires', 'scope')); + } + + /* OAuth2\Storage\AuthorizationCodeInterface */ + public function getAuthorizationCode($code) + { + $stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = :code', $this->config['code_table'])); + $stmt->execute(compact('code')); + + if ($code = $stmt->fetch(\PDO::FETCH_ASSOC)) { + // convert date string back to timestamp + $code['expires'] = strtotime($code['expires']); + } + + return $code; + } + + public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + if (func_num_args() > 6) { + // we are calling with an id token + return call_user_func_array(array($this, 'setAuthorizationCodeWithIdToken'), func_get_args()); + } + + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + // if it exists, update it. + if ($this->getAuthorizationCode($code)) { + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope where authorization_code=:code', $this->config['code_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope)', $this->config['code_table'])); + } + + return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); + } + + private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) + { + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + // if it exists, update it. + if ($this->getAuthorizationCode($code)) { + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, id_token =:id_token where authorization_code=:code', $this->config['code_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :id_token)', $this->config['code_table'])); + } + + return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); + } + + public function expireAuthorizationCode($code) + { + $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE authorization_code = :code', $this->config['code_table'])); + + return $stmt->execute(compact('code')); + } + + /* OAuth2\Storage\UserCredentialsInterface */ + public function checkUserCredentials($username, $password) + { + if ($user = $this->getUser($username)) { + return $this->checkPassword($user, $password); + } + + return false; + } + + public function getUserDetails($username) + { + return $this->getUser($username); + } + + /* UserClaimsInterface */ + public function getUserClaims($user_id, $claims) + { + if (!$userDetails = $this->getUserDetails($user_id)) { + 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) { + $userClaims[$value] = isset($userDetails[$value]) ? $userDetails[$value] : null; + } + + return $userClaims; + } + + /* OAuth2\Storage\RefreshTokenInterface */ + public function getRefreshToken($refresh_token) + { + $stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table'])); + + $token = $stmt->execute(compact('refresh_token')); + if ($token = $stmt->fetch(\PDO::FETCH_ASSOC)) { + // convert expires to epoch time + $token['expires'] = strtotime($token['expires']); + } + + return $token; + } + + public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null) + { + // convert expires to datestring + $expires = date('Y-m-d H:i:s', $expires); + + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (refresh_token, client_id, user_id, expires, scope) VALUES (:refresh_token, :client_id, :user_id, :expires, :scope)', $this->config['refresh_token_table'])); + + return $stmt->execute(compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope')); + } + + public function unsetRefreshToken($refresh_token) + { + $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table'])); + + return $stmt->execute(compact('refresh_token')); + } + + // plaintext passwords are bad! Override this for your application + protected function checkPassword($user, $password) + { + return $user['password'] == sha1($password); + } + + public function getUser($username) + { + $stmt = $this->db->prepare($sql = sprintf('SELECT * from %s where username=:username', $this->config['user_table'])); + $stmt->execute(array('username' => $username)); + + if (!$userInfo = $stmt->fetch(\PDO::FETCH_ASSOC)) { + 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, $firstName = null, $lastName = null) + { + // do not store in plaintext + $password = sha1($password); + + // if it exists, update it. + if ($this->getUser($username)) { + $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET password=:password, first_name=:firstName, last_name=:lastName where username=:username', $this->config['user_table'])); + } else { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (username, password, first_name, last_name) VALUES (:username, :password, :firstName, :lastName)', $this->config['user_table'])); + } + + return $stmt->execute(compact('username', 'password', 'firstName', 'lastName')); + } + + /* ScopeInterface */ + public function scopeExists($scope) + { + $scope = explode(' ', $scope); + $whereIn = implode(',', array_fill(0, count($scope), '?')); + $stmt = $this->db->prepare(sprintf('SELECT count(scope) as count FROM %s WHERE scope IN (%s)', $this->config['scope_table'], $whereIn)); + $stmt->execute($scope); + + if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return $result['count'] == count($scope); + } + + return false; + } + + public function getDefaultScope($client_id = null) + { + $stmt = $this->db->prepare(sprintf('SELECT scope FROM %s WHERE is_default=:is_default', $this->config['scope_table'])); + $stmt->execute(array('is_default' => true)); + + if ($result = $stmt->fetchAll(\PDO::FETCH_ASSOC)) { + $defaultScope = array_map(function ($row) { + return $row['scope']; + }, $result); + + return implode(' ', $defaultScope); + } + + return null; + } + + /* JWTBearerInterface */ + public function getClientKey($client_id, $subject) + { + $stmt = $this->db->prepare($sql = sprintf('SELECT public_key from %s where client_id=:client_id AND subject=:subject', $this->config['jwt_table'])); + + $stmt->execute(array('client_id' => $client_id, 'subject' => $subject)); + + return $stmt->fetchColumn(); + } + + 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, $expires, $jti) + { + $stmt = $this->db->prepare($sql = sprintf('SELECT * FROM %s WHERE issuer=:client_id AND subject=:subject AND audience=:audience AND expires=:expires AND jti=:jti', $this->config['jti_table'])); + + $stmt->execute(compact('client_id', 'subject', 'audience', 'expires', 'jti')); + + if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return array( + 'issuer' => $result['issuer'], + 'subject' => $result['subject'], + 'audience' => $result['audience'], + 'expires' => $result['expires'], + 'jti' => $result['jti'], + ); + } + + return null; + } + + public function setJti($client_id, $subject, $audience, $expires, $jti) + { + $stmt = $this->db->prepare(sprintf('INSERT INTO %s (issuer, subject, audience, expires, jti) VALUES (:client_id, :subject, :audience, :expires, :jti)', $this->config['jti_table'])); + + return $stmt->execute(compact('client_id', 'subject', 'audience', 'expires', 'jti')); + } + + /* PublicKeyInterface */ + public function getPublicKey($client_id = null) + { + $stmt = $this->db->prepare($sql = sprintf('SELECT public_key FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); + + $stmt->execute(compact('client_id')); + if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return $result['public_key']; + } + } + + public function getPrivateKey($client_id = null) + { + $stmt = $this->db->prepare($sql = sprintf('SELECT private_key FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); + + $stmt->execute(compact('client_id')); + if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return $result['private_key']; + } + } + + public function getEncryptionAlgorithm($client_id = null) + { + $stmt = $this->db->prepare($sql = sprintf('SELECT encryption_algorithm FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); + + $stmt->execute(compact('client_id')); + if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return $result['encryption_algorithm']; + } + + return 'RS256'; + } + + /** + * DDL to create OAuth2 database and tables for PDO storage + * + * @see https://github.com/dsquier/oauth2-server-php-mysql + */ + public function getBuildSql($dbName = 'oauth2_server_php') + { + $sql = " + CREATE TABLE {$this->config['client_table']} ( + client_id VARCHAR(80) NOT NULL, + client_secret VARCHAR(80) NOT NULL, + redirect_uri VARCHAR(2000), + grant_types VARCHAR(80), + scope VARCHAR(4000), + user_id VARCHAR(80), + PRIMARY KEY (client_id) + ); + + CREATE TABLE {$this->config['access_token_table']} ( + access_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(80), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (access_token) + ); + + CREATE TABLE {$this->config['code_table']} ( + authorization_code VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(80), + redirect_uri VARCHAR(2000), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + id_token VARCHAR(1000), + PRIMARY KEY (authorization_code) + ); + + CREATE TABLE {$this->config['refresh_token_table']} ( + refresh_token VARCHAR(40) NOT NULL, + client_id VARCHAR(80) NOT NULL, + user_id VARCHAR(80), + expires TIMESTAMP NOT NULL, + scope VARCHAR(4000), + PRIMARY KEY (refresh_token) + ); + + CREATE TABLE {$this->config['user_table']} ( + username VARCHAR(80), + password VARCHAR(80), + first_name VARCHAR(80), + last_name VARCHAR(80), + email VARCHAR(80), + email_verified BOOLEAN, + scope VARCHAR(4000) + ); + + CREATE TABLE {$this->config['scope_table']} ( + scope VARCHAR(80) NOT NULL, + is_default BOOLEAN, + PRIMARY KEY (scope) + ); + + CREATE TABLE {$this->config['jwt_table']} ( + client_id VARCHAR(80) NOT NULL, + subject VARCHAR(80), + public_key VARCHAR(2000) NOT NULL + ); + + CREATE TABLE {$this->config['jti_table']} ( + issuer VARCHAR(80) NOT NULL, + subject VARCHAR(80), + audiance VARCHAR(80), + expires TIMESTAMP NOT NULL, + jti VARCHAR(2000) NOT NULL + ); + + CREATE TABLE {$this->config['public_key_table']} ( + client_id VARCHAR(80), + public_key VARCHAR(2000), + private_key VARCHAR(2000), + encryption_algorithm VARCHAR(100) DEFAULT 'RS256' + ) +"; + + return $sql; + } +} From ac9d6e719f50c350ab1849264a9f0e17c726325e Mon Sep 17 00:00:00 2001 From: alanseiden Date: Sun, 29 Mar 2015 18:31:00 -0500 Subject: [PATCH 02/13] Changed connection logic and checkClientCredentials(), that is, up to line 101, to be compatible with ibm_db2 --- src/OAuth2/Storage/IbmDb2.php | 57 +++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index b5097b03d..ace204319 100644 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -34,29 +34,48 @@ class IbmDb2 implements public function __construct($connection, $config = array()) { - if (!$connection instanceof \PDO) { - if (is_string($connection)) { - $connection = array('dsn' => $connection); - } + if (!is_resource($connection)) { + // Note: Unlike PDO, IbmDb2 (ibm_db2 extension) cannot be configured via dsn string. + if (!is_array($connection)) { - throw new \InvalidArgumentException('First argument to OAuth2\Storage\Pdo must be an instance of PDO, a DSN string, or a configuration array'); - } - if (!isset($connection['dsn'])) { - throw new \InvalidArgumentException('configuration array must contain "dsn"'); + throw new \InvalidArgumentException('First argument to OAuth2\Storage\IbmDb2 must be a resource or a configuration array'); } - // merge optional parameters + + /* FYI: ZF2 used more flexible naming + $database = $findParameterValue(array('database', 'db')); + $username = $findParameterValue(array('username', 'uid', 'UID')); + $password = $findParameterValue(array('password', 'pwd', 'PWD')); + $isPersistent = $findParameterValue(array('persistent', 'PERSISTENT', 'Persistent')); + */ + + // merge optional parameters. Set empty defaults if not present in $connection array. $connection = array_merge(array( - 'username' => null, - 'password' => null, - 'options' => array(), + 'db' => '', + 'username' => '', + 'password' => '', + 'persistent' => false, + 'driver_options' => array(), ), $connection); - $connection = new \PDO($connection['dsn'], $connection['username'], $connection['password'], $connection['options']); + + // use persistent or not + $isPersistent = $connection['persistent']; + $connectFunction = ((bool) $isPersistent) ? 'db2_pconnect' : 'db2_connect'; + + // try to connect + $connection = $connectFunction($connection['database'], $connection['username'], $connection['password'], $connection['driver_options']); + + // this is how ZF2 handles connection errors + if ($connection === false) { + throw new Exception\RuntimeException(sprintf( + '%s: Unable to connect to database', + __METHOD__ + )); + } + } + $this->db = $connection; - // debugging - $connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $this->config = array_merge(array( 'client_table' => 'oauth_clients', 'access_token_table' => 'oauth_access_tokens', @@ -73,9 +92,9 @@ public function __construct($connection, $config = array()) /* OAuth2\Storage\ClientCredentialsInterface */ public function checkClientCredentials($client_id, $client_secret = null) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); - $stmt->execute(compact('client_id')); - $result = $stmt->fetch(\PDO::FETCH_ASSOC); + $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where client_id = ?', $this->config['client_table'])); + $successfulExecute = db2_execute($stmt, compact('client_id')); + $result = db2_fetch_assoc($stmt); // make this extensible return $result && $result['client_secret'] == $client_secret; From 1d97cf5bba05af172acfbb88b0b5eadbb0c46adb Mon Sep 17 00:00:00 2001 From: alanseiden Date: Tue, 31 Mar 2015 21:47:49 -0500 Subject: [PATCH 03/13] Complete IbmDb2 adaptation except for DDL statements --- src/OAuth2/Storage/IbmDb2.php | 157 ++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 73 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index ace204319..44e2a435a 100644 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -16,6 +16,7 @@ * a good idea. Be sure to override this for your application * * @author Brent Shaffer + * @author Alan Seiden */ class IbmDb2 implements AuthorizationCodeInterface, @@ -102,10 +103,10 @@ public function checkClientCredentials($client_id, $client_secret = null) public function isPublicClient($client_id) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); - $stmt->execute(compact('client_id')); - - if (!$result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where client_id = ?', $this->config['client_table'])); + $successfulExecute = db2_execute($stmt, compact('client_id')); + + if (!$result = db2_fetch_assoc($stmt)) { return false; } @@ -115,22 +116,23 @@ public function isPublicClient($client_id) /* OAuth2\Storage\ClientInterface */ public function getClientDetails($client_id) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where client_id = :client_id', $this->config['client_table'])); - $stmt->execute(compact('client_id')); - - return $stmt->fetch(\PDO::FETCH_ASSOC); + $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where client_id = ?', $this->config['client_table'])); + $successfulExecute = db2_execute($stmt, compact('client_id')); + + return db2_fetch_assoc($stmt); } public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null) { // if it exists, update it. if ($this->getClientDetails($client_id)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_secret=:client_secret, redirect_uri=:redirect_uri, grant_types=:grant_types, scope=:scope, user_id=:user_id where client_id=:client_id', $this->config['client_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET client_secret=?, redirect_uri=?, grant_types=?, scope=?, user_id=? where client_id=?', $this->config['client_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (client_id, client_secret, redirect_uri, grant_types, scope, user_id) VALUES (:client_id, :client_secret, :redirect_uri, :grant_types, :scope, :user_id)', $this->config['client_table'])); + $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (client_id, client_secret, redirect_uri, grant_types, scope, user_id) VALUES (?, ?, ?, ?, ?, ?)', $this->config['client_table'])); } - return $stmt->execute(compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id')); + return db2_execute($stmt, compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id')); + } public function checkRestrictedGrantType($client_id, $grant_type) @@ -149,10 +151,11 @@ public function checkRestrictedGrantType($client_id, $grant_type) /* OAuth2\Storage\AccessTokenInterface */ public function getAccessToken($access_token) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where access_token = :access_token', $this->config['access_token_table'])); + $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where access_token = ?', $this->config['access_token_table'])); - $token = $stmt->execute(compact('access_token')); - if ($token = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $token = db2_execute($stmt, compact('access_token')); + + if ($token = db2_fetch_assoc($stmt)) { // convert date string back to timestamp $token['expires'] = strtotime($token['expires']); } @@ -167,21 +170,21 @@ public function setAccessToken($access_token, $client_id, $user_id, $expires, $s // if it exists, update it. if ($this->getAccessToken($access_token)) { - $stmt = $this->db->prepare(sprintf('UPDATE %s SET client_id=:client_id, expires=:expires, user_id=:user_id, scope=:scope where access_token=:access_token', $this->config['access_token_table'])); + $stmt = db2_prepare($this->db, sprintf('UPDATE %s SET client_id=?, expires=?, user_id=?, scope=? where access_token=?', $this->config['access_token_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (access_token, client_id, expires, user_id, scope) VALUES (:access_token, :client_id, :expires, :user_id, :scope)', $this->config['access_token_table'])); + $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (access_token, client_id, expires, user_id, scope) VALUES (?, ?, ?, ?, ?)', $this->config['access_token_table'])); } - - return $stmt->execute(compact('access_token', 'client_id', 'user_id', 'expires', 'scope')); + + return db2_execute($stmt, compact('access_token', 'client_id', 'user_id', 'expires', 'scope')); } /* OAuth2\Storage\AuthorizationCodeInterface */ public function getAuthorizationCode($code) { - $stmt = $this->db->prepare(sprintf('SELECT * from %s where authorization_code = :code', $this->config['code_table'])); - $stmt->execute(compact('code')); + $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where authorization_code = ?', $this->config['code_table'])); + $successfulExecute = db2_execute($stmt, compact('client_id')); - if ($code = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if ($code = db2_fetch_assoc($stmt)) { // convert date string back to timestamp $code['expires'] = strtotime($code['expires']); } @@ -201,12 +204,12 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, // if it exists, update it. if ($this->getAuthorizationCode($code)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope where authorization_code=:code', $this->config['code_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET client_id=?, user_id=?, redirect_uri=?, expires=?, scope=? where authorization_code=?', $this->config['code_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope)', $this->config['code_table'])); + $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (?, ?, ?, ?, ?, ?)', $this->config['code_table'])); } - return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); + return db2_execute($stmt, compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); } private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) @@ -216,19 +219,20 @@ private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $r // if it exists, update it. if ($this->getAuthorizationCode($code)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET client_id=:client_id, user_id=:user_id, redirect_uri=:redirect_uri, expires=:expires, scope=:scope, id_token =:id_token where authorization_code=:code', $this->config['code_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET client_id=?, user_id=?, redirect_uri=?, expires=?, scope=?, id_token =? where authorization_code=?', $this->config['code_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token) VALUES (:code, :client_id, :user_id, :redirect_uri, :expires, :scope, :id_token)', $this->config['code_table'])); + $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token) VALUES (?, ?, ?, ?, ?, ?, ?)', $this->config['code_table'])); } - return $stmt->execute(compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); + return db2_execute($stmt, compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); } public function expireAuthorizationCode($code) { - $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE authorization_code = :code', $this->config['code_table'])); + $stmt = db2_prepare($this->db, sprintf('DELETE FROM %s WHERE authorization_code = ?', $this->config['code_table'])); - return $stmt->execute(compact('code')); + return db2_execute($stmt, compact('code')); + } /* OAuth2\Storage\UserCredentialsInterface */ @@ -288,10 +292,10 @@ protected function getUserClaim($claim, $userDetails) /* OAuth2\Storage\RefreshTokenInterface */ public function getRefreshToken($refresh_token) { - $stmt = $this->db->prepare(sprintf('SELECT * FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table'])); + $stmt = db2_prepare($this->db, sprintf('SELECT * FROM %s WHERE refresh_token = ?', $this->config['refresh_token_table'])); - $token = $stmt->execute(compact('refresh_token')); - if ($token = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $token = db2_execute($stmt, compact('refresh_token')); + if ($token = db2_fetch_assoc($stmt)) { // convert expires to epoch time $token['expires'] = strtotime($token['expires']); } @@ -304,16 +308,16 @@ public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, // convert expires to datestring $expires = date('Y-m-d H:i:s', $expires); - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (refresh_token, client_id, user_id, expires, scope) VALUES (:refresh_token, :client_id, :user_id, :expires, :scope)', $this->config['refresh_token_table'])); + $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (refresh_token, client_id, user_id, expires, scope) VALUES (?, ?, ?, ?, ?)', $this->config['refresh_token_table'])); - return $stmt->execute(compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope')); + return db2_execute($stmt, compact('refresh_token', 'client_id', 'user_id', 'expires', 'scope')); } public function unsetRefreshToken($refresh_token) { - $stmt = $this->db->prepare(sprintf('DELETE FROM %s WHERE refresh_token = :refresh_token', $this->config['refresh_token_table'])); + $stmt = db2_prepare($this->db, sprintf('DELETE FROM %s WHERE refresh_token = ?', $this->config['refresh_token_table'])); - return $stmt->execute(compact('refresh_token')); + return db2_execute($stmt, compact('refresh_token')); } // plaintext passwords are bad! Override this for your application @@ -324,10 +328,10 @@ protected function checkPassword($user, $password) public function getUser($username) { - $stmt = $this->db->prepare($sql = sprintf('SELECT * from %s where username=:username', $this->config['user_table'])); - $stmt->execute(array('username' => $username)); + $stmt = db2_prepare($this->db, $sql = sprintf('SELECT * from %s where username=?', $this->config['user_table'])); + $successfulExecute = db2_execute($stmt, array('username' => $username)); - if (!$userInfo = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if (!$userInfo = db2_fetch_assoc($stmt)) { return false; } @@ -344,12 +348,12 @@ public function setUser($username, $password, $firstName = null, $lastName = nul // if it exists, update it. if ($this->getUser($username)) { - $stmt = $this->db->prepare($sql = sprintf('UPDATE %s SET password=:password, first_name=:firstName, last_name=:lastName where username=:username', $this->config['user_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET password=?, first_name=?, last_name=? where username=?', $this->config['user_table'])); } else { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (username, password, first_name, last_name) VALUES (:username, :password, :firstName, :lastName)', $this->config['user_table'])); + $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (username, password, first_name, last_name) VALUES (?, ?, ?, ?)', $this->config['user_table'])); } - return $stmt->execute(compact('username', 'password', 'firstName', 'lastName')); + return db2_execute($stmt, compact('username', 'password', 'firstName', 'lastName')); } /* ScopeInterface */ @@ -357,10 +361,10 @@ public function scopeExists($scope) { $scope = explode(' ', $scope); $whereIn = implode(',', array_fill(0, count($scope), '?')); - $stmt = $this->db->prepare(sprintf('SELECT count(scope) as count FROM %s WHERE scope IN (%s)', $this->config['scope_table'], $whereIn)); - $stmt->execute($scope); + $stmt = db2_prepare($this->db, sprintf('SELECT count(scope) as count FROM %s WHERE scope IN (%s)', $this->config['scope_table'], $whereIn)); + $successfulExecute = db2_execute($stmt, $scope); - if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if ($result = db2_fetch_assoc($stmt)) { return $result['count'] == count($scope); } @@ -369,10 +373,17 @@ public function scopeExists($scope) public function getDefaultScope($client_id = null) { - $stmt = $this->db->prepare(sprintf('SELECT scope FROM %s WHERE is_default=:is_default', $this->config['scope_table'])); - $stmt->execute(array('is_default' => true)); + $stmt = db2_prepare($this->db, sprintf('SELECT scope FROM %s WHERE is_default=?', $this->config['scope_table'])); + $successfulExecute = db2_execute($stmt, array('is_default' => true)); - if ($result = $stmt->fetchAll(\PDO::FETCH_ASSOC)) { + + $result = false; + // was fetchAll() + while (db2_fetch_assoc($stmt) == $oneRecord) { + $result[] = $oneRecord; + } + + if ($result) { $defaultScope = array_map(function ($row) { return $row['scope']; }, $result); @@ -386,9 +397,9 @@ public function getDefaultScope($client_id = null) /* JWTBearerInterface */ public function getClientKey($client_id, $subject) { - $stmt = $this->db->prepare($sql = sprintf('SELECT public_key from %s where client_id=:client_id AND subject=:subject', $this->config['jwt_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('SELECT public_key from %s where client_id=? AND subject=?', $this->config['jwt_table'])); - $stmt->execute(array('client_id' => $client_id, 'subject' => $subject)); + $successfulExecute = db2_execute($stmt, array('client_id' => $client_id, 'subject' => $subject)); return $stmt->fetchColumn(); } @@ -408,11 +419,11 @@ public function getClientScope($client_id) public function getJti($client_id, $subject, $audience, $expires, $jti) { - $stmt = $this->db->prepare($sql = sprintf('SELECT * FROM %s WHERE issuer=:client_id AND subject=:subject AND audience=:audience AND expires=:expires AND jti=:jti', $this->config['jti_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('SELECT * FROM %s WHERE issuer=? AND subject=? AND audience=? AND expires=? AND jti=?', $this->config['jti_table'])); - $stmt->execute(compact('client_id', 'subject', 'audience', 'expires', 'jti')); + $successfulExecute = db2_execute($stmt, compact('client_id', 'subject', 'audience', 'expires', 'jti')); - if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + if ($result = db2_fetch_assoc($stmt)) { return array( 'issuer' => $result['issuer'], 'subject' => $result['subject'], @@ -427,38 +438,38 @@ public function getJti($client_id, $subject, $audience, $expires, $jti) public function setJti($client_id, $subject, $audience, $expires, $jti) { - $stmt = $this->db->prepare(sprintf('INSERT INTO %s (issuer, subject, audience, expires, jti) VALUES (:client_id, :subject, :audience, :expires, :jti)', $this->config['jti_table'])); + $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (issuer, subject, audience, expires, jti) VALUES (?, ?, ?, ?, ?)', $this->config['jti_table'])); - return $stmt->execute(compact('client_id', 'subject', 'audience', 'expires', 'jti')); + return db2_execute($stmt, compact('client_id', 'subject', 'audience', 'expires', 'jti')); } /* PublicKeyInterface */ public function getPublicKey($client_id = null) { - $stmt = $this->db->prepare($sql = sprintf('SELECT public_key FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('SELECT public_key FROM %s WHERE client_id=? OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); - $stmt->execute(compact('client_id')); - if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $successfulExecute = db2_execute($stmt, compact('client_id')); + if ($result = db2_fetch_assoc($stmt)) { return $result['public_key']; } } public function getPrivateKey($client_id = null) { - $stmt = $this->db->prepare($sql = sprintf('SELECT private_key FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('SELECT private_key FROM %s WHERE client_id=? OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); - $stmt->execute(compact('client_id')); - if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $successfulExecute = db2_execute($stmt, compact('client_id')); + if ($result = db2_fetch_assoc($stmt)) { return $result['private_key']; } } public function getEncryptionAlgorithm($client_id = null) { - $stmt = $this->db->prepare($sql = sprintf('SELECT encryption_algorithm FROM %s WHERE client_id=:client_id OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); + $stmt = db2_prepare($this->db, $sql = sprintf('SELECT encryption_algorithm FROM %s WHERE client_id=? OR client_id IS NULL ORDER BY client_id IS NOT NULL DESC', $this->config['public_key_table'])); - $stmt->execute(compact('client_id')); - if ($result = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $successfulExecute = db2_execute($stmt, compact('client_id')); + if ($result = db2_fetch_assoc($stmt)) { return $result['encryption_algorithm']; } @@ -466,7 +477,7 @@ public function getEncryptionAlgorithm($client_id = null) } /** - * DDL to create OAuth2 database and tables for PDO storage + * DDL to create OAuth2 database and tables for IbmDb2 storage * * @see https://github.com/dsquier/oauth2-server-php-mysql */ @@ -481,7 +492,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') scope VARCHAR(4000), user_id VARCHAR(80), PRIMARY KEY (client_id) - ); + ) CREATE TABLE {$this->config['access_token_table']} ( access_token VARCHAR(40) NOT NULL, @@ -490,7 +501,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') expires TIMESTAMP NOT NULL, scope VARCHAR(4000), PRIMARY KEY (access_token) - ); + ) CREATE TABLE {$this->config['code_table']} ( authorization_code VARCHAR(40) NOT NULL, @@ -501,7 +512,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') scope VARCHAR(4000), id_token VARCHAR(1000), PRIMARY KEY (authorization_code) - ); + ) CREATE TABLE {$this->config['refresh_token_table']} ( refresh_token VARCHAR(40) NOT NULL, @@ -510,7 +521,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') expires TIMESTAMP NOT NULL, scope VARCHAR(4000), PRIMARY KEY (refresh_token) - ); + ) CREATE TABLE {$this->config['user_table']} ( username VARCHAR(80), @@ -520,19 +531,19 @@ public function getBuildSql($dbName = 'oauth2_server_php') email VARCHAR(80), email_verified BOOLEAN, scope VARCHAR(4000) - ); + ) CREATE TABLE {$this->config['scope_table']} ( scope VARCHAR(80) NOT NULL, is_default BOOLEAN, PRIMARY KEY (scope) - ); + ) CREATE TABLE {$this->config['jwt_table']} ( client_id VARCHAR(80) NOT NULL, subject VARCHAR(80), public_key VARCHAR(2000) NOT NULL - ); + ) CREATE TABLE {$this->config['jti_table']} ( issuer VARCHAR(80) NOT NULL, @@ -540,7 +551,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') audiance VARCHAR(80), expires TIMESTAMP NOT NULL, jti VARCHAR(2000) NOT NULL - ); + ) CREATE TABLE {$this->config['public_key_table']} ( client_id VARCHAR(80), From c7898df925dde42a0e429ef5ed58460a1ae00123 Mon Sep 17 00:00:00 2001 From: Nigel Lundsten Date: Mon, 6 Apr 2015 09:28:26 -0700 Subject: [PATCH 04/13] change date string -- change some out of order params -- some caps array keys (due to caps field names) -- some debug --- src/OAuth2/Storage/IbmDb2.php | 53 ++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index 44e2a435a..1f40ca994 100644 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -37,18 +37,18 @@ public function __construct($connection, $config = array()) { if (!is_resource($connection)) { // Note: Unlike PDO, IbmDb2 (ibm_db2 extension) cannot be configured via dsn string. - + if (!is_array($connection)) { throw new \InvalidArgumentException('First argument to OAuth2\Storage\IbmDb2 must be a resource or a configuration array'); } - + /* FYI: ZF2 used more flexible naming $database = $findParameterValue(array('database', 'db')); $username = $findParameterValue(array('username', 'uid', 'UID')); $password = $findParameterValue(array('password', 'pwd', 'PWD')); $isPersistent = $findParameterValue(array('persistent', 'PERSISTENT', 'Persistent')); */ - + // merge optional parameters. Set empty defaults if not present in $connection array. $connection = array_merge(array( 'db' => '', @@ -57,11 +57,11 @@ public function __construct($connection, $config = array()) 'persistent' => false, 'driver_options' => array(), ), $connection); - + // use persistent or not $isPersistent = $connection['persistent']; $connectFunction = ((bool) $isPersistent) ? 'db2_pconnect' : 'db2_connect'; - + // try to connect $connection = $connectFunction($connection['database'], $connection['username'], $connection['password'], $connection['driver_options']); @@ -72,9 +72,9 @@ public function __construct($connection, $config = array()) __METHOD__ )); } - + } - + $this->db = $connection; $this->config = array_merge(array( @@ -98,14 +98,14 @@ public function checkClientCredentials($client_id, $client_secret = null) $result = db2_fetch_assoc($stmt); // make this extensible - return $result && $result['client_secret'] == $client_secret; + return $result && $result['CLIENT_SECRET'] == $client_secret; } public function isPublicClient($client_id) { $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where client_id = ?', $this->config['client_table'])); $successfulExecute = db2_execute($stmt, compact('client_id')); - + if (!$result = db2_fetch_assoc($stmt)) { return false; } @@ -118,7 +118,7 @@ public function getClientDetails($client_id) { $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where client_id = ?', $this->config['client_table'])); $successfulExecute = db2_execute($stmt, compact('client_id')); - + return db2_fetch_assoc($stmt); } @@ -132,7 +132,7 @@ public function setClientDetails($client_id, $client_secret = null, $redirect_ur } return db2_execute($stmt, compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id')); - + } public function checkRestrictedGrantType($client_id, $grant_type) @@ -154,7 +154,7 @@ public function getAccessToken($access_token) $stmt = db2_prepare($this->db, sprintf('SELECT * from %s where access_token = ?', $this->config['access_token_table'])); $token = db2_execute($stmt, compact('access_token')); - + if ($token = db2_fetch_assoc($stmt)) { // convert date string back to timestamp $token['expires'] = strtotime($token['expires']); @@ -166,16 +166,27 @@ public function getAccessToken($access_token) public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) { // convert expires to datestring - $expires = date('Y-m-d H:i:s', $expires); + $expires = date("Y-m-d-h.i.s", $expires); // if it exists, update it. if ($this->getAccessToken($access_token)) { $stmt = db2_prepare($this->db, sprintf('UPDATE %s SET client_id=?, expires=?, user_id=?, scope=? where access_token=?', $this->config['access_token_table'])); + if (false == $stmt) { + throw new \Exception(db2_stmt_errormsg()); + } + $executeSuccess = db2_execute($stmt, compact('client_id', 'expires', 'user_id', 'scope', 'access_token')); } else { $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (access_token, client_id, expires, user_id, scope) VALUES (?, ?, ?, ?, ?)', $this->config['access_token_table'])); + if (false == $stmt) { + throw new \Exception(db2_stmt_errormsg()); + } + $executeSuccess = db2_execute($stmt, compact('access_token', 'client_id', 'expires', 'user_id', 'scope')); } - - return db2_execute($stmt, compact('access_token', 'client_id', 'user_id', 'expires', 'scope')); + + if (false == $executeSuccess) { + throw new \Exception(db2_stmt_errormsg()); + } + } /* OAuth2\Storage\AuthorizationCodeInterface */ @@ -232,7 +243,7 @@ public function expireAuthorizationCode($code) $stmt = db2_prepare($this->db, sprintf('DELETE FROM %s WHERE authorization_code = ?', $this->config['code_table'])); return db2_execute($stmt, compact('code')); - + } /* OAuth2\Storage\UserCredentialsInterface */ @@ -323,7 +334,7 @@ public function unsetRefreshToken($refresh_token) // plaintext passwords are bad! Override this for your application protected function checkPassword($user, $password) { - return $user['password'] == sha1($password); + return $user['PASSWORD'] == sha1($password); } public function getUser($username) @@ -376,13 +387,11 @@ public function getDefaultScope($client_id = null) $stmt = db2_prepare($this->db, sprintf('SELECT scope FROM %s WHERE is_default=?', $this->config['scope_table'])); $successfulExecute = db2_execute($stmt, array('is_default' => true)); - + $result = false; // was fetchAll() - while (db2_fetch_assoc($stmt) == $oneRecord) { - $result[] = $oneRecord; - } - + $result = db2_fetch_assoc($stmt); + if ($result) { $defaultScope = array_map(function ($row) { return $row['scope']; From 8a76df23722e71b3294a3cba3d72617b6f1a4755 Mon Sep 17 00:00:00 2001 From: Nigel Lundsten Date: Tue, 7 Apr 2015 19:26:15 -0700 Subject: [PATCH 05/13] back to lowercase -- fix 'expires' formatting stuff --- src/OAuth2/Storage/IbmDb2.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) mode change 100644 => 100755 src/OAuth2/Storage/IbmDb2.php diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php old mode 100644 new mode 100755 index 1f40ca994..c5bec8f43 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -98,7 +98,7 @@ public function checkClientCredentials($client_id, $client_secret = null) $result = db2_fetch_assoc($stmt); // make this extensible - return $result && $result['CLIENT_SECRET'] == $client_secret; + return $result && $result['client_secret'] == $client_secret; } public function isPublicClient($client_id) @@ -156,7 +156,10 @@ public function getAccessToken($access_token) $token = db2_execute($stmt, compact('access_token')); if ($token = db2_fetch_assoc($stmt)) { - // convert date string back to timestamp + + //replace 10th character (dash) between day and time with a space + $token['expires'] = substr_replace($token['expires'], ' ', 10, 1); + // convert date string back to timestamp $token['expires'] = strtotime($token['expires']); } @@ -166,7 +169,7 @@ public function getAccessToken($access_token) public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null) { // convert expires to datestring - $expires = date("Y-m-d-h.i.s", $expires); + $expires = date("Y-m-d-H.i.s", $expires); // if it exists, update it. if ($this->getAccessToken($access_token)) { @@ -334,7 +337,7 @@ public function unsetRefreshToken($refresh_token) // plaintext passwords are bad! Override this for your application protected function checkPassword($user, $password) { - return $user['PASSWORD'] == sha1($password); + return $user['password'] == sha1($password); } public function getUser($username) From 6bc3857d03c63d58a0baa18cfc83baf36df804c8 Mon Sep 17 00:00:00 2001 From: alanseiden Date: Wed, 8 Apr 2015 19:14:16 -0400 Subject: [PATCH 06/13] Finish correcting parameter ordering; improve comments --- src/OAuth2/Storage/IbmDb2.php | 38 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index c5bec8f43..26d7f0e82 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -6,7 +6,6 @@ use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface; /** - * NOTE: WIP : convert pdo stuff to db2 * * NOTE: This class is meant to get users started * quickly. If your application requires further @@ -42,16 +41,9 @@ public function __construct($connection, $config = array()) throw new \InvalidArgumentException('First argument to OAuth2\Storage\IbmDb2 must be a resource or a configuration array'); } - /* FYI: ZF2 used more flexible naming - $database = $findParameterValue(array('database', 'db')); - $username = $findParameterValue(array('username', 'uid', 'UID')); - $password = $findParameterValue(array('password', 'pwd', 'PWD')); - $isPersistent = $findParameterValue(array('persistent', 'PERSISTENT', 'Persistent')); - */ - // merge optional parameters. Set empty defaults if not present in $connection array. $connection = array_merge(array( - 'db' => '', + 'database' => '', 'username' => '', 'password' => '', 'persistent' => false, @@ -127,12 +119,13 @@ public function setClientDetails($client_id, $client_secret = null, $redirect_ur // if it exists, update it. if ($this->getClientDetails($client_id)) { $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET client_secret=?, redirect_uri=?, grant_types=?, scope=?, user_id=? where client_id=?', $this->config['client_table'])); + return db2_execute($stmt, compact('client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id', 'client_id')); } else { $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (client_id, client_secret, redirect_uri, grant_types, scope, user_id) VALUES (?, ?, ?, ?, ?, ?)', $this->config['client_table'])); + return db2_execute($stmt, compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id')); + } - return db2_execute($stmt, compact('client_id', 'client_secret', 'redirect_uri', 'grant_types', 'scope', 'user_id')); - } public function checkRestrictedGrantType($client_id, $grant_type) @@ -157,9 +150,10 @@ public function getAccessToken($access_token) if ($token = db2_fetch_assoc($stmt)) { - //replace 10th character (dash) between day and time with a space + // db2 timestamps look like yyyy-mm-dd-hh.mm.ss.000000 where the last six are microseconds. + // replace 10th character (dash between day and time) with a space to make it intelligible to strtotime() $token['expires'] = substr_replace($token['expires'], ' ', 10, 1); - // convert date string back to timestamp + // convert date string back to Unix timestamp $token['expires'] = strtotime($token['expires']); } @@ -199,7 +193,10 @@ public function getAuthorizationCode($code) $successfulExecute = db2_execute($stmt, compact('client_id')); if ($code = db2_fetch_assoc($stmt)) { - // convert date string back to timestamp + // db2 timestamps look like yyyy-mm-dd-hh.mm.ss.000000 where the last six are microseconds. + // replace 10th character (dash between day and time) with a space to make it intelligible to strtotime() + $code['expires'] = substr_replace($code['expires'], ' ', 10, 1); + // convert date string back to Unix timestamp $code['expires'] = strtotime($code['expires']); } @@ -219,11 +216,13 @@ public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, // if it exists, update it. if ($this->getAuthorizationCode($code)) { $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET client_id=?, user_id=?, redirect_uri=?, expires=?, scope=? where authorization_code=?', $this->config['code_table'])); + return db2_execute($stmt, compact('client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'code')); } else { $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope) VALUES (?, ?, ?, ?, ?, ?)', $this->config['code_table'])); + return db2_execute($stmt, compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); + } - return db2_execute($stmt, compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope')); } private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null) @@ -234,11 +233,14 @@ private function setAuthorizationCodeWithIdToken($code, $client_id, $user_id, $r // if it exists, update it. if ($this->getAuthorizationCode($code)) { $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET client_id=?, user_id=?, redirect_uri=?, expires=?, scope=?, id_token =? where authorization_code=?', $this->config['code_table'])); + return db2_execute($stmt, compact('client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token', 'code')); + } else { $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (authorization_code, client_id, user_id, redirect_uri, expires, scope, id_token) VALUES (?, ?, ?, ?, ?, ?, ?)', $this->config['code_table'])); + return db2_execute($stmt, compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); + } - return db2_execute($stmt, compact('code', 'client_id', 'user_id', 'redirect_uri', 'expires', 'scope', 'id_token')); } public function expireAuthorizationCode($code) @@ -363,11 +365,13 @@ public function setUser($username, $password, $firstName = null, $lastName = nul // if it exists, update it. if ($this->getUser($username)) { $stmt = db2_prepare($this->db, $sql = sprintf('UPDATE %s SET password=?, first_name=?, last_name=? where username=?', $this->config['user_table'])); + return db2_execute($stmt, compact('password', 'firstName', 'lastName', 'username')); } else { $stmt = db2_prepare($this->db, sprintf('INSERT INTO %s (username, password, first_name, last_name) VALUES (?, ?, ?, ?)', $this->config['user_table'])); + return db2_execute($stmt, compact('username', 'password', 'firstName', 'lastName')); + } - return db2_execute($stmt, compact('username', 'password', 'firstName', 'lastName')); } /* ScopeInterface */ From 09f4138f052a7a98bc860202f70a84cc7f66159d Mon Sep 17 00:00:00 2001 From: alanseiden Date: Thu, 9 Apr 2015 00:52:36 -0400 Subject: [PATCH 07/13] Add 'for system name', primary key constraints, and comments to the SQL/DDL section, getBuildSql(). --- src/OAuth2/Storage/IbmDb2.php | 50 +++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index 26d7f0e82..4dbd4a6d2 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -494,32 +494,45 @@ public function getEncryptionAlgorithm($client_id = null) /** * DDL to create OAuth2 database and tables for IbmDb2 storage - * * @see https://github.com/dsquier/oauth2-server-php-mysql + * + * Notes for IbmDb2 version: + * 1. The syntax "for system name SHORTNAME" applies only to IBM i systems at 7.1 with recent PTFs applied, or later releases. + * It creates the specified short name for access from system utilities, RPG, etc. + * 2. For ease in handling lower-case column names in PHP, consider using DB2_CASE_LOWER in your connection options. + * http://php.net/manual/en/function.db2-connect.php + * $db = db2_connect('DATABASE', 'USER', 'PASSWORD', array('DB2_ATTR_CASE'=>DB2_CASE_LOWER)); + * */ public function getBuildSql($dbName = 'oauth2_server_php') { $sql = " - CREATE TABLE {$this->config['client_table']} ( + CREATE TABLE {$this->config['client_table']} + for system name OAUTHCLI + ( client_id VARCHAR(80) NOT NULL, client_secret VARCHAR(80) NOT NULL, redirect_uri VARCHAR(2000), grant_types VARCHAR(80), scope VARCHAR(4000), user_id VARCHAR(80), - PRIMARY KEY (client_id) + CONSTRAINT clients_client_id_pk PRIMARY KEY (client_id) ) - CREATE TABLE {$this->config['access_token_table']} ( + CREATE TABLE {$this->config['access_token_table']} + for system name OAUTHTOKEN + ( access_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(80), expires TIMESTAMP NOT NULL, scope VARCHAR(4000), - PRIMARY KEY (access_token) + CONSTRAINT access_token_pk PRIMARY KEY (access_token) ) - CREATE TABLE {$this->config['code_table']} ( + CREATE TABLE {$this->config['code_table']} + for system name OAUTHCODES + ( authorization_code VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(80), @@ -527,19 +540,23 @@ public function getBuildSql($dbName = 'oauth2_server_php') expires TIMESTAMP NOT NULL, scope VARCHAR(4000), id_token VARCHAR(1000), - PRIMARY KEY (authorization_code) + CONSTRAINT auth_code_pk PRIMARY KEY (authorization_code) ) - CREATE TABLE {$this->config['refresh_token_table']} ( - refresh_token VARCHAR(40) NOT NULL, + CREATE TABLE {$this->config['refresh_token_table']} + for system name OAUTHREFTK + ( + refresh_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(80), expires TIMESTAMP NOT NULL, scope VARCHAR(4000), - PRIMARY KEY (refresh_token) + CONSTRAINT refresh_token_pk PRIMARY KEY (refresh_token) ) - CREATE TABLE {$this->config['user_table']} ( + CREATE TABLE {$this->config['user_table']} + for system name OAUTHUSERS + ( username VARCHAR(80), password VARCHAR(80), first_name VARCHAR(80), @@ -547,12 +564,15 @@ public function getBuildSql($dbName = 'oauth2_server_php') email VARCHAR(80), email_verified BOOLEAN, scope VARCHAR(4000) + CONSTRAINT username_pk PRIMARY KEY (username) ) - CREATE TABLE {$this->config['scope_table']} ( + CREATE TABLE {$this->config['scope_table']} + for system name OAUTHSCOPE + ( scope VARCHAR(80) NOT NULL, is_default BOOLEAN, - PRIMARY KEY (scope) + CONSTRAINT scope_pk PRIMARY KEY (scope) ) CREATE TABLE {$this->config['jwt_table']} ( @@ -569,7 +589,9 @@ public function getBuildSql($dbName = 'oauth2_server_php') jti VARCHAR(2000) NOT NULL ) - CREATE TABLE {$this->config['public_key_table']} ( + CREATE TABLE {$this->config['public_key_table']} + for system name OAUTHPUBKY + ( client_id VARCHAR(80), public_key VARCHAR(2000), private_key VARCHAR(2000), From e42889d698f1306a5bb0891145c22819c540d31c Mon Sep 17 00:00:00 2001 From: alanseiden Date: Thu, 9 Apr 2015 00:55:45 -0400 Subject: [PATCH 08/13] Correct spacing in getBuildSql(). --- src/OAuth2/Storage/IbmDb2.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index 4dbd4a6d2..13909ca8c 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -532,7 +532,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') CREATE TABLE {$this->config['code_table']} for system name OAUTHCODES - ( + ( authorization_code VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(80), @@ -555,8 +555,8 @@ public function getBuildSql($dbName = 'oauth2_server_php') ) CREATE TABLE {$this->config['user_table']} - for system name OAUTHUSERS - ( + for system name OAUTHUSERS + ( username VARCHAR(80), password VARCHAR(80), first_name VARCHAR(80), @@ -568,20 +568,22 @@ public function getBuildSql($dbName = 'oauth2_server_php') ) CREATE TABLE {$this->config['scope_table']} - for system name OAUTHSCOPE - ( + for system name OAUTHSCOPE + ( scope VARCHAR(80) NOT NULL, is_default BOOLEAN, CONSTRAINT scope_pk PRIMARY KEY (scope) ) - CREATE TABLE {$this->config['jwt_table']} ( + CREATE TABLE {$this->config['jwt_table']} + ( client_id VARCHAR(80) NOT NULL, subject VARCHAR(80), public_key VARCHAR(2000) NOT NULL ) - CREATE TABLE {$this->config['jti_table']} ( + CREATE TABLE {$this->config['jti_table']} + ( issuer VARCHAR(80) NOT NULL, subject VARCHAR(80), audiance VARCHAR(80), From 3abed33bf8dba1aa66cd57d9a2e7741a44d9eaa8 Mon Sep 17 00:00:00 2001 From: alanseiden Date: Thu, 9 Apr 2015 00:57:00 -0400 Subject: [PATCH 09/13] One more spacing correction in getBuildSql(). --- src/OAuth2/Storage/IbmDb2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index 13909ca8c..b0fb3b2a5 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -544,8 +544,8 @@ public function getBuildSql($dbName = 'oauth2_server_php') ) CREATE TABLE {$this->config['refresh_token_table']} - for system name OAUTHREFTK - ( + for system name OAUTHREFTK + ( refresh_token VARCHAR(40) NOT NULL, client_id VARCHAR(80) NOT NULL, user_id VARCHAR(80), From bd60d16703e4b0637670e0b4c2e405259f766112 Mon Sep 17 00:00:00 2001 From: alanseiden Date: Mon, 4 May 2015 22:24:55 -0400 Subject: [PATCH 10/13] Use correct exception class --- src/OAuth2/Storage/IbmDb2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index b0fb3b2a5..23ae3b740 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -59,7 +59,7 @@ public function __construct($connection, $config = array()) // this is how ZF2 handles connection errors if ($connection === false) { - throw new Exception\RuntimeException(sprintf( + throw new \RuntimeException(sprintf( '%s: Unable to connect to database', __METHOD__ )); From 7856150495bd3dc0229267a0b67d7e7ba5cccff9 Mon Sep 17 00:00:00 2001 From: alanseiden Date: Mon, 4 May 2015 22:27:12 -0400 Subject: [PATCH 11/13] In DDL, replace BOOLEAN with the DB2-compatible type smallint --- src/OAuth2/Storage/IbmDb2.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index 23ae3b740..a6feef868 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -562,7 +562,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') first_name VARCHAR(80), last_name VARCHAR(80), email VARCHAR(80), - email_verified BOOLEAN, + email_verified smallint, scope VARCHAR(4000) CONSTRAINT username_pk PRIMARY KEY (username) ) @@ -571,7 +571,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') for system name OAUTHSCOPE ( scope VARCHAR(80) NOT NULL, - is_default BOOLEAN, + is_default smallint, CONSTRAINT scope_pk PRIMARY KEY (scope) ) From 24c544b5a959e8160e6493a90d9553513d26b68d Mon Sep 17 00:00:00 2001 From: alanseiden Date: Mon, 4 May 2015 22:28:32 -0400 Subject: [PATCH 12/13] In DDL, add comma before CONSTRAINT clause of oauthusers table --- src/OAuth2/Storage/IbmDb2.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index a6feef868..8e124abfb 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -563,7 +563,7 @@ public function getBuildSql($dbName = 'oauth2_server_php') last_name VARCHAR(80), email VARCHAR(80), email_verified smallint, - scope VARCHAR(4000) + scope VARCHAR(4000), CONSTRAINT username_pk PRIMARY KEY (username) ) From 325b4b054b8cec63bb988efde1cfaecb3ca7663f Mon Sep 17 00:00:00 2001 From: alanseiden Date: Mon, 4 May 2015 22:39:10 -0400 Subject: [PATCH 13/13] Check for ibm_db2 extension using same technique as in ZF2 driver. --- src/OAuth2/Storage/IbmDb2.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/OAuth2/Storage/IbmDb2.php b/src/OAuth2/Storage/IbmDb2.php index 8e124abfb..34d88bbb3 100755 --- a/src/OAuth2/Storage/IbmDb2.php +++ b/src/OAuth2/Storage/IbmDb2.php @@ -41,6 +41,10 @@ public function __construct($connection, $config = array()) throw new \InvalidArgumentException('First argument to OAuth2\Storage\IbmDb2 must be a resource or a configuration array'); } + if (!extension_loaded('ibm_db2')) { + throw new \RuntimeException('The ibm_db2 extension is required by OAuth2\Storage\IbmDb2.'); + } + // merge optional parameters. Set empty defaults if not present in $connection array. $connection = array_merge(array( 'database' => '',