Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
41.67% |
10 / 24 |
CRAP | |
56.52% |
104 / 184 |
token_storage | |
0.00% |
0 / 1 |
|
41.67% |
10 / 24 |
264.77 | |
56.52% |
104 / 184 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
retrieveAccessToken | |
100.00% |
1 / 1 |
3 | |
100.00% |
8 / 8 |
|||
storeAccessToken | |
100.00% |
1 / 1 |
3 | |
100.00% |
18 / 18 |
|||
hasAccessToken | |
100.00% |
1 / 1 |
3 | |
100.00% |
8 / 8 |
|||
clearToken | |
100.00% |
1 / 1 |
2 | |
100.00% |
9 / 9 |
|||
clearAllTokens | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 7 |
|||
storeAuthorizationState | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 9 |
|||
hasAuthorizationState | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 8 |
|||
retrieveAuthorizationState | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 8 |
|||
clearAuthorizationState | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 9 |
|||
clearAllAuthorizationStates | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 7 |
|||
set_user_id | |
0.00% |
0 / 1 |
2.01 | |
88.89% |
8 / 9 |
|||
has_access_token_by_session | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
has_state_by_session | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 6 |
|||
has_access_token | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
retrieve_access_token_by_session | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
retrieve_state_by_session | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 6 |
|||
_retrieve_access_token | |
100.00% |
1 / 1 |
3 | |
100.00% |
9 / 9 |
|||
_retrieve_state | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 5 |
|||
get_access_token_row | |
100.00% |
1 / 1 |
1 | |
100.00% |
6 / 6 |
|||
get_state_row | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 6 |
|||
json_encode_token | |
0.00% |
0 / 1 |
2.11 | |
70.00% |
7 / 10 |
|||
json_decode_token | |
0.00% |
0 / 1 |
3.17 | |
73.33% |
11 / 15 |
|||
get_service_name_for_db | |
0.00% |
0 / 1 |
2.15 | |
66.67% |
2 / 3 |
<?php | |
/** | |
* | |
* This file is part of the phpBB Forum Software package. | |
* | |
* @copyright (c) phpBB Limited <https://www.phpbb.com> | |
* @license GNU General Public License, version 2 (GPL-2.0) | |
* | |
* For full copyright and license information, please see | |
* the docs/CREDITS.txt file. | |
* | |
*/ | |
namespace phpbb\auth\provider\oauth; | |
use OAuth\OAuth1\Token\StdOAuth1Token; | |
use OAuth\Common\Token\TokenInterface; | |
use OAuth\Common\Storage\TokenStorageInterface; | |
use OAuth\Common\Storage\Exception\TokenNotFoundException; | |
use OAuth\Common\Storage\Exception\AuthorizationStateNotFoundException; | |
/** | |
* OAuth storage wrapper for phpBB's cache | |
*/ | |
class token_storage implements TokenStorageInterface | |
{ | |
/** @var \phpbb\db\driver\driver_interface */ | |
protected $db; | |
/** @var \phpbb\user */ | |
protected $user; | |
/** @var string OAuth table: token storage */ | |
protected $oauth_token_table; | |
/** @var string OAuth table: state */ | |
protected $oauth_state_table; | |
/** @var TokenInterface OAuth token */ | |
protected $cachedToken; | |
/** @var string OAuth state */ | |
protected $cachedState; | |
/** | |
* Constructor. | |
* | |
* @param \phpbb\db\driver\driver_interface $db Database object | |
* @param \phpbb\user $user User object | |
* @param string $oauth_token_table OAuth table: token storage | |
* @param string $oauth_state_table OAuth table: state | |
*/ | |
public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\user $user, $oauth_token_table, $oauth_state_table) | |
{ | |
$this->db = $db; | |
$this->user = $user; | |
$this->oauth_token_table = $oauth_token_table; | |
$this->oauth_state_table = $oauth_state_table; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function retrieveAccessToken($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedToken instanceof TokenInterface) | |
{ | |
return $this->cachedToken; | |
} | |
$data = [ | |
'user_id' => (int) $this->user->data['user_id'], | |
'provider' => $service, | |
]; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$data['session_id'] = $this->user->data['session_id']; | |
} | |
return $this->_retrieve_access_token($data); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function storeAccessToken($service, TokenInterface $token) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
$this->cachedToken = $token; | |
$data = [ | |
'oauth_token' => $this->json_encode_token($token), | |
]; | |
$sql = 'UPDATE ' . $this->oauth_token_table . ' | |
SET ' . $this->db->sql_build_array('UPDATE', $data) . ' | |
WHERE user_id = ' . (int) $this->user->data['user_id'] . " | |
AND provider = '" . $this->db->sql_escape($service) . "'"; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; | |
} | |
$this->db->sql_query($sql); | |
if (!$this->db->sql_affectedrows()) | |
{ | |
$data = [ | |
'user_id' => (int) $this->user->data['user_id'], | |
'provider' => $service, | |
'oauth_token' => $this->json_encode_token($token), | |
'session_id' => $this->user->data['session_id'], | |
]; | |
$sql = 'INSERT INTO ' . $this->oauth_token_table . $this->db->sql_build_array('INSERT', $data); | |
$this->db->sql_query($sql); | |
} | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hasAccessToken($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedToken) | |
{ | |
return true; | |
} | |
$data = [ | |
'user_id' => (int) $this->user->data['user_id'], | |
'provider' => $service, | |
]; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$data['session_id'] = $this->user->data['session_id']; | |
} | |
return $this->has_access_token($data); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function clearToken($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
$this->cachedToken = null; | |
$sql = 'DELETE FROM ' . $this->oauth_token_table . ' | |
WHERE user_id = ' . (int) $this->user->data['user_id'] . " | |
AND provider = '" . $this->db->sql_escape($service) . "'"; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; | |
} | |
$this->db->sql_query($sql); | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function clearAllTokens() | |
{ | |
$this->cachedToken = null; | |
$sql = 'DELETE FROM ' . $this->oauth_token_table . ' | |
WHERE user_id = ' . (int) $this->user->data['user_id']; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS && isset($this->user->data['session_id'])) | |
{ | |
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; | |
} | |
$this->db->sql_query($sql); | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function storeAuthorizationState($service, $state) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
$this->cachedState = $state; | |
$data = [ | |
'user_id' => (int) $this->user->data['user_id'], | |
'provider' => $service, | |
'oauth_state' => $state, | |
'session_id' => $this->user->data['session_id'], | |
]; | |
$sql = 'INSERT INTO ' . $this->oauth_state_table . ' ' . $this->db->sql_build_array('INSERT', $data); | |
$this->db->sql_query($sql); | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hasAuthorizationState($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedState) | |
{ | |
return true; | |
} | |
$data = [ | |
'user_id' => (int) $this->user->data['user_id'], | |
'provider' => $service, | |
]; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$data['session_id'] = $this->user->data['session_id']; | |
} | |
return (bool) $this->get_state_row($data); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function retrieveAuthorizationState($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedState) | |
{ | |
return $this->cachedState; | |
} | |
$data = [ | |
'user_id' => (int) $this->user->data['user_id'], | |
'provider' => $service, | |
]; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$data['session_id'] = $this->user->data['session_id']; | |
} | |
return $this->get_state_row($data); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function clearAuthorizationState($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
$this->cachedState = null; | |
$sql = 'DELETE FROM ' . $this->oauth_state_table . ' | |
WHERE user_id = ' . (int) $this->user->data['user_id'] . " | |
AND provider = '" . $this->db->sql_escape($service) . "'"; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; | |
} | |
$this->db->sql_query($sql); | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function clearAllAuthorizationStates() | |
{ | |
$this->cachedState = null; | |
$sql = 'DELETE FROM ' . $this->oauth_state_table . ' | |
WHERE user_id = ' . (int) $this->user->data['user_id']; | |
if ((int) $this->user->data['user_id'] === ANONYMOUS) | |
{ | |
$sql .= " AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; | |
} | |
$this->db->sql_query($sql); | |
return $this; | |
} | |
/** | |
* Updates the user_id field in the database associated with the token. | |
* | |
* @param int $user_id The user identifier | |
* @return void | |
*/ | |
public function set_user_id($user_id) | |
{ | |
if (!$this->cachedToken) | |
{ | |
return; | |
} | |
$data = [ | |
'user_id' => (int) $user_id, | |
]; | |
$sql = 'UPDATE ' . $this->oauth_token_table . ' | |
SET ' . $this->db->sql_build_array('UPDATE', $data) . ' | |
WHERE user_id = ' . (int) $this->user->data['user_id'] . " | |
AND session_id = '" . $this->db->sql_escape($this->user->data['session_id']) . "'"; | |
$this->db->sql_query($sql); | |
} | |
/** | |
* Checks to see if an access token exists solely by the session_id of the user. | |
* | |
* @param string $service The OAuth service name | |
* @return bool true if the user's access token exists, | |
* false if the user's access token does not exist | |
*/ | |
public function has_access_token_by_session($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedToken) | |
{ | |
return true; | |
} | |
$data = [ | |
'session_id' => $this->user->data['session_id'], | |
'provider' => $service, | |
]; | |
return $this->has_access_token($data); | |
} | |
/** | |
* Checks to see if a state exists solely by the session_id of the user. | |
* | |
* @param string $service The OAuth service name | |
* @return bool true if the user's state exists, | |
* false if the user's state does not exist | |
*/ | |
public function has_state_by_session($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedState) | |
{ | |
return true; | |
} | |
$data = [ | |
'session_id' => $this->user->data['session_id'], | |
'provider' => $service, | |
]; | |
return (bool) $this->get_state_row($data); | |
} | |
/** | |
* A helper function that performs the query for has access token functions. | |
* | |
* @param array $data The SQL WHERE data | |
* @return bool true if the user's access token exists, | |
* false if the user's access token does not exist | |
*/ | |
protected function has_access_token($data) | |
{ | |
return (bool) $this->get_access_token_row($data); | |
} | |
/** | |
* A helper function that performs the query for retrieving access token functions by session. | |
* Also checks if the token is a valid token. | |
* | |
* @param string $service The OAuth service provider name | |
* @return TokenInterface | |
* @throws TokenNotFoundException | |
*/ | |
public function retrieve_access_token_by_session($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedToken instanceof TokenInterface) | |
{ | |
return $this->cachedToken; | |
} | |
$data = [ | |
'session_id' => $this->user->data['session_id'], | |
'provider' => $service, | |
]; | |
return $this->_retrieve_access_token($data); | |
} | |
/** | |
* A helper function that performs the query for retrieving state functions by session. | |
* | |
* @param string $service The OAuth service provider name | |
* @return string The OAuth state | |
* @throws AuthorizationStateNotFoundException | |
*/ | |
public function retrieve_state_by_session($service) | |
{ | |
$service = $this->get_service_name_for_db($service); | |
if ($this->cachedState) | |
{ | |
return $this->cachedState; | |
} | |
$data = [ | |
'session_id' => $this->user->data['session_id'], | |
'provider' => $service, | |
]; | |
return $this->_retrieve_state($data); | |
} | |
/** | |
* A helper function that performs the query for retrieve access token functions. | |
* Also checks if the token is a valid token. | |
* | |
* @param array $data The SQL WHERE data | |
* @return TokenInterface | |
* @throws TokenNotFoundException | |
*/ | |
protected function _retrieve_access_token($data) | |
{ | |
$row = $this->get_access_token_row($data); | |
if (!$row) | |
{ | |
throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_NOT_STORED'); | |
} | |
$token = $this->json_decode_token($row['oauth_token']); | |
// Ensure that the token was serialized/unserialized correctly | |
if (!($token instanceof TokenInterface)) | |
{ | |
$this->clearToken($data['provider']); | |
throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_INCORRECTLY_STORED'); | |
} | |
$this->cachedToken = $token; | |
return $token; | |
} | |
/** | |
* A helper function that performs the query for retrieve state functions. | |
* | |
* @param array $data The SQL WHERE data | |
* @return string The OAuth state | |
* @throws AuthorizationStateNotFoundException | |
*/ | |
protected function _retrieve_state($data) | |
{ | |
$row = $this->get_state_row($data); | |
if (!$row) | |
{ | |
throw new AuthorizationStateNotFoundException(); | |
} | |
$this->cachedState = $row['oauth_state']; | |
return $this->cachedState; | |
} | |
/** | |
* A helper function that performs the query for retrieving an access token. | |
* | |
* @param array $data The SQL WHERE data | |
* @return array|false array with the OAuth token row, | |
* false if the token does not exist | |
*/ | |
protected function get_access_token_row($data) | |
{ | |
$sql = 'SELECT oauth_token | |
FROM ' . $this->oauth_token_table . ' | |
WHERE ' . $this->db->sql_build_array('SELECT', $data); | |
$result = $this->db->sql_query($sql); | |
$row = $this->db->sql_fetchrow($result); | |
$this->db->sql_freeresult($result); | |
return $row; | |
} | |
/** | |
* A helper function that performs the query for retrieving a state. | |
* | |
* @param array $data The SQL WHERE data | |
* @return array|false array with the OAuth state row, | |
* false if the state does not exist | |
*/ | |
protected function get_state_row($data) | |
{ | |
$sql = 'SELECT oauth_state | |
FROM ' . $this->oauth_state_table . ' | |
WHERE ' . $this->db->sql_build_array('SELECT', $data); | |
$result = $this->db->sql_query($sql); | |
$row = $this->db->sql_fetchrow($result); | |
$this->db->sql_freeresult($result); | |
return $row; | |
} | |
/** | |
* A helper function that JSON encodes a TokenInterface's data. | |
* | |
* @param TokenInterface $token | |
* @return string The json encoded TokenInterface's data | |
*/ | |
public function json_encode_token(TokenInterface $token) | |
{ | |
$members = [ | |
'accessToken' => $token->getAccessToken(), | |
'endOfLife' => $token->getEndOfLife(), | |
'extraParams' => $token->getExtraParams(), | |
'refreshToken' => $token->getRefreshToken(), | |
'token_class' => get_class($token), | |
]; | |
// Handle additional data needed for OAuth1 tokens | |
if ($token instanceof StdOAuth1Token) | |
{ | |
$members['requestToken'] = $token->getRequestToken(); | |
$members['requestTokenSecret'] = $token->getRequestTokenSecret(); | |
$members['accessTokenSecret'] = $token->getAccessTokenSecret(); | |
} | |
return json_encode($members); | |
} | |
/** | |
* A helper function that JSON decodes a data string and creates a TokenInterface. | |
* | |
* @param string $json The json encoded TokenInterface's data | |
* @return TokenInterface | |
* @throws TokenNotFoundException | |
*/ | |
public function json_decode_token($json) | |
{ | |
$token_data = json_decode($json, true); | |
if ($token_data === null) | |
{ | |
throw new TokenNotFoundException('AUTH_PROVIDER_OAUTH_TOKEN_ERROR_INCORRECTLY_STORED'); | |
} | |
$token_class = $token_data['token_class']; | |
$access_token = $token_data['accessToken']; | |
$refresh_token = $token_data['refreshToken']; | |
$endOfLife = $token_data['endOfLife']; | |
$extra_params = $token_data['extraParams']; | |
/** | |
* Create the token | |
* @var TokenInterface $token | |
*/ | |
$token = new $token_class($access_token, $refresh_token, TokenInterface::EOL_NEVER_EXPIRES, $extra_params); | |
$token->setEndOfLife($endOfLife); | |
// Handle OAuth 1.0 specific elements | |
if ($token instanceof StdOAuth1Token) | |
{ | |
$token->setRequestToken($token_data['requestToken']); | |
$token->setRequestTokenSecret($token_data['requestTokenSecret']); | |
$token->setAccessTokenSecret($token_data['accessTokenSecret']); | |
} | |
return $token; | |
} | |
/** | |
* Returns the service name as it must be stored in the database. | |
* | |
* @param string $provider The OAuth provider name | |
* @return string The OAuth service name | |
*/ | |
protected function get_service_name_for_db($provider) | |
{ | |
// Enforce the naming convention for oauth services | |
if (strpos($provider, 'auth.provider.oauth.service.') !== 0) | |
{ | |
$provider = 'auth.provider.oauth.service.' . strtolower($provider); | |
} | |
return $provider; | |
} | |
} |