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; | |
| } | |
| } |