diff --git a/autoload.php b/autoload.php deleted file mode 100644 index 4e50f135f..000000000 --- a/autoload.php +++ /dev/null @@ -1,26 +0,0 @@ -=5.4", - "google/auth": "dev-master" + "google/auth": "dev-master", + "monolog/monolog": "^1.17" }, "require-dev": { "phpunit/phpunit": "3.7.*", diff --git a/src/Google/AccessToken.php b/src/Google/AccessToken.php new file mode 100644 index 000000000..d9889a436 --- /dev/null +++ b/src/Google/AccessToken.php @@ -0,0 +1,227 @@ +http = $http; + $this->cache = $cache; + $this->setAccessToken($token); + } + + /** + * @param string|array $token + * @throws InvalidArgumentException + */ + public function setAccessToken($token) + { + if (is_string($token)) { + if ($json = json_decode($token, true)) { + $token = $json; + } else { + // assume $token is just the token string + $token = [ + 'access_token' => $token, + ]; + } + } + if ($token == null) { + throw new InvalidArgumentException('invalid json token'); + } + if (!isset($token['access_token'])) { + throw new InvalidArgumentException("Invalid token format"); + } + $this->token = $token; + } + + public function getAccessToken() + { + return $this->token; + } + + /** + * Revoke an OAuth2 access token or refresh token. This method will revoke the current access + * token, if a token isn't provided. + * @throws Google_Auth_Exception + * @param string|null $token The token (access token or a refresh token) that should be revoked. + * @return boolean Returns True if the revocation was successful, otherwise False. + */ + public function revokeToken() + { + if (!$this->token) { + // Not initialized, no token to actually revoke + return false; + } elseif (array_key_exists('refresh_token', $this->token)) { + $token = $this->token['refresh_token']; + } else { + $token = $this->token['access_token']; + } + + $request = $this->http->createRequest('POST', Google_Client::OAUTH2_REVOKE_URI); + $request->addHeader('Cache-Control', 'no-store'); + $request->addHeader('Content-Type', 'application/x-www-form-urlencoded'); + $request->getBody()->replaceFields(array('token' => $token)); + + $response = $this->http->send($request); + if ($response->getStatusCode() == 200) { + $this->token = null; + + return true; + } + + return false; + } + + /** + * Retrieve and cache a certificates file. + * + * @param $url string location + * @throws Google_Auth_Exception + * @return array certificates + */ + private function retrieveCertsFromLocation($url) + { + // If we're retrieving a local file, just grab it. + if ("http" != substr($url, 0, 4)) { + $file = file_get_contents($url); + if ($file) { + return json_decode($file, true); + } else { + throw new Google_Auth_Exception( + "Failed to retrieve verification certificates: '" . + $url . "'." + ); + } + } + + $response = $this->http->get($url); + + if ($response->getStatusCode() == 200) { + return $response->json(); + } + throw new Google_Auth_Exception( + "Failed to retrieve verification certificates: '" . + $response->getBody()->getContents() . "'.", + $response->getStatusCode() + ); + } + + // Gets federated sign-on certificates to use for verifying identity tokens. + // Returns certs as array structure, where keys are key ids, and values + // are PEM encoded certificates. + private function getFederatedSignOnCerts() + { + $cache = $this->getCache(); + + if (!$certs = $cache->get('federated_signon_certs')) { + $certs = $this->retrieveCertsFromLocation( + self::FEDERATED_SIGNON_CERT_URL + ); + + $cache->set('federated_signon_certs', $certs); + } + + return $certs; + } + + /** + * Verifies an id token and returns the authenticated apiLoginTicket. + * Throws an exception if the id token is not valid. + * The audience parameter can be used to control which id tokens are + * accepted. By default, the id token must have been issued to this OAuth2 client. + * + * @param $audience + * @return array the token payload, if successful + * @throws Google_Auth_Exception + */ + public function verifyIdToken($audience = null) + { + if (empty($this->token['id_token'])) { + throw new LogicException('id_token cannot be null'); + } + + // Check signature + $certs = $this->getFederatedSignonCerts(); + foreach ($certs as $keyName => $pem) { + $key = openssl_x509_read($pem); + + try { + $payload = JWT::decode($this->token['id_token'], $key, array('RS256')); + if (is_null($audience) || (property_exists($resp, 'aud') && $resp->aud == $audience)) { + return $payload; + } + openssl_x509_free($key); + } catch (ExpiredException $e) { + return false; + // continue + } catch (DomainException $e) { + // continue + } + } + + return false; + } + + public function getCache() + { + if (!$this->cache) { + $this->cache = $this->createDefaultCache(); + } + + return $this->cache; + } + + protected function createDefaultCache() + { + return new Google_Cache_File(sys_get_temp_dir().'/google-api-php-client'); + } +} \ No newline at end of file diff --git a/src/Google/Auth/AppIdentity.php b/src/Google/Auth/AppIdentity.php index d475870dd..f74e60027 100644 --- a/src/Google/Auth/AppIdentity.php +++ b/src/Google/Auth/AppIdentity.php @@ -20,11 +20,8 @@ * which is 5.3 and above only, so if you include this in a PHP 5.2 * setup or one without 5.3 things will blow up. */ -use google\appengine\api\app_identity\AppIdentityService; -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} +use google\appengine\api\app_identity\AppIdentityService; /** * Authentication via the Google App Engine App Identity service. diff --git a/src/Google/Auth/ComputeEngine.php b/src/Google/Auth/ComputeEngine.php deleted file mode 100644 index 76d269d45..000000000 --- a/src/Google/Auth/ComputeEngine.php +++ /dev/null @@ -1,149 +0,0 @@ - - */ -class Google_Auth_ComputeEngine implements Google_Auth_Interface -{ - const METADATA_AUTH_URL = - 'http://metadata/computeMetadata/v1/instance/service-accounts/default/token'; - private $client; - private $token; - - public function __construct(Google_Client $client, $config = null) - { - $this->client = $client; - } - - /** - * Perform an authenticated / signed apiHttpRequest. - * This function takes the apiHttpRequest, calls apiAuth->sign on it - * (which can modify the request in what ever way fits the auth mechanism) - * and then calls apiCurlIO::makeRequest on the signed request - * - * @param GuzzleHttp\Message\Request $request - * @return GuzzleHttp\Message\Response The resulting HTTP response including the - * responseHttpCode, responseHeaders and responseBody. - */ - public function authenticatedRequest($request) - { - $request = $this->sign($request); - - return $this->client->getHttpClient()->send($request); - } - - /** - * @param string $token - * @throws Google_Auth_Exception - */ - public function setAccessToken($token) - { - $token = json_decode($token, true); - if ($token == null) { - throw new Google_Auth_Exception('Could not json decode the token'); - } - if (! isset($token['access_token'])) { - throw new Google_Auth_Exception("Invalid token format"); - } - $token['created'] = time(); - $this->token = $token; - } - - public function getAccessToken() - { - return json_encode($this->token); - } - - /** - * Acquires a new access token from the compute engine metadata server. - * @throws Google_Auth_Exception - */ - public function acquireAccessToken() - { - $request = new Request( - 'GET', - self::METADATA_AUTH_URL, - array( - 'Metadata-Flavor' => 'Google' - ) - ); - $http = $this->client->getHttpClient(); - $http->setDefaultOption('disable_gzip', true); - $response = $http->send($request); - - if ($response->getStatusCode() == 200) { - $this->setAccessToken((string) $response->getBody()); - $this->token['created'] = time(); - return $this->getAccessToken(); - } else { - throw new Google_Auth_Exception( - sprintf( - "Error fetching service account access token, message: '%s'", - $response->getBody() - ), - $response->getStatusCode() - ); - } - } - - /** - * Include an accessToken in a given apiHttpRequest. - * @param GuzzleHttp\Message\Request $request - * @return GuzzleHttp\Message\Request $request - * @throws Google_Auth_Exception - */ - public function sign($request) - { - if ($this->isAccessTokenExpired()) { - $this->acquireAccessToken(); - } - - $this->client->getLogger()->debug('Compute engine service account authentication'); - - $request->setHeader('Authorization', 'Bearer ' . $this->token['access_token']); - - return $request; - } - - /** - * Returns if the access_token is expired. - * @return bool Returns True if the access_token is expired. - */ - public function isAccessTokenExpired() - { - if (!$this->token || !isset($this->token['created'])) { - return true; - } - - // If the token is set to expire in the next 30 seconds. - $expired = ($this->token['created'] - + ($this->token['expires_in'] - 30)) < time(); - - return $expired; - } -} diff --git a/src/Google/Auth/Exception.php b/src/Google/Auth/Exception.php index e4b75c14b..59420cedf 100644 --- a/src/Google/Auth/Exception.php +++ b/src/Google/Auth/Exception.php @@ -15,10 +15,6 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - class Google_Auth_Exception extends Google_Exception { } diff --git a/src/Google/Auth/Interface.php b/src/Google/Auth/Interface.php deleted file mode 100644 index 78fdb65da..000000000 --- a/src/Google/Auth/Interface.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - */ -interface Google_Auth_Interface -{ - /** - * Used for when a request should be authenticated - * - * @param GuzzleHttp\Message\Request $request - * @return GuzzleHttp\Message\Request $request - */ - public function sign($request); -} diff --git a/src/Google/Auth/OAuth2.php b/src/Google/Auth/OAuth2.php deleted file mode 100644 index 7b2b9ea51..000000000 --- a/src/Google/Auth/OAuth2.php +++ /dev/null @@ -1,458 +0,0 @@ -setAuth($this); - $this->signingAlgorithm = 'RS256'; // default - - $this->client = $client; - $this->http = $client->getHttpClient(); - } - - /** - * @param string $code - * @throws Google_Auth_Exception - * @return string - */ - public function authenticate($code, $crossClient = false) - { - if (strlen($code) == 0) { - throw new Google_Auth_Exception("Invalid code"); - } - - $auth = $this->createAuthClient(); - $auth->setCode($code); - $auth->setRedirectUri($this->client->getClassConfig($this, 'redirect_uri')); - - $creds = $auth->fetchAuthToken($this->http); - - return $this->handleAuthTokenResponse($creds); - } - - /** - * Create a URL to obtain user authorization. - * The authorization endpoint allows the user to first - * authenticate, and then grant/deny the access request. - * @param string $scope The scope is expressed as a list of space-delimited strings. - * @return string - */ - public function createAuthUrl($scope) - { - $auth = $this->createAuthClient(); - $params = array( - 'response_type' => 'code', - 'access_type' => $this->client->getClassConfig($this, 'access_type'), - 'scope' => $scope, - ); - - // Prefer prompt to approval prompt. - if ($this->client->getClassConfig($this, 'prompt')) { - $params = $this->maybeAddParam($params, 'prompt'); - } else { - $params = $this->maybeAddParam($params, 'approval_prompt'); - } - $params = $this->maybeAddParam($params, 'login_hint'); - $params = $this->maybeAddParam($params, 'hd'); - $params = $this->maybeAddParam($params, 'openid.realm'); - $params = $this->maybeAddParam($params, 'include_granted_scopes'); - - // If the list of scopes contains plus.login, add request_visible_actions - // to auth URL. - $rva = $this->client->getClassConfig($this, 'request_visible_actions'); - if (strpos($scope, 'plus.login') && strlen($rva) > 0) { - $params['request_visible_actions'] = $rva; - } - - if (isset($this->state)) { - $params['state'] = $this->state; - } - - $url = $auth->buildFullAuthorizationUri($params); - - return (string) $url; - } - - /** - * @param string $token - * @throws Google_Auth_Exception - */ - public function setAccessToken($token) - { - $token = json_decode($token, true); - if ($token == null) { - throw new Google_Auth_Exception('Could not json decode the token'); - } - if (! isset($token['access_token'])) { - throw new Google_Auth_Exception("Invalid token format"); - } - $this->token = $token; - } - - public function getAccessToken() - { - return json_encode($this->token); - } - - public function setState($state) - { - $this->state = $state; - } - - public function setSigningKey($signingKey) - { - $this->signingKey = $signingKey; - } - - public function setSigningAlgorithm($signingAlgorithm) - { - $this->signingAlgorithm = $signingAlgorithm; - } - - public function sign($request) - { - // add the developer key to the request before signing it - if ($this->client->getClassConfig($this, 'developer_key')) { - $request->getQuery()->set('key', $this->client->getClassConfig($this, 'developer_key')); - } - - // Cannot sign the request without an OAuth access token. - if (empty($this->token) && empty($this->signingKey)) { - return $request; - } - - // Check if the token is set to expire in the next 30 seconds - // (or has already expired). - if ($this->isAccessTokenExpired()) { - if ($this->signingKey) { - $this->refreshTokenWithAssertion(); - } else { - $this->client->getLogger()->debug('OAuth2 access token expired'); - if (! array_key_exists('refresh_token', $this->token)) { - $error = "The OAuth 2.0 access token has expired," - ." and a refresh token is not available. Refresh tokens" - ." are not returned for responses that were auto-approved."; - - $this->client->getLogger()->error($error); - throw new Google_Auth_Exception($error); - } - $this->refreshToken($this->token['refresh_token']); - } - } - - $this->client->getLogger()->debug('OAuth2 authentication'); - - // Add the OAuth2 header to the request - $request->setHeader('Authorization', 'Bearer ' . $this->token['access_token']); - - return $request; - } - - public function refreshToken($refreshToken) - { - $this->client->getLogger()->info('OAuth2 access token refresh'); - $auth = $this->createAuthClient(); - $auth->setRefreshToken($refreshToken); - - $creds = $auth->fetchAuthToken($this->http); - - return $this->handleAuthTokenResponse($creds); - } - - /** - * Revoke an OAuth2 access token or refresh token. This method will revoke the current access - * token, if a token isn't provided. - * @throws Google_Auth_Exception - * @param string|null $token The token (access token or a refresh token) that should be revoked. - * @return boolean Returns True if the revocation was successful, otherwise False. - */ - public function revokeToken($token = null) - { - if (!$token) { - if (!$this->token) { - // Not initialized, no token to actually revoke - return false; - } elseif (array_key_exists('refresh_token', $this->token)) { - $token = $this->token['refresh_token']; - } else { - $token = $this->token['access_token']; - } - } - - $request = $this->http->createRequest('POST', self::OAUTH2_REVOKE_URI); - $request->addHeader('Cache-Control', 'no-store'); - $request->addHeader('Content-Type', 'application/x-www-form-urlencoded'); - $request->getBody()->replaceFields(array('token' => $token)); - - $response = $this->http->send($request); - if ($response->getStatusCode() == 200) { - $this->token = null; - - return true; - } - - return false; - } - - /** - * Retrieve and cache a certificates file. - * - * @param $url string location - * @throws Google_Auth_Exception - * @return array certificates - */ - public function retrieveCertsFromLocation($url) - { - // If we're retrieving a local file, just grab it. - if ("http" != substr($url, 0, 4)) { - $file = file_get_contents($url); - if ($file) { - return json_decode($file, true); - } else { - throw new Google_Auth_Exception( - "Failed to retrieve verification certificates: '" . - $url . "'." - ); - } - } - - $request = $this->http->createRequest('GET', $url); - $response = $this->http->send($request); - - if ($response->getStatusCode() == 200) { - return $response->json(); - } - throw new Google_Auth_Exception( - "Failed to retrieve verification certificates: '" . - $response->getBody()->getContents() . "'.", - $response->getStatusCode() - ); - } - - // Gets federated sign-on certificates to use for verifying identity tokens. - // Returns certs as array structure, where keys are key ids, and values - // are PEM encoded certificates. - private function getFederatedSignOnCerts() - { - return $this->retrieveCertsFromLocation( - $this->client->getClassConfig($this, 'federated_signon_certs_url') - ); - } - - /** - * Verifies an id token and returns the authenticated apiLoginTicket. - * Throws an exception if the id token is not valid. - * The audience parameter can be used to control which id tokens are - * accepted. By default, the id token must have been issued to this OAuth2 client. - * - * @param $id_token - * @param $audience - * @return array the token payload, if successful - * @throws Google_Auth_Exception - */ - public function verifyIdToken($id_token = null, $audience = null) - { - if (is_null($id_token)) { - if (isset($this->token['id_token'])) { - $id_token = $this->token['id_token']; - } else { - throw new LogicException('id_token cannot be null'); - } - } - - if (is_null($audience)) { - $audience = $this->client->getClassConfig($this, 'client_id'); - } - - $auth = $this->createAuthClient(); - $auth->setIdToken($id_token); - $auth->setAudience($audience); - - // Check signature - $certs = $this->getFederatedSignonCerts(); - foreach ($certs as $keyName => $pem) { - try { - $key = openssl_x509_read($pem); - $payload = $auth->verifyIdToken($key, array('RS256')); - } catch (\DomainException $e) { - // try next cert - $this->client->getLogger()->notice('ID token validation failed: '.$e->getMessage()); - } - openssl_x509_free($key); - if (!empty($payload)) { - return (array) $payload; - } - } - - throw new Google_Auth_Exception("Invalid token signature: $id_token"); - } - - /** - * Fetches a fresh access token with a given assertion token. - * @param Google_Auth_AssertionCredentials $assertionCredentials optional. - * @return void - */ - public function refreshTokenWithAssertion() - { - if (is_null($this->signingKey)) { - throw new LogicException('Cannot refresh token with assertion without signing key'); - } - - $this->client->getLogger()->info( - 'OAuth2 access token refresh with Signed JWT assertion grants.' - ); - - $auth = $this->createAuthClient(); - $auth->setGrantType(OAuth2::JWT_URN); - $auth->setAudience(self::OAUTH2_TOKEN_URI); - $auth->setScope($this->client->getScopes()); - - $creds = $auth->fetchAuthToken($this->http); - - return $this->handleAuthTokenResponse($creds); - } - - /** - * Add a parameter to the auth params if not empty string. - */ - private function maybeAddParam($params, $name) - { - $param = $this->client->getClassConfig($this, $name); - if ($param != '') { - $params[$name] = $param; - } - return $params; - } - - /** - * create a default google auth object - */ - private function createAuthClient() - { - $auth = new OAuth2( - array( - 'clientId' => $this->client->getClassConfig($this, 'client_id'), - 'clientSecret' => $this->client->getClassConfig($this, 'client_secret'), - 'authorizationUri' => self::OAUTH2_AUTH_URL, - 'tokenCredentialUri' => self::OAUTH2_TOKEN_URI, - 'redirectUri' => $this->client->getClassConfig($this, 'redirect_uri'), - 'issuer' => $this->client->getClassConfig($this, 'client_id'), - 'signingKey' => $this->signingKey, - 'signingAlgorithm' => $this->signingAlgorithm, - ) - ); - - return $auth; - } - - /** - * Returns if the access_token is expired. - * @return bool Returns True if the access_token is expired. - */ - public function isAccessTokenExpired() - { - if (!$this->token || !isset($this->token['created'])) { - return true; - } - - // If the token is set to expire in the next 30 seconds. - $expired = ($this->token['created'] - + ($this->token['expires_in'] - 30)) < time(); - - return $expired; - } - - /** - * handle the http response, set the access token, and raise an error if - * applicable - */ - private function handleAuthTokenResponse($creds) - { - if ($creds != null && isset($creds['access_token'])) { - $this->setAccessToken(json_encode($creds)); - $this->token['created'] = time(); - return $this->getAccessToken(); - } elseif ($creds != null && isset($creds['error'])) { - $errorText = $creds['error']; - if (isset($creds['error_description'])) { - $errorText .= ": " . $creds['error_description']; - } - } - throw new Google_Auth_Exception( - sprintf( - "Error fetching OAuth2 access token, message: '%s'", - $errorText - ) - ); - } -} diff --git a/src/Google/Auth/Simple.php b/src/Google/Auth/Simple.php deleted file mode 100644 index 46f2857e5..000000000 --- a/src/Google/Auth/Simple.php +++ /dev/null @@ -1,64 +0,0 @@ -client = $client; - } - - /** - * Perform an authenticated / signed apiHttpRequest. - * This function takes the apiHttpRequest, calls apiAuth->sign on it - * (which can modify the request in what ever way fits the auth mechanism) - * and then calls apiCurlIO::makeRequest on the signed request - * - * @param GuzzleHttp\Message\Request $request - * @return GuzzleHttp\Message\Request The resulting HTTP response including the - * responseHttpCode, responseHeaders and responseBody. - */ - public function authenticatedRequest($request) - { - $request = $this->sign($request); - - return $this->client->getHttpClient()->send($request); - } - - public function sign($request) - { - $key = $this->client->getClassConfig($this, 'developer_key'); - if ($key) { - $this->client->getLogger()->debug( - 'Simple API Access developer key authentication' - ); - $request->getQuery()->set('key', $key); - } - return $request; - } -} diff --git a/src/Google/Cache/Abstract.php b/src/Google/Cache/Abstract.php deleted file mode 100644 index ff19f36ac..000000000 --- a/src/Google/Cache/Abstract.php +++ /dev/null @@ -1,53 +0,0 @@ - - */ -abstract class Google_Cache_Abstract -{ - - abstract public function __construct(Google_Client $client); - - /** - * Retrieves the data for the given key, or false if they - * key is unknown or expired - * - * @param String $key The key who's data to retrieve - * @param boolean|int $expiration Expiration time in seconds - * - */ - abstract public function get($key, $expiration = false); - - /** - * Store the key => $value set. The $value is serialized - * by this function so can be of any type - * - * @param string $key Key of the data - * @param string $value data - */ - abstract public function set($key, $value); - - /** - * Removes the key/data pair for the given $key - * - * @param String $key - */ - abstract public function delete($key); -} diff --git a/src/Google/Cache/Apc.php b/src/Google/Cache/Apc.php index 67a64ddb0..f3bb1afe3 100644 --- a/src/Google/Cache/Apc.php +++ b/src/Google/Cache/Apc.php @@ -15,9 +15,8 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} +use Google\Auth\CacheInterface; +use Psr\Log\LoggerInterface; /** * A persistent storage class based on the APC cache, which is not @@ -27,23 +26,23 @@ * * @author Chris Chabot */ -class Google_Cache_Apc extends Google_Cache_Abstract +class Google_Cache_Apc implements CacheInterface { /** - * @var Google_Client the current client + * @var Psr\Log\LoggerInterface logger */ - private $client; + private $logger; - public function __construct(Google_Client $client) + public function __construct(LoggerInterface $logger = null) { + $this->logger = $logger; + if (! function_exists('apc_add') ) { $error = "Apc functions not available"; - $client->getLogger()->error($error); + $this->log('error', $error); throw new Google_Cache_Exception($error); } - - $this->client = $client; } /** @@ -53,14 +52,16 @@ public function get($key, $expiration = false) { $ret = apc_fetch($key); if ($ret === false) { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'APC cache miss', array('key' => $key) ); return false; } if (is_numeric($expiration) && (time() - $ret['time'] > $expiration)) { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'APC cache miss (expired)', array('key' => $key, 'var' => $ret) ); @@ -68,7 +69,8 @@ public function get($key, $expiration = false) return false; } - $this->client->getLogger()->debug( + $this->log( + 'debug', 'APC cache hit', array('key' => $key, 'var' => $ret) ); @@ -85,14 +87,16 @@ public function set($key, $value) $rc = apc_store($key, $var); if ($rc == false) { - $this->client->getLogger()->error( + $this->log( + 'error', 'APC cache set failed', array('key' => $key, 'var' => $var) ); throw new Google_Cache_Exception("Couldn't store data"); } - $this->client->getLogger()->debug( + $this->log( + 'debug', 'APC cache set', array('key' => $key, 'var' => $var) ); @@ -104,10 +108,18 @@ public function set($key, $value) */ public function delete($key) { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'APC cache delete', array('key' => $key) ); apc_delete($key); } + + private function log($level, $message, $context = array()) + { + if ($this->logger) { + $this->logger->log($level, $message, $context); + } + } } diff --git a/src/Google/Cache/Exception.php b/src/Google/Cache/Exception.php index 2d751d583..6e4102e43 100644 --- a/src/Google/Cache/Exception.php +++ b/src/Google/Cache/Exception.php @@ -15,10 +15,6 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - class Google_Cache_Exception extends Google_Exception { } diff --git a/src/Google/Cache/File.php b/src/Google/Cache/File.php index 47256b89d..45b2ef00d 100644 --- a/src/Google/Cache/File.php +++ b/src/Google/Cache/File.php @@ -15,9 +15,8 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} +use Google\Auth\CacheInterface; +use Psr\Log\LoggerInterface; /* * This class implements a basic on disk storage. While that does @@ -27,21 +26,21 @@ * * @author Chris Chabot */ -class Google_Cache_File extends Google_Cache_Abstract +class Google_Cache_File implements CacheInterface { const MAX_LOCK_RETRIES = 10; private $path; private $fh; /** - * @var Google_Client the current client + * @var use Psr\Log\LoggerInterface logger */ - private $client; + private $logger; - public function __construct(Google_Client $client) + public function __construct($path, LoggerInterface $logger = null) { - $this->client = $client; - $this->path = $this->client->getClassConfig($this, 'directory'); + $this->path = $path; + $this->logger = $logger; } public function get($key, $expiration = false) @@ -50,7 +49,8 @@ public function get($key, $expiration = false) $data = false; if (!file_exists($storageFile)) { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'File cache miss', array('key' => $key, 'file' => $storageFile) ); @@ -60,7 +60,8 @@ public function get($key, $expiration = false) if ($expiration) { $mtime = filemtime($storageFile); if ((time() - $mtime) >= $expiration) { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'File cache miss (expired)', array('key' => $key, 'file' => $storageFile) ); @@ -74,7 +75,8 @@ public function get($key, $expiration = false) $data = fread($this->fh, filesize($storageFile)); $data = unserialize($data); } else { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'Cache file was empty', array('file' => $storageFile) ); @@ -82,7 +84,8 @@ public function get($key, $expiration = false) $this->unlock($storageFile); } - $this->client->getLogger()->debug( + $this->log( + 'debug', 'File cache hit', array('key' => $key, 'file' => $storageFile, 'var' => $data) ); @@ -100,12 +103,14 @@ public function set($key, $value) $result = fwrite($this->fh, $data); $this->unlock($storageFile); - $this->client->getLogger()->debug( + $this->log( + 'debug', 'File cache set', array('key' => $key, 'file' => $storageFile, 'var' => $value) ); } else { - $this->client->getLogger()->notice( + $this->log( + 'notice', 'File cache set failed', array('key' => $key, 'file' => $storageFile) ); @@ -116,14 +121,16 @@ public function delete($key) { $file = $this->getCacheFile($key); if (file_exists($file) && !unlink($file)) { - $this->client->getLogger()->error( + $this->log( + 'error', 'File cache delete failed', array('key' => $key, 'file' => $file) ); throw new Google_Cache_Exception("Cache file could not be deleted"); } - $this->client->getLogger()->debug( + $this->log( + 'debug', 'File cache delete', array('key' => $key, 'file' => $file) ); @@ -166,7 +173,8 @@ private function acquireWriteLock($storageFile) { $rc = $this->acquireLock(LOCK_EX, $storageFile); if (!$rc) { - $this->client->getLogger()->notice( + $this->log( + 'notice', 'File cache write lock failed', array('file' => $storageFile) ); @@ -180,7 +188,8 @@ private function acquireLock($type, $storageFile) $mode = $type == LOCK_EX ? "w" : "r"; $this->fh = fopen($storageFile, $mode); if (!$this->fh) { - $this->client->getLogger()->error( + $this->log( + 'error', 'Failed to open file during lock acquisition', array('file' => $storageFile) ); @@ -206,4 +215,11 @@ public function unlock($storageFile) flock($this->fh, LOCK_UN); } } + + private function log($level, $message, $context = array()) + { + if ($this->logger) { + $this->logger->log($level, $message, $context); + } + } } diff --git a/src/Google/Cache/Memcache.php b/src/Google/Cache/Memcache.php index 4a415afa7..0d6183699 100644 --- a/src/Google/Cache/Memcache.php +++ b/src/Google/Cache/Memcache.php @@ -15,9 +15,8 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} +use Google\Auth\CacheInterface; +use Psr\Log\LoggerInterface; /** * A persistent storage class based on the memcache, which is not @@ -29,7 +28,7 @@ * * @author Chris Chabot */ -class Google_Cache_Memcache extends Google_Cache_Abstract +class Google_Cache_Memcache implements CacheInterface { private $connection = false; private $mc = false; @@ -37,35 +36,23 @@ class Google_Cache_Memcache extends Google_Cache_Abstract private $port; /** - * @var Google_Client the current client + * @var use Psr\Log\LoggerInterface logger */ - private $client; + private $logger; - public function __construct(Google_Client $client) + public function __construct($host = null, $port = null, LoggerInterface $logger = null) { + $this->logger = $logger; + if (!function_exists('memcache_connect') && !class_exists("Memcached")) { $error = "Memcache functions not available"; - $client->getLogger()->error($error); + $this->log('error', $error); throw new Google_Cache_Exception($error); } - $this->client = $client; - - if ($client->isAppEngine()) { - // No credentials needed for GAE. - $this->mc = new Memcached(); - $this->connection = true; - } else { - $this->host = $client->getClassConfig($this, 'host'); - $this->port = $client->getClassConfig($this, 'port'); - if (empty($this->host) || (empty($this->port) && (string) $this->port != "0")) { - $error = "You need to supply a valid memcache host and port"; - - $client->getLogger()->error($error); - throw new Google_Cache_Exception($error); - } - } + $this->host = $host; + $this->port = $port; } /** @@ -81,14 +68,16 @@ public function get($key, $expiration = false) $ret = memcache_get($this->connection, $key); } if ($ret === false) { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'Memcache cache miss', array('key' => $key) ); return false; } if (is_numeric($expiration) && (time() - $ret['time'] > $expiration)) { - $this->client->getLogger()->debug( + $this->log( + 'debug', 'Memcache cache miss (expired)', array('key' => $key, 'var' => $ret) ); @@ -96,7 +85,8 @@ public function get($key, $expiration = false) return false; } - $this->client->getLogger()->debug( + $this->log( + 'debug', 'Memcache cache hit', array('key' => $key, 'var' => $ret) ); @@ -123,7 +113,7 @@ public function set($key, $value) $rc = memcache_set($this->connection, $key, $data, false); } if ($rc == false) { - $this->client->getLogger()->error( + $this->log('error', 'Memcache cache set failed', array('key' => $key, 'var' => $data) ); @@ -131,7 +121,8 @@ public function set($key, $value) throw new Google_Cache_Exception("Couldn't store data in cache"); } - $this->client->getLogger()->debug( + $this->log( + 'debug', 'Memcache cache set', array('key' => $key, 'var' => $data) ); @@ -150,7 +141,8 @@ public function delete($key) memcache_delete($this->connection, $key, 0); } - $this->client->getLogger()->debug( + $this->log( + 'debug', 'Memcache cache delete', array('key' => $key) ); @@ -177,8 +169,15 @@ private function connect() if (! $this->connection) { $error = "Couldn't connect to memcache server"; - $this->client->getLogger()->error($error); + $this->log('error', $error); throw new Google_Cache_Exception($error); } } + + private function log($level, $message, $context = array()) + { + if ($this->logger) { + $this->logger->log($level, $message, $context); + } + } } diff --git a/src/Google/Cache/Null.php b/src/Google/Cache/Null.php index 21b6a1cb3..723df9f93 100644 --- a/src/Google/Cache/Null.php +++ b/src/Google/Cache/Null.php @@ -15,22 +15,15 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} +use Google\Auth\CacheInterface; /** * A blank storage class, for cases where caching is not * required. */ -class Google_Cache_Null extends Google_Cache_Abstract +class Google_Cache_Null implements CacheInterface { - public function __construct(Google_Client $client) - { - - } - - /** + /** * @inheritDoc */ public function get($key, $expiration = false) diff --git a/src/Google/Client.php b/src/Google/Client.php index 99f9a123e..0ea786d80 100644 --- a/src/Google/Client.php +++ b/src/Google/Client.php @@ -15,14 +15,19 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/autoload.php'; -} - +use Google\Auth\ApplicationDefaultCredentials; +use Google\Auth\CacheInterface; +use Google\Auth\OAuth2; +use Google\Auth\ScopedAccessToken; +use Google\Auth\Simple; use GuzzleHttp\Client; use GuzzleHttp\ClientInterface; +use GuzzleHttp\Collection; use GuzzleHttp\Post\PostBodyInterface; use GuzzleHttp\Stream\Stream; +use Psr\Log\LoggerInterface; +use Monolog\Logger; + /** * The Google API Client * http://code.google.com/p/google-api-php-client/ @@ -31,16 +36,30 @@ class Google_Client { const LIBVER = "2.0.0-alpha"; const USER_AGENT_SUFFIX = "google-api-php-client/"; + const OAUTH2_REVOKE_URI = 'https://accounts.google.com/o/oauth2/revoke'; + const OAUTH2_TOKEN_URI = 'https://www.googleapis.com/oauth2/v3/token'; + const OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth'; + /** - * @var Google_Auth_Interface $auth + * @var Google\Auth\OAuth2 $auth */ private $auth; /** - * @var Google_Cache_Abstract $cache + * @var GuzzleHttp\ClientInterface $http + */ + private $http; + + /** + * @var Google\Auth\CacheInterface $cache */ private $cache; + /** + * @var array access token + */ + private $token; + /** * @var Google_Config $config */ @@ -58,10 +77,21 @@ class Google_Client /** @var array $scopes */ // Scopes requested by the client - protected $requestedScopes = array(); - - // definitions of services that are discovered. - protected $services = array(); + protected $requestedScopes = []; + + public static $retryConfig = [ + // Delays are specified in seconds + 'initial_delay' => 1, + 'max_delay' => 60, + // Base number for exponential backoff + 'factor' => 2, + // A random number between -jitter and jitter will be added to the + // factor on each iteration to allow for better distribution of + // retries. + 'jitter' => .5, + // Maximum number of retries allowed + 'retries' => 0 + ]; // Used to track authenticated state, can't discover services after doing authenticate() private $authenticated = false; @@ -71,35 +101,38 @@ class Google_Client * * @param $config Google_Config or string for the ini file to load */ - public function __construct($config = null, ClientInterface $httpClient = null) + public function __construct($config = array()) { - if (is_string($config) && strlen($config)) { - $config = new Google_Config($config); - } else if ( !($config instanceof Google_Config)) { - $config = new Google_Config(); + $this->config = Collection::fromConfig($config, [ + 'application_name' => '', - if ($this->isAppEngine()) { - // Automatically use Memcache if we're in AppEngine. - $config->setCacheClass('Google_Cache_Memcache'); - } + // Don't change these unless you're working against a special development + // or testing environment. + 'base_path' => 'https://www.googleapis.com', - if ($this->isAppEngine()) { - // Automatically disable compress.zlib, as currently unsupported. - $config->setClassConfig('request', 'disable_gzip', true); - } - } + // https://developers.google.com/console + 'client_id' => '', + 'client_secret' => '', + 'redirect_uri' => null, + 'state' => null, - if ($config->getIoClass() == Google_Config::USE_AUTO_IO_SELECTION) { - if (function_exists('curl_version') && function_exists('curl_exec') - && !$this->isAppEngine()) { - $config->setIoClass("Google_IO_Curl"); - } else { - $config->setIoClass("Google_IO_Stream"); - } - } + // Simple API access key, also from the API console. Ensure you get + // a Server key, and not a Browser key. + 'developer_key' => '', - $this->config = $config; - $this->httpClient = $httpClient; + // Other OAuth2 parameters. + 'hd' => '', + 'prompt' => '', + 'openid.realm' => '', + 'include_granted_scopes' => null, + 'login_hint' => '', + 'request_visible_actions' => '', + 'access_type' => 'online', + 'approval_prompt' => 'auto', + + // misc configuration + 'enable_gzip_for_uploads' => false, + ]); } /** @@ -124,139 +157,181 @@ public function getLibraryVersion() */ public function authenticate($code, $crossClient = false) { - $this->authenticated = true; - return $this->getAuth()->authenticate($code, $crossClient); - } + if (strlen($code) == 0) { + throw new InvalidArgumentException("Invalid code"); + } - /** - * Set the auth config from the JSON string provided. - * This structure should match the file downloaded from - * the "Download JSON" button on in the Google Developer - * Console. - * @param string $json the configuration json - * @throws Google_Exception - */ - public function setAuthConfig($json) - { - $data = json_decode($json); - if (isset($data->installed) || isset($data->web)) { - $key = isset($data->installed) ? 'installed' : 'web'; - $this->setClientId($data->$key->client_id); - $this->setClientSecret($data->$key->client_secret); - if (isset($data->$key->redirect_uris)) { - $this->setRedirectUri($data->$key->redirect_uris[0]); - } - } else { - $this->getAuth()->setSigningKey($data->private_key); - $this->setClientId($data->client_email); + $auth = $this->getOAuth2Service(); + $auth->setCode($code); + $auth->setRedirectUri($this->getConfig('redirect_uri')); + + $creds = $auth->fetchAuthToken($this->getHttpClient()); + if ($creds && isset($creds['access_token'])) { + $this->setAccessToken($creds); } + + return $creds; } /** - * Set the auth config from the JSON file in the path - * provided. This should match the file downloaded from - * the "Download JSON" button on in the Google Developer - * Console. - * @param string $file the file location of the client json + * Create a URL to obtain user authorization. + * The authorization endpoint allows the user to first + * authenticate, and then grant/deny the access request. + * @param string|array $scope The scope is expressed as an array or list of space-delimited strings. + * @return string */ - public function setAuthConfigFile($file) + public function createAuthUrl($scope = null) { - $this->setAuthConfig(file_get_contents($file)); + if (empty($scope)) { + $scope = $this->prepareScopes(); + } + if (is_array($scope)) { + $scope = implode(' ', $scope); + } + + $params = array_filter([ + 'access_type' => $this->config->get('access_type'), + 'approval_prompt' => $this->config->get('prompt') ? null : $this->config->get('approval_prompt'), + 'hd' => $this->config->get('hosted_domain'), + 'include_granted_scopes' => + $this->config->get('include_granted_scopes') === null + ? null + : var_export($this->config->get('include_granted_scopes'), true), + 'login_hint' => $this->config->get('login_hint'), + 'openid.realm' => $this->config->get('openid.realm'), + 'prompt' => $this->config->get('prompt'), + 'response_type' => 'code', + 'scope' => $scope, + 'state' => $this->config->get('state'), + ]); + + // If the list of scopes contains plus.login, add request_visible_actions + // to auth URL. + $rva = $this->config->get('request_visible_actions'); + if (strlen($rva) > 0 && false !== strpos($scope, 'plus.login')) { + $params['request_visible_actions'] = $rva; + } + + $auth = $this->getOAuth2Service(); + + return (string) $auth->buildFullAuthorizationUri($params); } /** - * @throws Google_Auth_Exception - * @return array - * @visible For Testing + * Fetches a fresh OAuth 2.0 access token with the given refresh token. + * @param string $refreshToken */ - public function prepareScopes() + public function refreshToken($refreshToken) { - if (empty($this->requestedScopes)) { - throw new Google_Auth_Exception("No scopes specified"); + $this->getLogger()->info('OAuth2 access token refresh'); + $auth = $this->getOAuth2Service(); + $auth->setRefreshToken($refreshToken); + + $creds = $auth->fetchAuthToken($this->getHttpClient()); + if ($creds && isset($creds['access_token'])) { + $this->setAccessToken($creds); } - $scopes = implode(' ', $this->requestedScopes); - return $scopes; + + return $creds; } /** - * Set the OAuth 2.0 access token using the string that resulted from calling createAuthUrl() - * or Google_Client#getAccessToken(). - * @param string $accessToken JSON encoded string containing in the following format: - * {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer", - * "expires_in":3600, "id_token":"TOKEN", "created":1320790426} + * Fetches a fresh access token with a given assertion token. + * @param Google_Auth_AssertionCredentials $assertionCredentials optional. + * @return void */ - public function setAccessToken($accessToken) + public function refreshTokenWithAssertion() { - if ($accessToken == 'null') { - $accessToken = null; + if (is_null($this->config->get('signing_key'))) { + throw new LogicException('config parameter "signing_key" must be set to' + . ' refresh a token with assertion'); } - $this->getAuth()->setAccessToken($accessToken); - } + $this->getLogger()->log( + 'info', + 'OAuth2 access token refresh with Signed JWT assertion grants.' + ); + $auth = $this->getOAuth2Service(); + $auth->setGrantType(OAuth2::JWT_URN); + $auth->setAudience(self::OAUTH2_TOKEN_URI); + $auth->setScope($this->getScopes()); - /** - * Set the authenticator object - * @param Google_Auth_Interface $auth - */ - public function setAuth(Google_Auth_Interface $auth) - { - $this->config->setAuthClass(get_class($auth)); - $this->auth = $auth; - } + if ($creds && isset($creds['access_token'])) { + $this->setAccessToken($creds); + } - /** - * Set the Cache object - * @param Google_Cache_Abstract $cache - */ - public function setCache(Google_Cache_Abstract $cache) - { - $this->config->setCacheClass(get_class($cache)); - $this->cache = $cache; + return $creds; } - /** - * Set the Logger object - * @param Google_Logger_Abstract $logger - */ - public function setLogger(Google_Logger_Abstract $logger) + public function attachAuthListener(ClientInterface $http) { - $this->config->setLoggerClass(get_class($logger)); - $this->logger = $logger; + if ($key = $this->getConfig('developer_key')) { + // if a developer key is set, authorize using that + $simple = new Simple(['key' => $key]); + $http->setDefaultOption('auth', 'simple'); + $http->getEmitter()->attach($simple); + $this->getLogger()->log('info', 'Authenticated with API key'); + } else { + $scopes = $this->prepareScopes(); + if ($this->token) { + // if a token has been set manually, authorize using it instead + if ($this->isAccessTokenExpired() && isset($this->token['refresh_token'])) { + // try to get another access token with the refresh token + $this->refreshToken($this->token['refresh_token']); + } + $accessToken = $this->token['access_token']; + $scoped = new ScopedAccessToken( + function($scopes) use ($accessToken) { + return $accessToken; + }, + $scopes, + [] + ); + $http->setDefaultOption('auth', 'scoped'); + $http->getEmitter()->attach($scoped); + $this->getLogger()->log('info', 'Authenticated with access token'); + } else { + try { + // try to fetch credentials using ApplicationDefaultCredentials, if applicable + $fetcher = ApplicationDefaultCredentials::getFetcher( + $scopes, + $this->getHttpClient(), + array(), + $this->cache + ); + $http->setDefaultOption('auth', 'google_auth'); + $http->getEmitter()->attach($fetcher); + $this->getLogger()->log('info', 'Authenticated with Application Default Credentials'); + } catch (DomainException $e) { + // no auth + $this->getLogger()->log('info', $e->getMessage()); + } + } + } } /** - * Construct the OAuth 2.0 authorization request URI. - * @return string + * @param string|array $token + * @throws InvalidArgumentException */ - public function createAuthUrl() + public function setAccessToken($token) { - $scopes = $this->prepareScopes(); - return $this->getAuth()->createAuthUrl($scopes); + if (is_string($token)) { + $token = json_decode($token, true); + } + if ($token == null) { + throw new InvalidArgumentException('invalid json token'); + } + if (!isset($token['access_token'])) { + throw new InvalidArgumentException("Invalid token format"); + } + $this->token = $token; } - /** - * Get the OAuth 2.0 access token. - * @return string $accessToken JSON encoded string in the following format: - * {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer", - * "expires_in":3600,"id_token":"TOKEN", "created":1320790426} - */ public function getAccessToken() { - $token = $this->getAuth()->getAccessToken(); - // The response is json encoded, so could be the string null. - // It is arguable whether this check should be here or lower - // in the library. - return (null == $token || 'null' == $token || '[]' == $token) ? null : $token; - } - - /** - * Get the OAuth 2.0 refresh token. - * @return string $refreshToken refresh token or null if not available - */ - public function getRefreshToken() - { - return $this->getAuth()->getRefreshToken(); + return $this->token; } /** @@ -265,55 +340,23 @@ public function getRefreshToken() */ public function isAccessTokenExpired() { - return $this->getAuth()->isAccessTokenExpired(); - } - - /** - * Set OAuth 2.0 "state" parameter to achieve per-request customization. - * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2 - * @param string $state - */ - public function setState($state) - { - $this->getAuth()->setState($state); - } - - /** - * @param string $accessType Possible values for access_type include: - * {@code "offline"} to request offline access from the user. - * {@code "online"} to request online access from the user. - */ - public function setAccessType($accessType) - { - $this->config->setAccessType($accessType); - } + if (!$this->token) { + return true; + } - /** - * @param string $approvalPrompt Possible values for approval_prompt include: - * {@code "force"} to force the approval UI to appear. (This is the default value) - * {@code "auto"} to request auto-approval when possible. - */ - public function setApprovalPrompt($approvalPrompt) - { - $this->config->setApprovalPrompt($approvalPrompt); - } + $created = 0; + if (isset($this->token['created'])) { + $created = $this->token['created']; + } elseif (isset($this->token['id_token'])) { + $payload = $this->verifyIdToken(); + $created = $payload ? $payload->iat : 0; + } - /** - * Set the login hint, email address or sub id. - * @param string $loginHint - */ - public function setLoginHint($loginHint) - { - $this->config->setLoginHint($loginHint); - } + // If the token is set to expire in the next 30 seconds. + $expired = ($created + + ($this->token['expires_in'] - 30)) < time(); - /** - * Set the application name, this is included in the User-Agent HTTP header. - * @param string $applicationName - */ - public function setApplicationName($applicationName) - { - $this->config->setApplicationName($applicationName); + return $expired; } /** @@ -322,7 +365,12 @@ public function setApplicationName($applicationName) */ public function setClientId($clientId) { - $this->config->setClientId($clientId); + $this->config->set('client_id', $clientId); + } + + public function getClientId() + { + return $this->getConfig('client_id'); } /** @@ -331,7 +379,12 @@ public function setClientId($clientId) */ public function setClientSecret($clientSecret) { - $this->config->setClientSecret($clientSecret); + $this->config->set('client_secret', $clientSecret); + } + + public function getClientSecret() + { + return $this->config->get('client_secret'); } /** @@ -340,23 +393,12 @@ public function setClientSecret($clientSecret) */ public function setRedirectUri($redirectUri) { - $this->config->setRedirectUri($redirectUri); + $this->config->set('redirect_uri', $redirectUri); } - /** - * If 'plus.login' is included in the list of requested scopes, you can use - * this method to define types of app activities that your app will write. - * You can find a list of available types here: - * @link https://developers.google.com/+/api/moment-types - * - * @param array $requestVisibleActions Array of app activity types - */ - public function setRequestVisibleActions($requestVisibleActions) + public function getRedirectUri() { - if (is_array($requestVisibleActions)) { - $requestVisibleActions = join(" ", $requestVisibleActions); - } - $this->config->setRequestVisibleActions($requestVisibleActions); + return $this->config->get('redirect_uri'); } /** @@ -366,60 +408,17 @@ public function setRequestVisibleActions($requestVisibleActions) */ public function setDeveloperKey($developerKey) { - $this->config->setDeveloperKey($developerKey); - } - - /** - * Set the hd (hosted domain) parameter streamlines the login process for - * Google Apps hosted accounts. By including the domain of the user, you - * restrict sign-in to accounts at that domain. - * @param $hd string - the domain to use. - */ - public function setHostedDomain($hd) - { - $this->config->setHostedDomain($hd); - } - - /** - * Set the prompt hint. Valid values are none, consent and select_account. - * If no value is specified and the user has not previously authorized - * access, then the user is shown a consent screen. - * @param $prompt string - */ - public function setPrompt($prompt) - { - $this->config->setPrompt($prompt); + $this->config->set('developer_key', $developerKey); } /** - * openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth - * 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which - * an authentication request is valid. - * @param $realm string - the URL-space to use. - */ - public function setOpenidRealm($realm) - { - $this->config->setOpenidRealm($realm); - } - - /** - * If this is provided with the value true, and the authorization request is - * granted, the authorization will include any previous authorizations - * granted to this user/application combination for other scopes. - * @param $include boolean - the URL-space to use. - */ - public function setIncludeGrantedScopes($include) - { - $this->config->setIncludeGrantedScopes($include); - } - - /** - * Fetches a fresh OAuth 2.0 access token with the given refresh token. - * @param string $refreshToken + * Set OAuth 2.0 "state" parameter to achieve per-request customization. + * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2 + * @param string $state */ - public function refreshToken($refreshToken) + public function setState($state) { - $this->getAuth()->refreshToken($refreshToken); + $this->config->set('state', $state); } /** @@ -431,7 +430,12 @@ public function refreshToken($refreshToken) */ public function revokeToken($token = null) { - return $this->getAuth()->revokeToken($token); + $token = new Google_AccessToken( + $token ?: $this->getAccessToken(), + $this->getHttpClient() + ); + + return $token->revokeToken(); } /** @@ -444,24 +448,12 @@ public function revokeToken($token = null) */ public function verifyIdToken($token = null) { - return $this->getAuth()->verifyIdToken($token); - } + $token = new Google_AccessToken( + $token ?: $this->getAccessToken(), + $this->getHttpClient() + ); - /** - * Verify a JWT that was signed with your own certificates. - * - * @param $id_token string The JWT token - * @param $cert_location array of certificates - * @param $audience string the expected consumer of the token - * @param $issuer string the expected issuer, defaults to Google - * @param [$max_expiry] the max lifetime of a token, defaults to MAX_TOKEN_LIFETIME_SECS - * @return mixed token information if valid, false if not - */ - public function verifySignedJwt($id_token, $cert_location, $audience, $issuer, $max_expiry = null) - { - $auth = new Google_Auth_OAuth2($this); - $certs = $auth->retrieveCertsFromLocation($cert_location); - return $auth->verifySignedJwtWithCerts($id_token, $certs, $audience, $issuer, $max_expiry); + return $token->verifyIdToken(); } /** @@ -505,27 +497,17 @@ public function getScopes() } /** - * Declare whether batch calls should be used. This may increase throughput - * by making multiple requests in one connection. - * - * @param boolean $useBatch True if the batch support should - * be enabled. Defaults to False. - */ - public function setUseBatch($useBatch) - { - // This is actually an alias for setDefer. - $this->setDefer($useBatch); - } - - /** - * Declare whether making API calls should make the call immediately, or - * return a request which can be called with ->execute(); - * - * @param boolean $defer True if calls should not be executed right away. + * @throws Google_Auth_Exception + * @return array + * @visible For Testing */ - public function setDefer($defer) + public function prepareScopes() { - $this->deferExecution = $defer; + if (empty($this->requestedScopes)) { + return null; + } + $scopes = implode(' ', $this->requestedScopes); + return $scopes; } /** @@ -539,19 +521,14 @@ public function execute($request, $expectedClass = null) { $request->setHeader( 'User-Agent', - $this->getApplicationName() + $this->getConfig('application_name') . " " . self::USER_AGENT_SUFFIX . $this->getLibraryVersion() ); $http = $this->getHttpClient(); - if ($this->getClassConfig('request', 'disable_gzip')) { - $http->setDefaultOption('disable_gzip', true); - } - $config = $this->getClassConfig('Google_Task_Runner'); - $retryMap = $this->getClassConfig('Google_Service_Exception', 'retry_map'); - $result = Google_Http_REST::execute($http, $request, $config, $retryMap); + $result = Google_Http_REST::execute($http, $request, static::$retryConfig); $expectedClass = $expectedClass ?: $request->getHeader('X-Php-Expected-Class'); if ($expectedClass) { $result = new $expectedClass($result); @@ -560,6 +537,86 @@ public function execute($request, $expectedClass = null) return $result; } + /** + * Declare whether batch calls should be used. This may increase throughput + * by making multiple requests in one connection. + * + * @param boolean $useBatch True if the batch support should + * be enabled. Defaults to False. + */ + public function setUseBatch($useBatch) + { + // This is actually an alias for setDefer. + $this->setDefer($useBatch); + } + + /** + * Are we running in Google AppEngine? + * return bool + */ + public function isAppEngine() + { + return (isset($_SERVER['SERVER_SOFTWARE']) && + strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false); + } + + public function setConfig($name, $value) + { + $this->config->set($name, $value); + } + + public function getConfig($name, $default = null) + { + return $this->config->get($name) ?: $default; + } + + /** + * Set the auth config from new or deprecated JSON config. + * This structure should match the file downloaded from + * the "Download JSON" button on in the Google Developer + * Console. + * @param string|array|stdClass $json the configuration json + * @throws Google_Exception + */ + public function setAuthConfig($json) + { + if (is_string($json)) { + $data = json_decode($json); + } elseif (is_array($json)) { + $data = (object) $json; + } elseif (!$json instanceof stdClass) { + throw new InvalidArgumentException('invalid auth config type'); + } + + $key = isset($data->installed) ? 'installed' : 'web'; + if (isset($data->$key)) { + // old-style + $this->setClientId($data->$key->client_id); + $this->setClientSecret($data->$key->client_secret); + if (isset($data->$key->redirect_uris)) { + $this->setRedirectUri($data->$key->redirect_uris[0]); + } + } else { + // new-style + $this->setClientId($data->client_id); + $this->setClientSecret($data->client_secret); + if (isset($data->redirect_uris)) { + $this->setRedirectUri($data->redirect_uris[0]); + } + } + } + + /** + * Declare whether making API calls should make the call immediately, or + * return a request which can be called with ->execute(); + * + * @param boolean $defer True if calls should not be executed right away. + */ + public function setDefer($defer) + { + $this->deferExecution = $defer; + } + /** * Whether or not to return raw requests * @return boolean @@ -570,116 +627,124 @@ public function shouldDefer() } /** - * @return Google_Auth_Interface Authentication implementation + * @return Google\Auth\OAuth2 implementation */ - public function getAuth() + public function getOAuth2Service() { if (!isset($this->auth)) { - $class = $this->config->getAuthClass(); - $this->auth = new $class($this); + $this->auth = $this->createOAuth2Service(); } + return $this->auth; } /** - * @return Google_Cache_Abstract Cache implementation + * create a default google auth object */ - public function getCache() + public function createOAuth2Service() { - if (!isset($this->cache)) { - $class = $this->config->getCacheClass(); - $this->cache = new $class($this); - } - return $this->cache; + $auth = new OAuth2([ + 'clientId' => $this->getClientId(), + 'clientSecret' => $this->getClientSecret(), + 'authorizationUri' => self::OAUTH2_AUTH_URL, + 'tokenCredentialUri' => self::OAUTH2_TOKEN_URI, + 'redirectUri' => $this->getRedirectUri(), + 'issuer' => $this->config->get('client_id'), + 'signingKey' => $this->config->get('signing_key'), + 'signingAlgorithm' => $this->config->get('signing_algorithm'), + ]); + + return $auth; } /** - * @return Google_Logger_Abstract Logger implementation + * Set the Cache object + * @param Google\Auth\CacheInterface $cache */ - public function getLogger() + public function setCache(CacheInterface $cache) { - if (!isset($this->logger)) { - $class = $this->config->getLoggerClass(); - $this->logger = new $class($this); - } - return $this->logger; + $this->cache = $cache; } /** - * Retrieve custom configuration for a specific class. - * @param $class string|object - class or instance of class to retrieve - * @param $key string optional - key to retrieve - * @return array + * @return Google\Auth\CacheInterface Cache implementation */ - public function getClassConfig($class, $key = null) + public function getCache() { - if (!is_string($class)) { - $class = get_class($class); + if (!isset($this->cache)) { + $this->cache = $this->createDefaultCache(); } - return $this->config->getClassConfig($class, $key); + + return $this->cache; } - /** - * Set configuration specific to a given class. - * $config->setClassConfig('Google_Cache_File', - * array('directory' => '/tmp/cache')); - * @param $class string|object - The class name for the configuration - * @param $config string key or an array of configuration values - * @param $value string optional - if $config is a key, the value - * - */ - public function setClassConfig($class, $config, $value = null) + protected function createDefaultCache() { - if (!is_string($class)) { - $class = get_class($class); + if ($this->isAppEngine()) { + $cache = new Google_Cache_Memcache(); + } else { + $cacheDir = sys_get_temp_dir() . '/google-api-php-client'; + $cache = new Google_Cache_File($cacheDir); } - $this->config->setClassConfig($class, $config, $value); + return $cache; } /** - * @return string the base URL to use for calls to the APIs + * Set the Logger object + * @param Psr\Log\LoggerInterface $logger */ - public function getBasePath() + public function setLogger(LoggerInterface $logger) { - return $this->config->getBasePath(); + $this->logger = $logger; } /** - * @return string the name of the application + * @return Psr\Log\LoggerInterface implementation */ - public function getApplicationName() + public function getLogger() { - return $this->config->getApplicationName(); + if (!isset($this->logger)) { + $this->logger = $this->createDefaultLogger(); + } + + return $this->logger; } - /** - * Are we running in Google AppEngine? - * return bool - */ - public function isAppEngine() + protected function createDefaultLogger() { - return (isset($_SERVER['SERVER_SOFTWARE']) && - strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false); + $logger = new Logger('google-api-php-client'); + + return $logger; } - public function setHttpClient(ClientInterface $httpClient) + /** + * Set the Http Client object + * @param GuzzleHttp\ClientInterface $http + */ + public function setHttpClient(ClientInterface $http) { - $this->httpClient = $httpClient; + $this->http = $http; } + /** + * @return GuzzleHttp\ClientInterface implementation + */ public function getHttpClient() { - if (is_null($this->httpClient)) { - $this->httpClient = $this->createDefaultHttpClient(); + if (is_null($this->http)) { + $this->http = $this->createDefaultHttpClient(); } - return $this->httpClient; + return $this->http; } protected function createDefaultHttpClient() { - $options = array('base_url' => $this->getBasePath()); + $options = [ + 'base_url' => $this->config->get('base_path'), + 'defaults' => ['auth' => 'google_auth'], + ]; return new Client($options); } diff --git a/src/Google/Config.php b/src/Google/Config.php deleted file mode 100644 index 4ca8d903e..000000000 --- a/src/Google/Config.php +++ /dev/null @@ -1,448 +0,0 @@ -configuration = array( - // The application_name is included in the User-Agent HTTP header. - 'application_name' => '', - - // Which Authentication, Storage and HTTP IO classes to use. - 'auth_class' => 'Google_Auth_OAuth2', - 'io_class' => self::USE_AUTO_IO_SELECTION, - 'cache_class' => 'Google_Cache_File', - 'logger_class' => 'Google_Logger_Null', - - // Don't change these unless you're working against a special development - // or testing environment. - 'base_path' => 'https://www.googleapis.com', - - // Definition of class specific values, like file paths and so on. - 'classes' => array( - 'Google_IO_Abstract' => array( - 'request_timeout_seconds' => 100, - ), - 'Google_Logger_Abstract' => array( - 'level' => 'debug', - 'log_format' => "[%datetime%] %level%: %message% %context%\n", - 'date_format' => 'd/M/Y:H:i:s O', - 'allow_newlines' => true - ), - 'Google_Logger_File' => array( - 'file' => 'php://stdout', - 'mode' => 0640, - 'lock' => false, - ), - 'request' => array( - // Disable the use of gzip on calls if set to true. Defaults to false. - 'disable_gzip' => self::GZIP_ENABLED, - - // We default gzip to disabled on uploads even if gzip is otherwise - // enabled, due to some issues seen with small packet sizes for uploads. - // Please test with this option before enabling gzip for uploads in - // a production environment. - 'enable_gzip_for_uploads' => self::GZIP_UPLOADS_DISABLED, - ), - // If you want to pass in OAuth 2.0 settings, they will need to be - // structured like this. - 'Google_Auth_OAuth2' => array( - // Keys for OAuth 2.0 access, see the API console at - // https://developers.google.com/console - 'client_id' => '', - 'client_secret' => '', - 'redirect_uri' => null, - - // Simple API access key, also from the API console. Ensure you get - // a Server key, and not a Browser key. - 'developer_key' => '', - - // Other parameters. - 'hd' => '', - 'prompt' => '', - 'openid.realm' => '', - 'include_granted_scopes' => '', - 'login_hint' => '', - 'request_visible_actions' => '', - 'access_type' => 'online', - 'approval_prompt' => 'auto', - 'federated_signon_certs_url' => - 'https://www.googleapis.com/oauth2/v1/certs', - ), - 'Google_Task_Runner' => array( - // Delays are specified in seconds - 'initial_delay' => 1, - 'max_delay' => 60, - // Base number for exponential backoff - 'factor' => 2, - // A random number between -jitter and jitter will be added to the - // factor on each iteration to allow for better distribution of - // retries. - 'jitter' => .5, - // Maximum number of retries allowed - 'retries' => 0 - ), - 'Google_Service_Exception' => array( - 'retry_map' => array( - '500' => self::TASK_RETRY_ALWAYS, - '503' => self::TASK_RETRY_ALWAYS, - 'rateLimitExceeded' => self::TASK_RETRY_ALWAYS, - 'userRateLimitExceeded' => self::TASK_RETRY_ALWAYS, - CURLE_COULDNT_RESOLVE_HOST => self::TASK_RETRY_ALWAYS, - CURLE_COULDNT_CONNECT => self::TASK_RETRY_ALWAYS, - CURLE_OPERATION_TIMEOUTED => self::TASK_RETRY_ALWAYS, - CURLE_SSL_CONNECT_ERROR => self::TASK_RETRY_ALWAYS, - CURLE_GOT_NOTHING => self::TASK_RETRY_ALWAYS - ) - ), - // Set a default directory for the file cache. - 'Google_Cache_File' => array( - 'directory' => sys_get_temp_dir() . '/Google_Client' - ) - ), - ); - if ($ini_file_location) { - $ini = parse_ini_file($ini_file_location, true); - if (is_array($ini) && count($ini)) { - $merged_configuration = $ini + $this->configuration; - if (isset($ini['classes']) && isset($this->configuration['classes'])) { - $merged_configuration['classes'] = $ini['classes'] + $this->configuration['classes']; - } - $this->configuration = $merged_configuration; - } - } - } - - /** - * Set configuration specific to a given class. - * $config->setClassConfig('Google_Cache_File', - * array('directory' => '/tmp/cache')); - * @param $class string The class name for the configuration - * @param $config string key or an array of configuration values - * @param $value string optional - if $config is a key, the value - */ - public function setClassConfig($class, $config, $value = null) - { - if (!is_array($config)) { - if (!isset($this->configuration['classes'][$class])) { - $this->configuration['classes'][$class] = array(); - } - $this->configuration['classes'][$class][$config] = $value; - } else { - $this->configuration['classes'][$class] = $config; - } - } - - public function getClassConfig($class, $key = null) - { - if (!isset($this->configuration['classes'][$class])) { - return null; - } - if ($key === null) { - return $this->configuration['classes'][$class]; - } else { - return $this->configuration['classes'][$class][$key]; - } - } - - /** - * Return the configured cache class. - * @return string - */ - public function getCacheClass() - { - return $this->configuration['cache_class']; - } - - /** - * Return the configured logger class. - * @return string - */ - public function getLoggerClass() - { - return $this->configuration['logger_class']; - } - - /** - * Return the configured Auth class. - * @return string - */ - public function getAuthClass() - { - return $this->configuration['auth_class']; - } - - /** - * Set the auth class. - * - * @param $class string the class name to set - */ - public function setAuthClass($class) - { - $prev = $this->configuration['auth_class']; - if (!isset($this->configuration['classes'][$class]) && - isset($this->configuration['classes'][$prev])) { - $this->configuration['classes'][$class] = - $this->configuration['classes'][$prev]; - } - $this->configuration['auth_class'] = $class; - } - - /** - * Set the IO class. - * - * @param $class string the class name to set - */ - public function setIoClass($class) - { - $prev = $this->configuration['io_class']; - if (!isset($this->configuration['classes'][$class]) && - isset($this->configuration['classes'][$prev])) { - $this->configuration['classes'][$class] = - $this->configuration['classes'][$prev]; - } - $this->configuration['io_class'] = $class; - } - - /** - * Set the cache class. - * - * @param $class string the class name to set - */ - public function setCacheClass($class) - { - $prev = $this->configuration['cache_class']; - if (!isset($this->configuration['classes'][$class]) && - isset($this->configuration['classes'][$prev])) { - $this->configuration['classes'][$class] = - $this->configuration['classes'][$prev]; - } - $this->configuration['cache_class'] = $class; - } - - /** - * Set the logger class. - * - * @param $class string the class name to set - */ - public function setLoggerClass($class) - { - $prev = $this->configuration['logger_class']; - if (!isset($this->configuration['classes'][$class]) && - isset($this->configuration['classes'][$prev])) { - $this->configuration['classes'][$class] = - $this->configuration['classes'][$prev]; - } - $this->configuration['logger_class'] = $class; - } - - /** - * Return the configured IO class. - * - * @return string - */ - public function getIoClass() - { - return $this->configuration['io_class']; - } - - /** - * Set the application name, this is included in the User-Agent HTTP header. - * @param string $name - */ - public function setApplicationName($name) - { - $this->configuration['application_name'] = $name; - } - - /** - * @return string the name of the application - */ - public function getApplicationName() - { - return $this->configuration['application_name']; - } - - /** - * Set the client ID for the auth class. - * @param $clientId string - the API console client ID - */ - public function setClientId($clientId) - { - $this->setAuthConfig('client_id', $clientId); - } - - /** - * Set the client secret for the auth class. - * @param $secret string - the API console client secret - */ - public function setClientSecret($secret) - { - $this->setAuthConfig('client_secret', $secret); - } - - /** - * Set the redirect uri for the auth class. Note that if using the - * Javascript based sign in flow, this should be the string 'postmessage'. - * - * @param $uri string - the URI that users should be redirected to - */ - public function setRedirectUri($uri) - { - $this->setAuthConfig('redirect_uri', $uri); - } - - /** - * Set the app activities for the auth class. - * @param $rva string a space separated list of app activity types - */ - public function setRequestVisibleActions($rva) - { - $this->setAuthConfig('request_visible_actions', $rva); - } - - /** - * Set the the access type requested (offline or online.) - * @param $access string - the access type - */ - public function setAccessType($access) - { - $this->setAuthConfig('access_type', $access); - } - - /** - * Set when to show the approval prompt (auto or force) - * @param $approval string - the approval request - */ - public function setApprovalPrompt($approval) - { - $this->setAuthConfig('approval_prompt', $approval); - } - - /** - * Set the login hint (email address or sub identifier) - * @param $hint string - */ - public function setLoginHint($hint) - { - $this->setAuthConfig('login_hint', $hint); - } - - /** - * Set the developer key for the auth class. Note that this is separate value - * from the client ID - if it looks like a URL, its a client ID! - * @param $key string - the API console developer key - */ - public function setDeveloperKey($key) - { - $this->setAuthConfig('developer_key', $key); - } - - /** - * Set the hd (hosted domain) parameter streamlines the login process for - * Google Apps hosted accounts. By including the domain of the user, you - * restrict sign-in to accounts at that domain. - * - * This should not be used to ensure security on your application - check - * the hd values within an id token after sign in to ensure that the user - * is from the domain you were expecting. - * - * @param $hd string - the domain to use. - */ - public function setHostedDomain($hd) - { - $this->setAuthConfig('hd', $hd); - } - - /** - * Set the prompt hint. Valid values are none, consent and select_account. - * If no value is specified and the user has not previously authorized - * access, then the user is shown a consent screen. - * @param $prompt string - */ - public function setPrompt($prompt) - { - $this->setAuthConfig('prompt', $prompt); - } - - /** - * openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth - * 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which - * an authentication request is valid. - * @param $realm string - the URL-space to use. - */ - public function setOpenidRealm($realm) - { - $this->setAuthConfig('openid.realm', $realm); - } - - /** - * If this is provided with the value true, and the authorization request is - * granted, the authorization will include any previous authorizations - * granted to this user/application combination for other scopes. - * @param $include boolean - the URL-space to use. - */ - public function setIncludeGrantedScopes($include) - { - $this->setAuthConfig( - 'include_granted_scopes', - $include ? "true" : "false" - ); - } - - /** - * @return string the base URL to use for API calls - */ - public function getBasePath() - { - return $this->configuration['base_path']; - } - - /** - * Set the auth configuration for the current auth class. - * @param $key - the key to set - * @param $value - the parameter value - */ - private function setAuthConfig($key, $value) - { - if (!isset($this->configuration['classes'][$this->getAuthClass()])) { - $this->configuration['classes'][$this->getAuthClass()] = array(); - } - $this->configuration['classes'][$this->getAuthClass()][$key] = $value; - } -} diff --git a/src/Google/Http/MediaFileUpload.php b/src/Google/Http/MediaFileUpload.php index c567d2220..20381d6d1 100644 --- a/src/Google/Http/MediaFileUpload.php +++ b/src/Google/Http/MediaFileUpload.php @@ -19,10 +19,6 @@ use GuzzleHttp\Stream\Stream; use GuzzleHttp\Url; -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - /** * Manage large file uploads, which may be media but can be any type * of sizable data. diff --git a/src/Google/Http/Batch.php b/src/Google/Http/Parallel.php similarity index 95% rename from src/Google/Http/Batch.php rename to src/Google/Http/Parallel.php index 333232e0b..a28fdd3c9 100644 --- a/src/Google/Http/Batch.php +++ b/src/Google/Http/Parallel.php @@ -15,10 +15,6 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - use GuzzleHttp\Pool; use GuzzleHttp\Message\RequestInterface; use GuzzleHttp\Message\ResponseInterface; @@ -27,7 +23,7 @@ /** * Class to handle batched requests to the Google API service. */ -class Google_Http_Batch +class Google_Http_Parallel { /** @var array service requests to be executed. */ private $requests = array(); @@ -71,6 +67,7 @@ protected function parseResponse(BatchResults $responses) $response = $responses[$i]; if ( $response instanceof ResponseInterface && + $response->getStatusCode() < 300 && $class = $this->requests[$requestKeys[$j]]->getHeader('X-Php-Expected-Class') ) { $response = new $class($response->json()); diff --git a/src/Google/Http/REST.php b/src/Google/Http/REST.php index da2f43003..6bf51fa00 100644 --- a/src/Google/Http/REST.php +++ b/src/Google/Http/REST.php @@ -19,10 +19,6 @@ use GuzzleHttp\Message\ResponseInterface; use GuzzleHttp\ClientInterface; -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - /** * This class implements the RESTful transport of apiServiceRequest()'s */ @@ -42,15 +38,19 @@ public static function execute( ClientInterface $client, RequestInterface $request, $config = array(), - $retryMap = array() + $retryMap = null ) { $runner = new Google_Task_Runner( $config, sprintf('%s %s', $request->getMethod(), $request->getUrl()), array(get_class(), 'doExecute'), - array($client, $request, $retryMap) + array($client, $request) ); + if (!is_null($retryMap)) { + $runner->setRetryMap($retryMap); + } + return $runner->run(); } @@ -63,11 +63,11 @@ public static function execute( * @throws Google_Service_Exception on server side error (ie: not authenticated, * invalid or malformed post body, invalid url) */ - public static function doExecute(ClientInterface $client, RequestInterface $request, $retryMap = array()) + public static function doExecute(ClientInterface $client, RequestInterface $request) { $response = $client->send($request); - return self::decodeHttpResponse($response, $request, $retryMap); + return self::decodeHttpResponse($response, $request); } /** @@ -80,8 +80,7 @@ public static function doExecute(ClientInterface $client, RequestInterface $requ */ public static function decodeHttpResponse( ResponseInterface $response, - RequestInterface $request = null, - $retryMap = array() + RequestInterface $request = null ) { $result = $response->json(); $body = (string) $response->getBody(); @@ -94,7 +93,7 @@ public static function decodeHttpResponse( if (isset($result['error']) && isset($result['error']['errors'])) { $errors = $result['error']['errors']; } - throw new Google_Service_Exception($body, $code, null, $errors, $retryMap); + throw new Google_Service_Exception($body, $code, null, $errors); } // return raw response when "alt" is "media" diff --git a/src/Google/IO/Exception.php b/src/Google/IO/Exception.php index da9342df3..3151e6350 100644 --- a/src/Google/IO/Exception.php +++ b/src/Google/IO/Exception.php @@ -15,10 +15,6 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - class Google_IO_Exception extends Google_Exception implements Google_Task_Retryable { /** diff --git a/src/Google/Logger/Abstract.php b/src/Google/Logger/Abstract.php deleted file mode 100644 index d759b9579..000000000 --- a/src/Google/Logger/Abstract.php +++ /dev/null @@ -1,408 +0,0 @@ - 600, - self::ALERT => 550, - self::CRITICAL => 500, - self::ERROR => 400, - self::WARNING => 300, - self::NOTICE => 250, - self::INFO => 200, - self::DEBUG => 100, - ); - - /** - * @var integer $level The minimum logging level - */ - protected $level = self::DEBUG; - - /** - * @var string $logFormat The current log format - */ - protected $logFormat = self::DEFAULT_LOG_FORMAT; - /** - * @var string $dateFormat The current date format - */ - protected $dateFormat = self::DEFAULT_DATE_FORMAT; - - /** - * @var boolean $allowNewLines If newlines are allowed - */ - protected $allowNewLines = false; - - /** - * @param Google_Client $client The current Google client - */ - public function __construct(Google_Client $client) - { - $this->setLevel( - $client->getClassConfig('Google_Logger_Abstract', 'level') - ); - - $format = $client->getClassConfig('Google_Logger_Abstract', 'log_format'); - $this->logFormat = $format ? $format : self::DEFAULT_LOG_FORMAT; - - $format = $client->getClassConfig('Google_Logger_Abstract', 'date_format'); - $this->dateFormat = $format ? $format : self::DEFAULT_DATE_FORMAT; - - $this->allowNewLines = (bool) $client->getClassConfig( - 'Google_Logger_Abstract', - 'allow_newlines' - ); - } - - /** - * Sets the minimum logging level that this logger handles. - * - * @param integer $level - */ - public function setLevel($level) - { - $this->level = $this->normalizeLevel($level); - } - - /** - * Checks if the logger should handle messages at the provided level. - * - * @param integer $level - * @return boolean - */ - public function shouldHandle($level) - { - return $this->normalizeLevel($level) >= $this->level; - } - - /** - * System is unusable. - * - * @param string $message The log message - * @param array $context The log context - */ - public function emergency($message, array $context = array()) - { - $this->log(self::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message The log message - * @param array $context The log context - */ - public function alert($message, array $context = array()) - { - $this->log(self::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message The log message - * @param array $context The log context - */ - public function critical($message, array $context = array()) - { - $this->log(self::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message The log message - * @param array $context The log context - */ - public function error($message, array $context = array()) - { - $this->log(self::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message The log message - * @param array $context The log context - */ - public function warning($message, array $context = array()) - { - $this->log(self::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message The log message - * @param array $context The log context - */ - public function notice($message, array $context = array()) - { - $this->log(self::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message The log message - * @param array $context The log context - */ - public function info($message, array $context = array()) - { - $this->log(self::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message The log message - * @param array $context The log context - */ - public function debug($message, array $context = array()) - { - $this->log(self::DEBUG, $message, $context); - } - - /** - * Logs with an arbitrary level. - * - * @param mixed $level The log level - * @param string $message The log message - * @param array $context The log context - */ - public function log($level, $message, array $context = array()) - { - if (!$this->shouldHandle($level)) { - return false; - } - - $levelName = is_int($level) ? array_search($level, self::$levels) : $level; - $message = $this->interpolate( - array( - 'message' => $message, - 'context' => $context, - 'level' => strtoupper($levelName), - 'datetime' => new DateTime(), - ) - ); - - $this->write($message); - } - - /** - * Interpolates log variables into the defined log format. - * - * @param array $variables The log variables. - * @return string - */ - protected function interpolate(array $variables = array()) - { - $template = $this->logFormat; - - if (!$variables['context']) { - $template = str_replace('%context%', '', $template); - unset($variables['context']); - } else { - $this->reverseJsonInContext($variables['context']); - } - - foreach ($variables as $key => $value) { - if (strpos($template, '%'. $key .'%') !== false) { - $template = str_replace( - '%' . $key . '%', - $this->export($value), - $template - ); - } - } - - return $template; - } - - /** - * Reverses JSON encoded PHP arrays and objects so that they log better. - * - * @param array $context The log context - */ - protected function reverseJsonInContext(array &$context) - { - if (!$context) { - return; - } - - foreach ($context as $key => $val) { - if (!$val || !is_string($val) || !($val[0] == '{' || $val[0] == '[')) { - continue; - } - - $json = @json_decode($val); - if (is_object($json) || is_array($json)) { - $context[$key] = $json; - } - } - } - - /** - * Exports a PHP value for logging to a string. - * - * @param mixed $value The value to - */ - protected function export($value) - { - if (is_string($value)) { - if ($this->allowNewLines) { - return $value; - } - - return preg_replace('/[\r\n]+/', ' ', $value); - } - - if (is_resource($value)) { - return sprintf( - 'resource(%d) of type (%s)', - $value, - get_resource_type($value) - ); - } - - if ($value instanceof DateTime) { - return $value->format($this->dateFormat); - } - - if (version_compare(PHP_VERSION, '5.4.0', '>=')) { - $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; - - if ($this->allowNewLines) { - $options |= JSON_PRETTY_PRINT; - } - - return @json_encode($value, $options); - } - - return str_replace('\\/', '/', @json_encode($value)); - } - - /** - * Converts a given log level to the integer form. - * - * @param mixed $level The logging level - * @return integer $level The normalized level - * @throws Google_Logger_Exception If $level is invalid - */ - protected function normalizeLevel($level) - { - if (is_int($level) && array_search($level, self::$levels) !== false) { - return $level; - } - - if (is_string($level) && isset(self::$levels[$level])) { - return self::$levels[$level]; - } - - throw new Google_Logger_Exception( - sprintf("Unknown LogLevel: '%s'", $level) - ); - } - - /** - * Writes a message to the current log implementation. - * - * @param string $message The message - */ - abstract protected function write($message); -} diff --git a/src/Google/Logger/Exception.php b/src/Google/Logger/Exception.php deleted file mode 100644 index 6b0e87370..000000000 --- a/src/Google/Logger/Exception.php +++ /dev/null @@ -1,24 +0,0 @@ -getClassConfig('Google_Logger_File', 'file'); - if (!is_string($file) && !is_resource($file)) { - throw new Google_Logger_Exception( - 'File logger requires a filename or a valid file pointer' - ); - } - - $mode = $client->getClassConfig('Google_Logger_File', 'mode'); - if (!$mode) { - $this->mode = $mode; - } - - $this->lock = (bool) $client->getClassConfig('Google_Logger_File', 'lock'); - $this->file = $file; - } - - /** - * {@inheritdoc} - */ - protected function write($message) - { - if (is_string($this->file)) { - $this->open(); - } elseif (!is_resource($this->file)) { - throw new Google_Logger_Exception('File pointer is no longer available'); - } - - if ($this->lock) { - flock($this->file, LOCK_EX); - } - - fwrite($this->file, (string) $message); - - if ($this->lock) { - flock($this->file, LOCK_UN); - } - } - - /** - * Opens the log for writing. - * - * @return resource - */ - private function open() - { - // Used for trapping `fopen()` errors. - $this->trappedErrorNumber = null; - $this->trappedErrorString = null; - - $old = set_error_handler(array($this, 'trapError')); - - $needsChmod = !file_exists($this->file); - $fh = fopen($this->file, 'a'); - - restore_error_handler(); - - // Handles trapped `fopen()` errors. - if ($this->trappedErrorNumber) { - throw new Google_Logger_Exception( - sprintf( - "Logger Error: '%s'", - $this->trappedErrorString - ), - $this->trappedErrorNumber - ); - } - - if ($needsChmod) { - @chmod($this->file, $this->mode & ~umask()); - } - - return $this->file = $fh; - } - - /** - * Closes the log stream resource. - */ - private function close() - { - if (is_resource($this->file)) { - fclose($this->file); - } - } - - /** - * Traps `fopen()` errors. - * - * @param integer $errno The error number - * @param string $errstr The error string - */ - private function trapError($errno, $errstr) - { - $this->trappedErrorNumber = $errno; - $this->trappedErrorString = $errstr; - } - - public function __destruct() - { - $this->close(); - } -} diff --git a/src/Google/Logger/Null.php b/src/Google/Logger/Null.php deleted file mode 100644 index 62fa890c0..000000000 --- a/src/Google/Logger/Null.php +++ /dev/null @@ -1,43 +0,0 @@ -setLogger($logger); - } - } - - /** - * Sets the PSR-3 logger where logging will be delegated. - * - * NOTE: The `$logger` should technically implement - * `Psr\Log\LoggerInterface`, but we don't explicitly require this so that - * we can be compatible with PHP 5.2. - * - * @param Psr\Log\LoggerInterface $logger The PSR-3 logger - */ - public function setLogger(/*Psr\Log\LoggerInterface*/ $logger) - { - $this->logger = $logger; - } - - /** - * {@inheritdoc} - */ - public function shouldHandle($level) - { - return isset($this->logger) && parent::shouldHandle($level); - } - - /** - * {@inheritdoc} - */ - public function log($level, $message, array $context = array()) - { - if (!$this->shouldHandle($level)) { - return false; - } - - if ($context) { - $this->reverseJsonInContext($context); - } - - $levelName = is_int($level) ? array_search($level, self::$levels) : $level; - $this->logger->log($levelName, $message, $context); - } - - /** - * {@inheritdoc} - */ - protected function write($message, array $context = array()) - { - } -} diff --git a/src/Google/Service/Exception.php b/src/Google/Service/Exception.php index 65c945b73..abfd3f7f1 100644 --- a/src/Google/Service/Exception.php +++ b/src/Google/Service/Exception.php @@ -15,22 +15,13 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - -class Google_Service_Exception extends Google_Exception implements Google_Task_Retryable +class Google_Service_Exception extends Google_Exception { /** * Optional list of errors returned in a JSON body of an HTTP error response. */ protected $errors = array(); - /** - * @var array $retryMap Map of errors with retry counts. - */ - private $retryMap = array(); - /** * Override default constructor to add the ability to set $errors and a retry * map. @@ -46,8 +37,7 @@ public function __construct( $message, $code = 0, Exception $previous = null, - $errors = array(), - array $retryMap = null + $errors = array() ) { if (version_compare(PHP_VERSION, '5.3.0') >= 0) { parent::__construct($message, $code, $previous); @@ -56,10 +46,6 @@ public function __construct( } $this->errors = $errors; - - if (is_array($retryMap)) { - $this->retryMap = $retryMap; - } } /** @@ -79,27 +65,4 @@ public function getErrors() { return $this->errors; } - - /** - * Gets the number of times the associated task can be retried. - * - * NOTE: -1 is returned if the task can be retried indefinitely - * - * @return integer - */ - public function allowedRetries() - { - if (isset($this->retryMap[$this->code])) { - return $this->retryMap[$this->code]; - } - - $errors = $this->getErrors(); - - if (!empty($errors) && isset($errors[0]['reason']) && - isset($this->retryMap[$errors[0]['reason']])) { - return $this->retryMap[$errors[0]['reason']]; - } - - return 0; - } } diff --git a/src/Google/Service/Resource.php b/src/Google/Service/Resource.php index d97f6475e..a719402a5 100644 --- a/src/Google/Service/Resource.php +++ b/src/Google/Service/Resource.php @@ -15,10 +15,6 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - /** * Implements the actual methods/resources of the discovered Google API using magic function * calling overloading (__call()), which on call will see if the method name (plus.activities.list) @@ -194,13 +190,15 @@ public function call($name, $arguments, $expected_class = null) $parameters ); - $request = $this->client->getHttpClient()->createRequest( + $http = $this->client->getHttpClient(); + $this->client->attachAuthListener($http); + + $request = $http->createRequest( $method['httpMethod'], $url, ['json' => $postBody] ); - $request = $this->client->getAuth()->sign($request); if ($this->client->shouldDefer()) { // @TODO find a better way to do this $request->setHeader('X-Php-Expected-Class', $expected_class); diff --git a/src/Google/Task/Exception.php b/src/Google/Task/Exception.php index 231bf2b1d..5422e6fc4 100644 --- a/src/Google/Task/Exception.php +++ b/src/Google/Task/Exception.php @@ -15,10 +15,6 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - class Google_Task_Exception extends Google_Exception { } diff --git a/src/Google/Task/Retryable.php b/src/Google/Task/Retryable.php index cd8bdec7b..19aa4ddc2 100644 --- a/src/Google/Task/Retryable.php +++ b/src/Google/Task/Retryable.php @@ -15,22 +15,10 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - /** * Interface for checking how many times a given task can be retried following * a failure. */ interface Google_Task_Retryable { - /** - * Gets the number of times the associated task can be retried. - * - * NOTE: -1 is returned if the task can be retried indefinitely - * - * @return integer - */ - public function allowedRetries(); } diff --git a/src/Google/Task/Runner.php b/src/Google/Task/Runner.php index 8bba66881..67bad1c46 100644 --- a/src/Google/Task/Runner.php +++ b/src/Google/Task/Runner.php @@ -15,10 +15,6 @@ * limitations under the License. */ -if (!class_exists('Google_Client')) { - require_once dirname(__FILE__) . '/../autoload.php'; -} - /** * A task runner with exponential backoff support. * @@ -26,6 +22,10 @@ */ class Google_Task_Runner { + const TASK_RETRY_NEVER = 0; + const TASK_RETRY_ONCE = 1; + const TASK_RETRY_ALWAYS = -1; + /** * @var integer $maxDelay The max time (in seconds) to wait before a retry. */ @@ -68,6 +68,21 @@ class Google_Task_Runner */ private $arguments; + /** + * @var array $retryMap Map of errors with retry counts. + */ + protected $retryMap = [ + '500' => self::TASK_RETRY_ALWAYS, + '503' => self::TASK_RETRY_ALWAYS, + 'rateLimitExceeded' => self::TASK_RETRY_ALWAYS, + 'userRateLimitExceeded' => self::TASK_RETRY_ALWAYS, + CURLE_COULDNT_RESOLVE_HOST => self::TASK_RETRY_ALWAYS, + CURLE_COULDNT_CONNECT => self::TASK_RETRY_ALWAYS, + CURLE_OPERATION_TIMEOUTED => self::TASK_RETRY_ALWAYS, + CURLE_SSL_CONNECT_ERROR => self::TASK_RETRY_ALWAYS, + CURLE_GOT_NOTHING => self::TASK_RETRY_ALWAYS + ]; + /** * Creates a new task runner with exponential backoff support. * @@ -148,7 +163,7 @@ public function __construct( * * @return boolean */ - public function canAttmpt() + public function canAttempt() { return $this->attempts < $this->maxAttempts; } @@ -164,10 +179,10 @@ public function run() while ($this->attempt()) { try { return call_user_func_array($this->action, $this->arguments); - } catch (Google_Task_Retryable $exception) { - $allowedRetries = $exception->allowedRetries(); + } catch (Google_Service_Exception $exception) { + $allowedRetries = $this->allowedRetries($exception->getCode(), $exception->getErrors()); - if (!$this->canAttmpt() || !$allowedRetries) { + if (!$this->canAttempt() || !$allowedRetries) { throw $exception; } @@ -192,7 +207,7 @@ public function run() */ public function attempt() { - if (!$this->canAttmpt()) { + if (!$this->canAttempt()) { return false; } @@ -237,4 +252,30 @@ private function getJitter() { return $this->jitter * 2 * mt_rand() / mt_getrandmax() - $this->jitter; } + + /** + * Gets the number of times the associated task can be retried. + * + * NOTE: -1 is returned if the task can be retried indefinitely + * + * @return integer + */ + public function allowedRetries($code, $errors = array()) + { + if (isset($this->retryMap[$code])) { + return $this->retryMap[$code]; + } + + if (!empty($errors) && isset($errors[0]['reason']) && + isset($this->retryMap[$errors[0]['reason']])) { + return $this->retryMap[$errors[0]['reason']]; + } + + return 0; + } + + public function setRetryMap($retryMap) + { + $this->retryMap = $retryMap; + } } diff --git a/src/Google/autoload.php b/src/Google/autoload.php deleted file mode 100644 index 6f5f3ee3e..000000000 --- a/src/Google/autoload.php +++ /dev/null @@ -1,36 +0,0 @@ - + + Service/*.php diff --git a/tests/BaseTest.php b/tests/BaseTest.php index 72d47868b..daf50e9ae 100644 --- a/tests/BaseTest.php +++ b/tests/BaseTest.php @@ -19,63 +19,138 @@ class BaseTest extends PHPUnit_Framework_TestCase { private $key; private $token; + private $client; private $memcacheHost; private $memcachePort; protected $testDir = __DIR__; - public function __construct() + public function getClient() { - parent::__construct(); + if (!$this->client) { + $this->client = $this->createClient(); + } - $this->token = $this->loadToken(); - $this->memcacheHost = getenv('MEMCACHE_HOST') ? getenv('MEMCACHE_HOST') : null; - $this->memcachePort = getenv('MEMCACHE_PORT') ? getenv('MEMCACHE_PORT') : null; + return $this->client; } - public function getClient() + public function getCache() + { + return new Google_Cache_File(sys_get_temp_dir().'/google-api-php-client-tests'); + } + + private function createClient() { + $defaults = [ + 'auth' => 'google_auth', + 'exceptions' => false + ]; + if ($proxy = getenv('HTTP_PROXY')) { + $defaults['proxy'] = $proxy; + $defaults['verify'] = false; + } + $httpClient = new GuzzleHttp\Client([ + 'defaults' => $defaults, + ]); + $client = new Google_Client(); + $client->setHttpClient($httpClient); + $client->setScopes([ + "https://www.googleapis.com/auth/plus.me", + "https://www.googleapis.com/auth/urlshortener", + "https://www.googleapis.com/auth/tasks", + "https://www.googleapis.com/auth/adsense", + "https://www.googleapis.com/auth/youtube", + ]); + if ($this->key) { $client->setDeveloperKey($this->key); } - if (strlen($this->token)) { + if ($this->token) { $client->setAccessToken($this->token); } - if (strlen($this->memcacheHost)) { - $client->setClassConfig('Google_Cache_Memcache', 'host', $this->memcacheHost); - $client->setClassConfig('Google_Cache_Memcache', 'port', $this->memcachePort); - } - if ($proxy = getenv('HTTP_PROXY')) { - $httpClient = new GuzzleHttp\Client([ - 'defaults' => [ - 'proxy' => $proxy, - 'verify' => false, - ] - ]); - $client->setHttpClient($httpClient); - } + list($clientId, $clientSecret) = $this->getClientIdAndSecret(); + $client->setClientId($clientId); + $client->setClientSecret($clientSecret); + $client->setCache($this->getCache()); + return $client; } public function checkToken() { - if (!strlen($this->token)) { - $this->markTestSkipped("Test requires access token\nrun \"php tests/OAuthHelper.php\""); - return false; + $cache = $this->getCache(); + $this->token = $cache->get('access_token'); + if (!$this->token) { + if (!$this->tryToGetAnAccessToken()) { + return $this->markTestSkipped("Test requires access token"); + } } + + $client = $this->getClient(); + $client->setAccessToken($this->token); + + if ($client->isAccessTokenExpired()) { + if (isset($this->token['refresh_token'])) { + $this->token = $client->refreshToken($this->token['refresh_token']); + } + } + + $cache->set('access_token', $this->token); + return true; } - public function loadToken() + public function tryToGetAnAccessToken() { - if (file_exists($f = dirname(__FILE__) . DIRECTORY_SEPARATOR . '.accessToken')) { - $t = file_get_contents($f); - if ($token = json_decode($t, true)) { - if ($token['expires_in'] + $token['created'] > time()) { - return $t; - } + $client = $this->getClient(); + if (!($client->getClientId() && $client->getClientSecret())) { + $this->markTestSkipped("Test requires GCLOUD_CLIENT_ID and GCLOUD_CLIENT_SECRET to be set"); + } + + $client = $this->getClient(); + $client->setRedirectUri("urn:ietf:wg:oauth:2.0:oob"); + $client->setConfig('access_type', 'offline'); + $authUrl = $client->createAuthUrl(); + + echo "\nPlease enter the auth code:\n"; + ob_flush(); + `open '$authUrl'`; + $authCode = trim(fgets(STDIN)); + + if ($accessToken = $client->authenticate($authCode)) { + if (isset($accessToken['access_token'])) { + $this->token = $accessToken; + + return true; } } + + return false; + } + + private function getClientIdAndSecret() + { + $clientId = getenv('GCLOUD_CLIENT_ID') ? getenv('GCLOUD_CLIENT_ID') : null; + $clientSecret = getenv('GCLOUD_CLIENT_SECRET') ? getenv('GCLOUD_CLIENT_SECRET') : null; + + return array($clientId, $clientSecret); + } + + public function checkServiceAccountToken() + { + if (!$f = getenv('GOOGLE_APPLICATION_CREDENTIALS')) { + $skip = "This test requires the GOOGLE_APPLICATION_CREDENTIALS environment variable to be set\n" + . "see https://developers.google.com/accounts/docs/application-default-credentials"; + $this->markTestSkipped($skip); + + return false; + } + + if (!file_exists($f)) { + $this->markTestSkipped('invalid path for GOOGLE_APPLICATION_CREDENTIALS'); + } + + return true; } public function checkKey() diff --git a/tests/Google/AccessTokenTest.php b/tests/Google/AccessTokenTest.php new file mode 100644 index 000000000..6c58096a6 --- /dev/null +++ b/tests/Google/AccessTokenTest.php @@ -0,0 +1,160 @@ +getMock('GuzzleHttp\Post\PostBodyInterface'); + $postBody->expects($this->exactly(3)) + ->method('replaceFields') + ->will($this->returnCallback( + function ($fields) use (&$token) { + $token = isset($fields['token']) ? $fields['token'] : null; + } + )); + $request = $this->getMock('GuzzleHttp\Message\RequestInterface'); + $request->expects($this->exactly(3)) + ->method('getBody') + ->will($this->returnValue($postBody)); + $response = $this->getMock('GuzzleHttp\Message\ResponseInterface'); + $response->expects($this->exactly(3)) + ->method('getStatusCode') + ->will($this->returnValue(200)); + $http = $this->getMock('GuzzleHttp\ClientInterface'); + $http->expects($this->exactly(3)) + ->method('send') + ->will($this->returnValue($response)); + $http->expects($this->exactly(3)) + ->method('createRequest') + ->will($this->returnValue($request)); + + // Test with access token. + $t = new Google_AccessToken( + array( + 'access_token' => $accessToken, + 'created' => time(), + 'expires_in' => '3600' + ), + $http + ); + $this->assertTrue($t->revokeToken()); + $this->assertEquals($accessToken, $token); + + // Test with refresh token. + $t->setAccessToken( + array( + 'access_token' => $accessToken, + 'refresh_token' => $refreshToken, + 'created' => time(), + 'expires_in' => '3600' + ) + ); + $this->assertTrue($t->revokeToken()); + $this->assertEquals($refreshToken, $token); + + // Test with string token + $t->setAccessToken($accessToken2); + $this->assertTrue($t->revokeToken()); + $this->assertEquals($accessToken2, $token); + } + + // /** + // * Most of the logic for ID token validation is in AuthTest - + // * this is just a general check to ensure we verify a valid + // * id token if one exists. + // */ + // public function testValidateIdToken() + // { + // $this->checkToken(); + // $client = $this->getClient(); + // $token = json_decode($client->getAccessToken()); + // $segments = explode(".", $token->id_token); + // $this->assertEquals(3, count($segments)); + // // Extract the client ID in this case as it wont be set on the test client. + // $data = json_decode(JWT::urlSafeB64Decode($segments[1])); + // $oauth = new Google_Auth_OAuth2($client); + // $payload = $oauth->verifyIdToken($token->id_token, $data->aud); + // $this->assertArrayHasKey('sub', $payload); + // $this->assertTrue(strlen($payload['sub']) > 0); + + // // TODO: Need to be smart about testing/disabling the + // // caching for this test to make sense. Not sure how to do that + // // at the moment. + // $client = $this->getClient(); + // $data = json_decode(JWT::urlSafeB64Decode($segments[1])); + // $oauth = new Google_Auth_OAuth2($client); + // $payload = $oauth->verifyIdToken($token->id_token, $data->aud); + // $this->assertArrayHasKey('sub', $payload); + // $this->assertTrue(strlen($payload['sub']) > 0); + // } + + // /** + // * Test for revoking token when none is opened + // */ + // public function testRevokeWhenNoTokenExists() + // { + // $client = new Google_Client(); + // $this->assertFalse($client->revokeToken()); + // } + + // /** + // * Test that the ID token is properly refreshed. + // */ + // public function testRefreshTokenSetsValues() + // { + // $client = new Google_Client(); + // $request = $this->getMock('GuzzleHttp\Message\RequestInterface'); + // $request->expects($this->once()) + // ->method('getBody') + // ->will($this->returnValue($this->getMock('GuzzleHttp\Post\PostBodyInterface'))); + // $response = $this->getMock('GuzzleHttp\Message\ResponseInterface'); + // $response->expects($this->once()) + // ->method('json') + // ->will($this->returnValue(array( + // 'access_token' => 'xyz', + // 'id_token' => 'ID_TOKEN', + // ))); + // $response->expects($this->once()) + // ->method('getBody') + // ->will($this->returnValue($this->getMock('GuzzleHttp\Post\PostBody'))); + // $http = $this->getMock('GuzzleHttp\ClientInterface'); + // $http->expects($this->once()) + // ->method('send') + // ->will($this->returnValue($response)); + // $http->expects($this->once()) + // ->method('createRequest') + // ->will($this->returnValue($request)); + // $client->setHttpClient($http); + // $oauth = new Google_Auth_OAuth2($client); + // $oauth->refreshToken("REFRESH_TOKEN"); + // $token = json_decode($oauth->getAccessToken(), true); + // $this->assertEquals($token['id_token'], "ID_TOKEN"); + // } +} diff --git a/tests/Google/Auth/ComputeEngineTest.php b/tests/Google/Auth/ComputeEngineTest.php deleted file mode 100644 index e866a2303..000000000 --- a/tests/Google/Auth/ComputeEngineTest.php +++ /dev/null @@ -1,83 +0,0 @@ - "ACCESS_TOKEN", - 'expires_in' => "12345" - ) - ); - $response = $this->getMock("GuzzleHttp\Message\ResponseInterface", array(), array('')); - $response->expects($this->any()) - ->method('getStatusCode') - ->will($this->returnValue(200)); - $response->expects($this->any()) - ->method('getBody') - ->will($this->returnValue($response_data)); - $http = $this->getMockBuilder('GuzzleHttp\Client') - ->disableOriginalConstructor() - ->getMock(); - $http->expects($this->once()) - ->method('send') - ->will($this->returnValue($response)); - $client->setHttpClient($http); - - /* Run method */ - $oauth = new Google_Auth_ComputeEngine($client); - $oauth->acquireAccessToken(); - $token = json_decode($oauth->getAccessToken(), true); - - /* Check results */ - $this->assertEquals($token['access_token'], "ACCESS_TOKEN"); - $this->assertEquals($token['expires_in'], "12345"); - $this->assertTrue($token['created'] > 0); - } - - public function testSign() - { - $client = new Google_Client(); - $oauth = new Google_Auth_ComputeEngine($client); - - /* Load mock access token */ - $oauth->setAccessToken( - json_encode( - array( - 'access_token' => "ACCESS_TOKEN", - 'expires_in' => "12345" - ) - ) - ); - - /* Sign a URL and verify auth header is correctly set */ - $req = new Request('GET', 'http://localhost'); - $req = $oauth->sign($req); - $auth = $req->getHeader('authorization'); - $this->assertEquals('Bearer ACCESS_TOKEN', $auth); - } -} diff --git a/tests/Google/Auth/OAuth2Test.php b/tests/Google/Auth/OAuth2Test.php deleted file mode 100644 index b5a4be53c..000000000 --- a/tests/Google/Auth/OAuth2Test.php +++ /dev/null @@ -1,251 +0,0 @@ -getClient(); - $oauth = new Google_Auth_OAuth2($client); - - $client->setClientId('clientId1'); - $client->setClientSecret('clientSecret1'); - $client->setRedirectUri('http://localhost'); - $client->setDeveloperKey('devKey'); - $client->setAccessType('offline'); - $client->setApprovalPrompt('force'); - $client->setRequestVisibleActions('http://foo'); - - $req = new Request('GET', 'http://localhost'); - $req = $oauth->sign($req); - - $this->assertEquals('http://localhost?key=devKey', $req->getUrl()); - - // test accessToken - $oauth->setAccessToken( - json_encode( - array( - 'access_token' => 'ACCESS_TOKEN', - 'created' => time(), - 'expires_in' => '3600' - ) - ) - ); - - $req = $oauth->sign($req); - $auth = $req->getHeader('authorization'); - $this->assertEquals('Bearer ACCESS_TOKEN', $auth); - } - - public function testRevokeAccess() - { - $accessToken = "ACCESS_TOKEN"; - $refreshToken = "REFRESH_TOKEN"; - $accessToken2 = "ACCESS_TOKEN_2"; - $token = ""; - - $client = $this->getClient(); - - $postBody = $this->getMock('GuzzleHttp\Post\PostBodyInterface'); - $postBody->expects($this->exactly(3)) - ->method('replaceFields') - ->will($this->returnCallback( - function ($fields) use (&$token) { - $token = isset($fields['token']) ? $fields['token'] : null; - } - )); - $request = $this->getMock('GuzzleHttp\Message\RequestInterface'); - $request->expects($this->exactly(3)) - ->method('getBody') - ->will($this->returnValue($postBody)); - $response = $this->getMock('GuzzleHttp\Message\ResponseInterface'); - $response->expects($this->exactly(3)) - ->method('getStatusCode') - ->will($this->returnValue(200)); - $http = $this->getMock('GuzzleHttp\ClientInterface'); - $http->expects($this->exactly(3)) - ->method('send') - ->will($this->returnValue($response)); - $http->expects($this->exactly(3)) - ->method('createRequest') - ->will($this->returnValue($request)); - - // Test with access token. - $client->setHttpClient($http); - $oauth = new Google_Auth_OAuth2($client); - $oauth->setAccessToken( - json_encode( - array( - 'access_token' => $accessToken, - 'created' => time(), - 'expires_in' => '3600' - ) - ) - ); - $this->assertTrue($oauth->revokeToken()); - $this->assertEquals($accessToken, $token); - - // Test with refresh token. - $oauth->setAccessToken( - json_encode( - array( - 'access_token' => $accessToken, - 'refresh_token' => $refreshToken, - 'created' => time(), - 'expires_in' => '3600' - ) - ) - ); - $this->assertTrue($oauth->revokeToken()); - $this->assertEquals($refreshToken, $token); - - // Test with passed in token. - $this->assertTrue($oauth->revokeToken($accessToken2)); - $this->assertEquals($accessToken2, $token); - } - - public function testCreateAuthUrl() - { - $client = $this->getClient(); - $oauth = new Google_Auth_OAuth2($client); - - $client->setClientId('clientId1'); - $client->setClientSecret('clientSecret1'); - $client->setRedirectUri('http://localhost'); - $client->setDeveloperKey('devKey'); - $client->setAccessType('offline'); - $client->setApprovalPrompt('force'); - $client->setState('xyz'); - $client->setRequestVisibleActions(array('http://foo')); - $client->setLoginHint("bob@example.org"); - - $authUrl = $oauth->createAuthUrl("http://googleapis.com/scope/foo"); - $expected = "https://accounts.google.com/o/oauth2/auth" - . "?response_type=code" - . "&access_type=offline" - . "&scope=http%3A%2F%2Fgoogleapis.com%2Fscope%2Ffoo" - . "&approval_prompt=force" - . "&login_hint=bob%40example.org" - . "&state=xyz" - . "&client_id=clientId1" - . "&redirect_uri=http%3A%2F%2Flocalhost"; - $this->assertEquals($expected, $authUrl); - - // Again with a blank login hint (should remove all traces from authUrl) - $client->setLoginHint(""); - $client->setHostedDomain("example.com"); - $client->setOpenidRealm("example.com"); - $client->setPrompt("select_account"); - $client->setIncludeGrantedScopes(true); - $authUrl = $oauth->createAuthUrl("http://googleapis.com/scope/foo"); - $expected = "https://accounts.google.com/o/oauth2/auth" - . "?response_type=code" - . "&access_type=offline" - . "&scope=http%3A%2F%2Fgoogleapis.com%2Fscope%2Ffoo" - . "&prompt=select_account" - . "&hd=example.com" - . "&openid.realm=example.com" - . "&include_granted_scopes=true" - . "&state=xyz" - . "&client_id=clientId1" - . "&redirect_uri=http%3A%2F%2Flocalhost"; - $this->assertEquals($expected, $authUrl); - } - - /** - * Most of the logic for ID token validation is in AuthTest - - * this is just a general check to ensure we verify a valid - * id token if one exists. - */ - public function testValidateIdToken() - { - if (!$this->checkToken()) { - return; - } - - $client = $this->getClient(); - $token = json_decode($client->getAccessToken()); - $segments = explode(".", $token->id_token); - $this->assertEquals(3, count($segments)); - // Extract the client ID in this case as it wont be set on the test client. - $data = json_decode(JWT::urlSafeB64Decode($segments[1])); - $oauth = new Google_Auth_OAuth2($client); - $payload = $oauth->verifyIdToken($token->id_token, $data->aud); - $this->assertArrayHasKey('sub', $payload); - $this->assertTrue(strlen($payload['sub']) > 0); - - // TODO: Need to be smart about testing/disabling the - // caching for this test to make sense. Not sure how to do that - // at the moment. - $client = $this->getClient(); - $data = json_decode(JWT::urlSafeB64Decode($segments[1])); - $oauth = new Google_Auth_OAuth2($client); - $payload = $oauth->verifyIdToken($token->id_token, $data->aud); - $this->assertArrayHasKey('sub', $payload); - $this->assertTrue(strlen($payload['sub']) > 0); - } - - /** - * Test for revoking token when none is opened - */ - public function testRevokeWhenNoTokenExists() - { - $client = new Google_Client(); - $this->assertFalse($client->revokeToken()); - } - - /** - * Test that the ID token is properly refreshed. - */ - public function testRefreshTokenSetsValues() - { - $client = new Google_Client(); - $request = $this->getMock('GuzzleHttp\Message\RequestInterface'); - $request->expects($this->once()) - ->method('getBody') - ->will($this->returnValue($this->getMock('GuzzleHttp\Post\PostBodyInterface'))); - $response = $this->getMock('GuzzleHttp\Message\ResponseInterface'); - $response->expects($this->once()) - ->method('json') - ->will($this->returnValue(array( - 'access_token' => 'xyz', - 'id_token' => 'ID_TOKEN', - ))); - $response->expects($this->once()) - ->method('getBody') - ->will($this->returnValue($this->getMock('GuzzleHttp\Post\PostBody'))); - $http = $this->getMock('GuzzleHttp\ClientInterface'); - $http->expects($this->once()) - ->method('send') - ->will($this->returnValue($response)); - $http->expects($this->once()) - ->method('createRequest') - ->will($this->returnValue($request)); - $client->setHttpClient($http); - $oauth = new Google_Auth_OAuth2($client); - $oauth->refreshToken("REFRESH_TOKEN"); - $token = json_decode($oauth->getAccessToken(), true); - $this->assertEquals($token['id_token'], "ID_TOKEN"); - } -} diff --git a/tests/Google/CacheTest.php b/tests/Google/CacheTest.php index 0fc32e166..9d9b1bfb5 100644 --- a/tests/Google/CacheTest.php +++ b/tests/Google/CacheTest.php @@ -23,28 +23,16 @@ class Google_CacheTest extends BaseTest public function testFile() { $dir = sys_get_temp_dir() . '/google-api-php-client/tests'; - $client = $this->getClient(); - $client->setClassConfig( - 'Google_Cache_File', - 'directory', - $dir - ); - $cache = new Google_Cache_File($client); + $cache = new Google_Cache_File($dir); $cache->set('foo', 'bar'); $this->assertEquals($cache->get('foo'), 'bar'); $this->getSetDelete($cache); } - /** - * @requires extension Memcache - */ public function testNull() { - $client = $this->getClient(); - $cache = new Google_Cache_Null($client); - $client->setCache($cache); - + $cache = new Google_Cache_Null(); $cache->set('foo', 'bar'); $cache->delete('foo'); $this->assertEquals(false, $cache->get('foo')); @@ -65,12 +53,13 @@ public function testNull() */ public function testMemcache() { - $client = $this->getClient(); - if (!$client->getClassConfig('Google_Cache_Memcache', 'host')) { - $this->markTestSkipped('Test requires memcache host specified'); + $host = getenv('MEMCACHE_HOST') ? getenv('MEMCACHE_HOST') : null; + $port = getenv('MEMCACHE_PORT') ? getenv('MEMCACHE_PORT') : null; + if (!($host && $port)) { + $this->markTestSkipped('Test requires memcache host and port specified'); } - $cache = new Google_Cache_Memcache($client); + $cache = new Google_Cache_Memcache($host, $port); $this->getSetDelete($cache); } @@ -83,8 +72,7 @@ public function testAPC() if (!ini_get('apc.enable_cli')) { $this->markTestSkipped('Test requires APC enabled for CLI'); } - $client = $this->getClient(); - $cache = new Google_Cache_Apc($client); + $cache = new Google_Cache_Apc(); $this->getSetDelete($cache); } diff --git a/tests/Google/ClientTest.php b/tests/Google/ClientTest.php index 252914f1e..97941b7c6 100644 --- a/tests/Google/ClientTest.php +++ b/tests/Google/ClientTest.php @@ -18,38 +18,103 @@ * under the License. */ +use GuzzleHttp\Client; +use GuzzleHttp\Event\RequestEvents; use GuzzleHttp\Message\Request; class Google_ClientTest extends BaseTest { - public function testClient() + public function testClientConstructor() { - $client = new Google_Client(); - $client->setAccessType('foo'); - $client->setDeveloperKey('foo'); - $req = new Request('GET', 'http://foo.com'); - $client->getAuth()->sign($req); - $key = $req->getQuery()->get('key'); - $this->assertEquals('foo', $key); - - $client->setAccessToken(json_encode(array('access_token' => '1'))); - $this->assertEquals("{\"access_token\":\"1\"}", $client->getAccessToken()); + $this->assertInstanceOf('Google_Client', $this->getClient()); } - public function testClientConstructor() + public function testSignAppKey() { - $this->assertInstanceOf('Google_Client', $this->getClient()); + $client = $this->getClient(); + $client->setDeveloperKey('devKey'); + + $http = new Client(); + $client->attachAuthListener($http); + + $listeners = $http->getEmitter()->listeners('before'); + $this->assertEquals(1, count($listeners)); + $this->assertEquals(2, count($listeners[0])); + $this->assertInstanceOf('Google\Auth\Simple', $listeners[0][0]); } - /** - * @expectedException Google_Auth_Exception - */ - public function testPrepareInvalidScopes() + public function testSignAccessToken() + { + $client = $this->getClient(); + + $http = new Client(); + $client->setAccessToken([ + 'access_token' => 'test_token', + 'expires_in' => 3600, + 'created' => time(), + ]); + $client->setScopes('test_scope'); + $client->attachAuthListener($http); + + $listeners = $http->getEmitter()->listeners('before'); + $this->assertEquals(1, count($listeners)); + $this->assertEquals(2, count($listeners[0])); + $this->assertInstanceOf('Google\Auth\ScopedAccessToken', $listeners[0][0]); + } + + public function testCreateAuthUrl() + { + $client = $this->getClient(); + + $client->setClientId('clientId1'); + $client->setClientSecret('clientSecret1'); + $client->setRedirectUri('http://localhost'); + $client->setDeveloperKey('devKey'); + $client->setState('xyz'); + $client->setConfig('access_type', 'offline'); + $client->setConfig('approval_prompt', 'force'); + $client->setConfig('request_visible_actions', 'http://foo'); + $client->setConfig('login_hint', 'bob@example.org'); + + $authUrl = $client->createAuthUrl("http://googleapis.com/scope/foo"); + $expected = "https://accounts.google.com/o/oauth2/auth" + . "?access_type=offline" + . "&approval_prompt=force" + . "&login_hint=bob%40example.org" + . "&response_type=code" + . "&scope=http%3A%2F%2Fgoogleapis.com%2Fscope%2Ffoo" + . "&state=xyz" + . "&client_id=clientId1" + . "&redirect_uri=http%3A%2F%2Flocalhost"; + $this->assertEquals($expected, $authUrl); + + // Again with a blank login hint (should remove all traces from authUrl) + $client->setConfig('login_hint', ''); + $client->setConfig('hosted_domain', 'example.com'); + $client->setConfig('openid.realm', 'example.com'); + $client->setConfig('prompt', 'select_account'); + $client->setConfig('include_granted_scopes', true); + $authUrl = $client->createAuthUrl("http://googleapis.com/scope/foo"); + $expected = "https://accounts.google.com/o/oauth2/auth" + . "?access_type=offline" + . "&hd=example.com" + . "&include_granted_scopes=true" + . "&openid.realm=example.com" + . "&prompt=select_account" + . "&response_type=code" + . "&scope=http%3A%2F%2Fgoogleapis.com%2Fscope%2Ffoo" + . "&state=xyz" + . "&client_id=clientId1" + . "&redirect_uri=http%3A%2F%2Flocalhost"; + $this->assertEquals($expected, $authUrl); + } + + public function testPrepareNoScopes() { $client = new Google_Client(); $scopes = $client->prepareScopes(); - $this->assertEquals("", $scopes); + $this->assertEquals(null, $scopes); } public function testNoAuthIsNull() @@ -84,11 +149,11 @@ public function testPrepareService() $this->assertEquals("http://test.com scope2", $scopes); $this->assertEquals( '' - . 'https://accounts.google.com/o/oauth2/auth' - . '?response_type=code' - . '&access_type=online' - . '&scope=http%3A%2F%2Ftest.com%20scope2' + . 'https://accounts.google.com/o/oauth2/auth' + . '?access_type=online' . '&approval_prompt=auto' + . '&response_type=code' + . '&scope=http%3A%2F%2Ftest.com%20scope2' . '&state=xyz' . '&client_id=test1' . '&redirect_uri=http%3A%2F%2Flocalhost%2F', @@ -110,6 +175,9 @@ public function testPrepareService() $http->expects($this->once()) ->method('createRequest') ->will($this->returnValue($request)); + $http->expects($this->once()) + ->method('getEmitter') + ->will($this->returnValue($this->getMock('GuzzleHttp\Event\EmitterInterface'))); $http->expects($this->once()) ->method('send') ->will($this->returnValue($response)); @@ -124,25 +192,21 @@ public function testSettersGetters() $client->setClientId("client1"); $client->setClientSecret('client1secret'); $client->setState('1'); - $client->setApprovalPrompt('force'); - $client->setAccessType('offline'); + $client->setConfig('approval_prompt', 'force'); + $client->setConfig('access_type', 'offline'); $client->setRedirectUri('localhost'); - $client->setApplicationName('me'); - $this->assertEquals('object', gettype($client->getAuth())); + $client->setConfig('application_name', 'me'); $this->assertEquals('object', gettype($client->getCache())); - $client->setAuth(new Google_Auth_Simple($client)); - $client->setAuth(new Google_Auth_OAuth2($client)); - try { $client->setAccessToken(null); $this->fail('Should have thrown an Google_Auth_Exception.'); - } catch (Google_Auth_Exception $e) { - $this->assertEquals('Could not json decode the token', $e->getMessage()); + } catch (InvalidArgumentException $e) { + $this->assertEquals('invalid json token', $e->getMessage()); } - $token = json_encode(array('access_token' => 'token')); + $token = array('access_token' => 'token'); $client->setAccessToken($token); $this->assertEquals($token, $client->getAccessToken()); } @@ -161,8 +225,7 @@ public function testAppEngineAutoConfig() public function testJsonConfig() { // Device config - $config = new Google_Config(); - $client = new Google_Client($config); + $client = new Google_Client(); $device = '{"installed":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret"'. ':"N0aHCBT1qX1VAcF5J1pJAn6S","token_uri":"https://accounts.google.com/o/oauth2/token",'. @@ -171,14 +234,12 @@ public function testJsonConfig() '"https://www.googleapis.com/oauth2/v1/certs"}}'; $dObj = json_decode($device); $client->setAuthConfig($device); - $cfg = $config->getClassConfig('Google_Auth_OAuth2'); - $this->assertEquals($cfg['client_id'], $dObj->installed->client_id); - $this->assertEquals($cfg['client_secret'], $dObj->installed->client_secret); - $this->assertEquals($cfg['redirect_uri'], $dObj->installed->redirect_uris[0]); + $this->assertEquals($client->getClientId(), $dObj->installed->client_id); + $this->assertEquals($client->getClientSecret(), $dObj->installed->client_secret); + $this->assertEquals($client->getRedirectUri(), $dObj->installed->redirect_uris[0]); // Web config - $config = new Google_Config(); - $client = new Google_Client($config); + $client = new Google_Client(); $web = '{"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","client_secret"' . ':"lpoubuib8bj-Fmke_YhhyHGgXc","token_uri":"https://accounts.google.com/o/oauth2/token"' . ',"client_email":"123456789@developer.gserviceaccount.com","client_x509_cert_url":'. @@ -187,40 +248,41 @@ public function testJsonConfig() '"https://www.googleapis.com/oauth2/v1/certs"}}'; $wObj = json_decode($web); $client->setAuthConfig($web); - $cfg = $config->getClassConfig('Google_Auth_OAuth2'); - $this->assertEquals($cfg['client_id'], $wObj->web->client_id); - $this->assertEquals($cfg['client_secret'], $wObj->web->client_secret); - $this->assertEquals($cfg['redirect_uri'], ''); + $this->assertEquals($client->getClientId(), $wObj->web->client_id); + $this->assertEquals($client->getClientSecret(), $wObj->web->client_secret); + $this->assertEquals($client->getRedirectUri(), ''); } public function testIniConfig() { - $config = new Google_Config($this->testDir . "/config/test.ini"); - $this->assertEquals('My Test application', $config->getApplicationName()); + $config = parse_ini_file($this->testDir . "/config/test.ini"); + $client = new Google_Client($config); + + $this->assertEquals('My Test application', $client->getConfig('application_name')); $this->assertEquals( 'gjfiwnGinpena3', - $config->getClassConfig('Google_Auth_OAuth2', 'client_secret') - ); - $this->assertInternalType( - 'array', - $config->getClassConfig('Google_IO_Abstract') - ); - $this->assertEquals( - 100, - $config->getClassConfig('Google_IO_Abstract', 'request_timeout_seconds') + $client->getClientSecret() ); } public function testNoAuth() { /** @var $noAuth Google_Auth_Simple */ - $noAuth = new Google_Auth_Simple($this->getClient()); $client = new Google_Client(); - $client->setAuth($noAuth); $client->setDeveloperKey(null); - $req = new Request('GET', 'http://example.com'); - $resp = $noAuth->sign($req); - $this->assertEquals('http://example.com', $resp->getUrl()); + // unset application credentials + $GOOGLE_APPLICATION_CREDENTIALS = getenv('GOOGLE_APPLICATION_CREDENTIALS'); + $HOME = getenv('HOME'); + putenv('GOOGLE_APPLICATION_CREDENTIALS='); + putenv('HOME='.sys_get_temp_dir()); + $http = new Client(); + $client->attachAuthListener($http); + + $listeners = $http->getEmitter()->listeners('before'); + + putenv("GOOGLE_APPLICATION_CREDENTIALS=$GOOGLE_APPLICATION_CREDENTIALS"); + putenv("HOME=$HOME"); + $this->assertEquals(0, count($listeners)); } } diff --git a/tests/Google/Http/BatchTest.php b/tests/Google/Http/ParallelTest.php similarity index 63% rename from tests/Google/Http/BatchTest.php rename to tests/Google/Http/ParallelTest.php index c8aaa538d..a82d0689f 100644 --- a/tests/Google/Http/BatchTest.php +++ b/tests/Google/Http/ParallelTest.php @@ -18,25 +18,27 @@ * under the License. */ -class Google_Http_BatchTest extends BaseTest +class Google_Http_ParallelTest extends BaseTest { + public $parallel; public $plus; - public function testBatchRequestWithAuth() + public function setUp() { - if (!$this->checkToken()) { - return; - } + $this->checkToken(); $client = $this->getClient(); - $batch = new Google_Http_Batch($client); + $client->setUseBatch(true); + $this->parallel = new Google_Http_Parallel($client); $this->plus = new Google_Service_Plus($client); + } - $client->setUseBatch(true); - $batch->add($this->plus->people->get('me'), 'key1'); - $batch->add($this->plus->people->get('me'), 'key2'); - $batch->add($this->plus->people->get('me'), 'key3'); + public function testBatchRequestWithAuth() + { + $this->parallel->add($this->plus->people->get('me'), 'key1'); + $this->parallel->add($this->plus->people->get('me'), 'key2'); + $this->parallel->add($this->plus->people->get('me'), 'key3'); - $result = $batch->execute(); + $result = $this->parallel->execute(); $this->assertTrue(isset($result['response-key1'])); $this->assertTrue(isset($result['response-key2'])); $this->assertTrue(isset($result['response-key3'])); @@ -47,19 +49,11 @@ public function testBatchRequestWithAuth() public function testBatchRequest() { - if (!$this->checkToken()) { - return; - } - $client = $this->getClient(); - $batch = new Google_Http_Batch($client); - $this->plus = new Google_Service_Plus($client); - - $client->setUseBatch(true); - $batch->add($this->plus->people->get('+LarryPage'), 'key1'); - $batch->add($this->plus->people->get('+LarryPage'), 'key2'); - $batch->add($this->plus->people->get('+LarryPage'), 'key3'); + $this->parallel->add($this->plus->people->get('+LarryPage'), 'key1'); + $this->parallel->add($this->plus->people->get('+LarryPage'), 'key2'); + $this->parallel->add($this->plus->people->get('+LarryPage'), 'key3'); - $result = $batch->execute(); + $result = $this->parallel->execute(); $this->assertTrue(isset($result['response-key1'])); $this->assertTrue(isset($result['response-key2'])); $this->assertTrue(isset($result['response-key3'])); @@ -70,19 +64,15 @@ public function testBatchRequest() public function testInvalidBatchRequest() { - $client = $this->getClient(); - $batch = new Google_Http_Batch($client); - $this->plus = new Google_Service_Plus($client); - - $client->setUseBatch(true); - $batch->add($this->plus->people->get('123456789987654321'), 'key1'); - $batch->add($this->plus->people->get('+LarryPage'), 'key2'); + $this->parallel->add($this->plus->people->get('123456789987654321'), 'key1'); + $this->parallel->add($this->plus->people->get('+LarryPage'), 'key2'); - $result = $batch->execute(); + $result = $this->parallel->execute(); $this->assertTrue(isset($result['response-key2'])); $this->assertInstanceOf( - 'GuzzleHttp\Exception\ClientException', + 'GuzzleHttp\Message\Response', $result['response-key1'] ); + $this->assertEquals(404, $result['response-key1']->getStatusCode()); } } diff --git a/tests/Google/LoggerTest.php b/tests/Google/LoggerTest.php deleted file mode 100644 index 255616a60..000000000 --- a/tests/Google/LoggerTest.php +++ /dev/null @@ -1,445 +0,0 @@ -client = new Google_Client(); - } - - /** - * @dataProvider logLevelsProvider - */ - public function testPsrMethods($key) - { - $message = 'This is my log message'; - $context = array('some'=>'context'); - - $logger = $this->getLogger('log'); - $logger->expects($this->once()) - ->method('log') - ->with($key, $message, $context); - - call_user_func(array($logger, $key), $message, $context); - } - - /** - * @dataProvider invalidLevelsProvider - * @expectedException Google_Logger_Exception - * @expectedExceptionMessage Unknown LogLevel - */ - public function testSetLabelWithBadValue($level) - { - $this->getLogger()->setLevel($level); - } - - /** - * @dataProvider invalidLevelsProvider - * @expectedException Google_Logger_Exception - * @expectedExceptionMessage Unknown LogLevel - */ - public function testSetLabelWithBadValueFromConfig($level) - { - $this->client->setClassConfig('Google_Logger_Abstract', 'level', $level); - $this->getLogger(); - } - - /** - * @dataProvider filterProvider - */ - public function testShouldHandle($setLevel, $handleLevel, $expected) - { - $logger = $this->getLogger(); - $logger->setLevel($setLevel); - - $this->assertEquals($expected, $logger->shouldHandle($handleLevel)); - } - - /** - * @dataProvider filterProvider - */ - public function testShouldHandleFromConfig($config, $handleLevel, $expected) - { - $this->client->setClassConfig('Google_Logger_Abstract', 'level', $config); - $logger = $this->getLogger(); - - $this->assertEquals($expected, $logger->shouldHandle($handleLevel)); - } - - /** - * @dataProvider filterProvider - */ - public function testShouldWrite($setLevel, $logLevel, $expected) - { - $logger = $this->getLogger(); - $logger->expects($expected ? $this->once() : $this->never()) - ->method('write'); - - $logger->setLevel($setLevel); - $logger->log($logLevel, 'This is my log message'); - } - - /** - * @dataProvider filterProvider - */ - public function testShouldWriteFromConfig($config, $logLevel, $expected) - { - $this->client->setClassConfig('Google_Logger_Abstract', 'level', $config); - $logger = $this->getLogger(); - $logger->expects($expected ? $this->once() : $this->never()) - ->method('write'); - - $logger->log($logLevel, 'This is my log message'); - } - - /** - * @dataProvider formattingProvider - */ - public function testMessageFormatting( - $format, - $date_format, - $newlines, - $message, - $context, - $expected - ) { - $this->client->setClassConfig( - 'Google_Logger_Abstract', - 'log_format', - $format - ); - $this->client->setClassConfig( - 'Google_Logger_Abstract', - 'date_format', - $date_format - ); - $this->client->setClassConfig( - 'Google_Logger_Abstract', - 'allow_newlines', - $newlines - ); - - $logger = $this->getLogger(); - $logger->expects($this->once()) - ->method('write') - ->with($expected); - - $logger->log('debug', $message, $context); - } - - public function testNullLoggerNeverWrites() - { - $logger = $this->getLogger('write', 'Google_Logger_Null'); - $logger->expects($this->never()) - ->method('write'); - - $logger->log( - 'emergency', - 'Should not be written', - array('same' => 'for this') - ); - } - - public function testNullLoggerNeverHandles() - { - $logger = $this->getLogger('write', 'Google_Logger_Null'); - $this->assertFalse($logger->shouldHandle('emergency')); - $this->assertFalse($logger->shouldHandle(600)); - } - - public function testPsrNeverWrites() - { - $logger = $this->getLogger('write', 'Google_Logger_Psr'); - $logger->expects($this->never()) - ->method('write'); - - $logger->log( - 'emergency', - 'Should not be written', - array('same' => 'for this') - ); - - $logger->setLogger($this->getLogger()); - - $logger->log( - 'emergency', - 'Should not be written', - array('same' => 'for this') - ); - } - - public function testPsrNeverShouldHandleWhenNoLoggerSet() - { - $logger = $this->getLogger(null, 'Google_Logger_Psr'); - $this->assertFalse($logger->shouldHandle('emergency')); - $this->assertFalse($logger->shouldHandle(600)); - } - - public function testPsrShouldHandleWhenLoggerSet() - { - $logger = $this->getLogger(null, 'Google_Logger_Psr'); - $logger->setLogger($this->getLogger()); - - $this->assertTrue($logger->shouldHandle('emergency')); - $this->assertTrue($logger->shouldHandle(600)); - } - - /** - * @dataProvider logLevelsProvider - */ - public function testPsrDelegates($key) - { - $message = 'This is my log message'; - $context = array('some'=>'context'); - - $delegate = $this->getLogger('log'); - $delegate->expects($this->once()) - ->method('log') - ->with($key, $message, $context); - - $logger = $this->getLogger(null, 'Google_Logger_Psr'); - $logger->setLogger($delegate); - - call_user_func(array($logger, $key), $message, $context); - } - - /** - * @expectedException Google_Logger_Exception - * @expectedExceptionMessage File logger requires a filename or a valid file pointer - */ - public function testLoggerWithBadFileType() - { - $this->client->setClassConfig('Google_Logger_File', 'file', false); - $logger = $this->getLogger(null, 'Google_Logger_File'); - } - - /** - * @expectedException Google_Logger_Exception - * @expectedExceptionMessage Logger Error - */ - public function testLoggerWithBadFileValue() - { - $this->client->setClassConfig('Google_Logger_File', 'file', 'not://exist'); - - $logger = $this->getLogger(null, 'Google_Logger_File'); - $logger->log('emergency', 'will fail'); - } - - /** - * @expectedException Google_Logger_Exception - * @expectedExceptionMessage File pointer is no longer available - */ - public function testLoggerWithClosedFileReference() - { - $fp = fopen('php://memory', 'r+'); - - $this->client->setClassConfig('Google_Logger_File', 'file', $fp); - $logger = $this->getLogger(null, 'Google_Logger_File'); - - fclose($fp); - - $logger->log('emergency', 'will fail'); - } - - public function testLoggerWithFileReference() - { - $fp = fopen('php://memory', 'r+'); - - $this->client->setClassConfig('Google_Logger_File', 'file', $fp); - $this->client->setClassConfig( - 'Google_Logger_Abstract', - 'log_format', - "%level% - %message%\n" - ); - - $logger = $this->getLogger(null, 'Google_Logger_File'); - $logger->log('emergency', 'test one'); - $logger->log('alert', 'test two'); - $logger->log(500, 'test three'); - - rewind($fp); - $this->assertEquals( - "EMERGENCY - test one\nALERT - test two\nCRITICAL - test three\n", - stream_get_contents($fp) - ); - - fclose($fp); - } - - public function testLoggerWithFile() - { - $this->expectOutputString( - "EMERGENCY - test one\nALERT - test two\nCRITICAL - test three\n" - ); - - $this->client->setClassConfig( - 'Google_Logger_File', - 'file', - 'php://output' - ); - $this->client->setClassConfig( - 'Google_Logger_Abstract', - 'log_format', - "%level% - %message%\n" - ); - - $logger = $this->getLogger(null, 'Google_Logger_File'); - $logger->log('emergency', 'test one'); - $logger->log('alert', 'test two'); - $logger->log(500, 'test three'); - } - - public function formattingProvider() - { - return array( - 'no interpolation' => array( - 'this is my format', - 'd/M/Y:H:i:s O', - false, - 'you wont see this', - array('or' => 'this'), - 'this is my format', - ), - 'only message interpolation' => array( - 'my format: %message%', - 'd/M/Y:H:i:s O', - false, - 'you will see this', - array('but not' => 'this'), - 'my format: you will see this', - ), - 'message and level interpolation' => array( - '%level% - my format: %message%', - 'd/M/Y:H:i:s O', - false, - 'you will see this', - array('but not' => 'this'), - 'DEBUG - my format: you will see this', - ), - 'message, level, datetime interpolation' => array( - '[%datetime%] %level% - my format: %message%', - '\T\I\M\E', - false, - 'you will see this', - array('but not' => 'this'), - '[TIME] DEBUG - my format: you will see this', - ), - 'message, level, datetime, context interpolation' => array( - '[%datetime%] %level% - my format: %message%: %context%', - '\T\I\M\E', - false, - 'you will see this', - array('and' => 'this'), - '[TIME] DEBUG - my format: you will see this: {"and":"this"}', - ), - 'reverse JSON in context' => array( - '%context%', - 'd/M/Y:H:i:s O', - false, - 'you will not see this', - array('reverse' => json_encode(array('this' => 'is cool'))), - '{"reverse":{"this":"is cool"}}', - ), - 'remove newlines in message' => array( - '%message%', - 'd/M/Y:H:i:s O', - false, - "This \n\n\r\n is \r my \r\n newlines \r\r\r\n\n message", - array('you wont' => 'see this'), - 'This is my newlines message', - ), - 'allow newlines in message' => array( - '%message%', - 'd/M/Y:H:i:s O', - true, - "This \n\n\r\n is \r my \r\n newlines \r\r\r\n\n message", - array('you wont' => 'see this'), - "This \n\n\r\n is \r my \r\n newlines \r\r\r\n\n message", - ), - 'allow newlines in JSON' => array( - '%context%', - 'd/M/Y:H:i:s O', - true, - "wont see this", - array('you will' => 'see this'), - version_compare(PHP_VERSION, '5.4.0', '>=') ? - "{\n \"you will\": \"see this\"\n}" : - '{"you will":"see this"}' - ), - ); - } - - public function filterProvider() - { - return array( - array('debug', 'debug', true), - array(100, 'debug', true), - array('info', 'debug', false), - array('info', 'notice', true), - array('notice', 'notice', true), - array(250, 'notice', true), - array('notice', 'debug', false), - array(250, 'alert', true), - array('error', 'error', true), - array('error', 'warning', false), - array('error', 'critical', true), - array(600, 'alert', false), - array(600, 'critical', false), - array(600, 'emergency', true), - array('emergency', 'emergency', true), - ); - } - - public function invalidLevelsProvider() - { - return array( - array('100'), - array('DEBUG'), - array(100.0), - array(''), - array(0), - ); - } - - public function logLevelsProvider() - { - return array( - array('emergency', 600), - array('alert', 550), - array('critical', 500), - array('error', 400), - array('warning', 300), - array('notice', 250), - array('info', 200), - array('debug', 100), - ); - } - - private function getLogger($methods = null, $type = 'Google_Logger_Abstract') - { - return $this->getMockBuilder($type) - ->setMethods((array) $methods) - ->setConstructorArgs(array($this->client)) - ->getMockForAbstractClass(); - } -} diff --git a/tests/Google/Service/AdSenseTest.php b/tests/Google/Service/AdSenseTest.php index 3d04937f6..14dbb3b1e 100644 --- a/tests/Google/Service/AdSenseTest.php +++ b/tests/Google/Service/AdSenseTest.php @@ -18,17 +18,14 @@ class Google_Service_AdSenseTest extends BaseTest { public $adsense; - public function __construct() + public function setUp() { - parent::__construct(); + $this->checkToken(); $this->adsense = new Google_Service_AdSense($this->getClient()); } public function testAccountsList() { - if (!$this->checkToken()) { - return; - } $accounts = $this->adsense->accounts->listAccounts(); $this->assertArrayHasKey('kind', $accounts); $this->assertEquals($accounts['kind'], 'adsense#accounts'); @@ -251,9 +248,6 @@ public function testAccountsCustomChannelsAdUnitsList() public function testAdClientsList() { - if (!$this->checkToken()) { - return; - } $adClients = $this->adsense->adclients->listAdclients(); $this->checkAdClientsCollection($adClients); } diff --git a/tests/Google/Service/PlusTest.php b/tests/Google/Service/PlusTest.php index cb132afdc..edd012f52 100644 --- a/tests/Google/Service/PlusTest.php +++ b/tests/Google/Service/PlusTest.php @@ -19,15 +19,14 @@ class Google_Service_PlusTest extends BaseTest { /** @var Google_PlusService */ public $plus; - public function __construct() + public function setUp() { - parent::__construct(); + $this->checkToken(); $this->plus = new Google_Service_Plus($this->getClient()); } public function testGetPerson() { - $this->checkToken(); $person = $this->plus->people->get("118051310819094153327"); $this->assertArrayHasKey('kind', $person); $this->assertArrayHasKey('displayName', $person); @@ -37,7 +36,6 @@ public function testGetPerson() public function testListActivities() { - $this->checkToken(); $activities = $this->plus->activities ->listActivities("118051310819094153327", "public"); diff --git a/tests/Google/Service/ResourceTest.php b/tests/Google/Service/ResourceTest.php index 9312093ac..0f3fa759c 100644 --- a/tests/Google/Service/ResourceTest.php +++ b/tests/Google/Service/ResourceTest.php @@ -41,31 +41,15 @@ public function setUp() $this->client = $this->getMockBuilder("Google_Client") ->disableOriginalConstructor() ->getMock(); - $this->logger = $this->getMockBuilder("Google_Logger_Null") + $this->logger = $this->getMockBuilder("Monolog\Logger") ->disableOriginalConstructor() ->getMock(); - $this->client->expects($this->any()) - ->method("getClassConfig") - ->will($this->returnCallback(function($class, $type) { - if (!is_string($class)) { - $class = get_class($class); - } - $configMap = array( - "Google_Auth_Simple" => array( - "developer_key" => "testKey" - ), - ); - return isset($configMap[$class][$type]) ? $configMap[$class][$type] : null; - })); $this->client->expects($this->any()) ->method("getLogger") ->will($this->returnValue($this->logger)); $this->client->expects($this->any()) ->method("shouldDefer") ->will($this->returnValue(true)); - $this->client->expects($this->any()) - ->method("getAuth") - ->will($this->returnValue(new Google_Auth_Simple($this->client))); $this->client->expects($this->any()) ->method("getHttpClient") ->will($this->returnValue(new GuzzleHttp\Client([ @@ -97,7 +81,7 @@ public function testCallFailure() $resource->call("someothermethod", array()); } - public function testCallSimple() + public function testCall() { $resource = new Google_Service_Resource( $this->service, @@ -114,7 +98,7 @@ public function testCallSimple() ) ); $request = $resource->call("testMethod", array(array())); - $this->assertEquals("https://test.example.com/method/path?key=testKey", $request->getUrl()); + $this->assertEquals("https://test.example.com/method/path", $request->getUrl()); $this->assertEquals("POST", $request->getMethod()); } @@ -136,7 +120,7 @@ public function testCallServiceDefinedRoot() ) ); $request = $resource->call("testMethod", array(array())); - $this->assertEquals("https://sample.example.com/method/path?key=testKey", $request->getUrl()); + $this->assertEquals("https://sample.example.com/method/path", $request->getUrl()); $this->assertEquals("POST", $request->getMethod()); } diff --git a/tests/Google/Service/TasksTest.php b/tests/Google/Service/TasksTest.php index 8f98586d1..26e8a8fb6 100644 --- a/tests/Google/Service/TasksTest.php +++ b/tests/Google/Service/TasksTest.php @@ -20,17 +20,14 @@ class Google_Service_TasksTest extends BaseTest /** @var Google_TasksService */ public $taskService; - public function __construct() + public function setUp() { - parent::__construct(); + $this->checkToken(); $this->taskService = new Google_Service_Tasks($this->getClient()); } public function testInsertTask() { - if (!$this->checkToken()) { - return; - } $list = $this->createTaskList('List: ' . __METHOD__); $task = $this->createTask('Task: '.__METHOD__, $list->id); $this->assertIsTask($task); diff --git a/tests/Google/Service/YouTubeTest.php b/tests/Google/Service/YouTubeTest.php index 657760aa3..b7745f8ae 100644 --- a/tests/Google/Service/YouTubeTest.php +++ b/tests/Google/Service/YouTubeTest.php @@ -17,26 +17,22 @@ class Google_Service_YouTubeTest extends BaseTest { - /** @var Google_PlusService */ - public $plus; - public function __construct() + /** @var Google_Service_YouTube */ + public $youtube; + public function setUp() { - parent::__construct(); + $this->checkToken(); $this->youtube = new Google_Service_YouTube($this->getClient()); } public function testMissingFieldsAreNull() { - if (!$this->checkToken()) { - return; - } - $parts = "id,brandingSettings"; $opts = array("mine" => true); $channels = $this->youtube->channels->listChannels($parts, $opts); $newChannel = new Google_Service_YouTube_Channel(); - $newChannel->setId($channels[0]->getId()); + $newChannel->setId( $channels[0]->getId()); $newChannel->setBrandingSettings($channels[0]->getBrandingSettings()); $simpleOriginal = $channels[0]->toSimpleObject(); diff --git a/tests/Google/Task/RunnerTest.php b/tests/Google/Task/RunnerTest.php index 359e916a9..1dc73c9fd 100644 --- a/tests/Google/Task/RunnerTest.php +++ b/tests/Google/Task/RunnerTest.php @@ -26,9 +26,16 @@ class Google_Task_RunnerTest extends PHPUnit_Framework_TestCase private $mockedCallsCount = 0; private $currentMockedCall = 0; private $mockedCalls = array(); + private $nextRetryMap; + private static $retryConfig; protected function setUp() { + // hack to reset static Google_Client::$retryConfig each test + if (!self::$retryConfig) { + self::$retryConfig = Google_Client::$retryConfig; + } + Google_Client::$retryConfig = self::$retryConfig; $this->client = new Google_Client(); } @@ -99,10 +106,7 @@ public function testCustomRestRetryMapReplacesDefaults( $errorCode, $errorBody = '{}' ) { - $this->client->setClassConfig( - 'Google_Service_Exception', - array('retry_map' => array()) - ); + $this->setRetryMap(array()); $this->setTaskConfig(array('retries' => 5)); $this->setNextResponse($errorCode, $errorBody)->makeRequest(); @@ -110,9 +114,8 @@ public function testCustomRestRetryMapReplacesDefaults( public function testCustomRestRetryMapAddsNewHandlers() { - $this->client->setClassConfig( - 'Google_Service_Exception', - array('retry_map' => array('403' => Google_Config::TASK_RETRY_ALWAYS)) + $this->setRetryMap( + array('403' => Google_Task_Runner::TASK_RETRY_ALWAYS) ); $this->setTaskConfig(array('retries' => 5)); @@ -129,9 +132,8 @@ public function testCustomRestRetryMapAddsNewHandlers() */ public function testCustomRestRetryMapWithCustomLimits($limit) { - $this->client->setClassConfig( - 'Google_Service_Exception', - array('retry_map' => array('403' => $limit)) + $this->setRetryMap( + array('403' => $limit) ); $this->setTaskConfig(array('retries' => 5)); @@ -227,10 +229,7 @@ public function testCustomCurlRetryMapReplacesDefaults( $errorCode, $errorMessage = '' ) { - $this->client->setClassConfig( - 'Google_Service_Exception', - array('retry_map' => array()) - ); + $this->setRetryMap(array()); $this->setTaskConfig(array('retries' => 5)); $this->setNextResponseThrows($errorMessage, $errorCode)->makeRequest(); @@ -241,11 +240,8 @@ public function testCustomCurlRetryMapReplacesDefaults( */ public function testCustomCurlRetryMapAddsNewHandlers() { - $this->client->setClassConfig( - 'Google_Service_Exception', - array('retry_map' => array( - CURLE_COULDNT_RESOLVE_PROXY => Google_Config::TASK_RETRY_ALWAYS - )) + $this->setRetryMap( + array(CURLE_COULDNT_RESOLVE_PROXY => Google_Task_Runner::TASK_RETRY_ALWAYS) ); $this->setTaskConfig(array('retries' => 5)); @@ -263,11 +259,8 @@ public function testCustomCurlRetryMapAddsNewHandlers() */ public function testCustomCurlRetryMapWithCustomLimits($limit) { - $this->client->setClassConfig( - 'Google_Service_Exception', - array('retry_map' => array( - CURLE_COULDNT_RESOLVE_PROXY => $limit - )) + $this->setRetryMap( + array(CURLE_COULDNT_RESOLVE_PROXY => $limit) ); $this->setTaskConfig(array('retries' => 5)); @@ -301,7 +294,7 @@ public function testBadTaskConfig($config, $message) $this->setTaskConfig($config); new Google_Task_Runner( - $this->client->getClassConfig('Google_Task_Runner'), + Google_Client::$retryConfig, '', array($this, 'testBadTaskConfig') ); @@ -313,7 +306,7 @@ public function testBadTaskConfig($config, $message) */ public function testBadTaskCallback() { - $config = $this->client->getClassConfig('Google_Task_Runner'); + $config = []; new Google_Task_Runner($config, '', 5); } @@ -322,7 +315,7 @@ public function testBadTaskCallback() */ public function testTaskRetryOffByDefault() { - $this->setNextTaskAllowedRetries(Google_Config::TASK_RETRY_ALWAYS) + $this->setNextTaskAllowedRetries(Google_Task_Runner::TASK_RETRY_ALWAYS) ->runTask(); } @@ -332,7 +325,7 @@ public function testTaskRetryOffByDefault() public function testOneTaskRetryWithError() { $this->setTaskConfig(array('retries' => 1)); - $this->setNextTasksAllowedRetries(2, Google_Config::TASK_RETRY_ALWAYS) + $this->setNextTasksAllowedRetries(2, Google_Task_Runner::TASK_RETRY_ALWAYS) ->runTask(); } @@ -342,14 +335,14 @@ public function testOneTaskRetryWithError() public function testMultipleTaskRetriesWithErrors() { $this->setTaskConfig(array('retries' => 5)); - $this->setNextTasksAllowedRetries(6, Google_Config::TASK_RETRY_ALWAYS) + $this->setNextTasksAllowedRetries(6, Google_Task_Runner::TASK_RETRY_ALWAYS) ->runTask(); } public function testOneTaskRetryWithSuccess() { $this->setTaskConfig(array('retries' => 1)); - $result = $this->setNextTaskAllowedRetries(Google_Config::TASK_RETRY_ALWAYS) + $result = $this->setNextTaskAllowedRetries(Google_Task_Runner::TASK_RETRY_ALWAYS) ->setNextTaskReturnValue('success') ->runTask(); @@ -359,7 +352,7 @@ public function testOneTaskRetryWithSuccess() public function testMultipleTaskRetriesWithSuccess() { $this->setTaskConfig(array('retries' => 5)); - $result = $this->setNextTasksAllowedRetries(2, Google_Config::TASK_RETRY_ALWAYS) + $result = $this->setNextTasksAllowedRetries(2, Google_Task_Runner::TASK_RETRY_ALWAYS) ->setNextTaskReturnValue('success') ->runTask(); @@ -396,8 +389,8 @@ public function testTaskTimeouts($config, $minTime) public function testTaskWithManualRetries() { $this->setTaskConfig(array('retries' => 2)); - $this->setNextTasksAllowedRetries(2, Google_Config::TASK_RETRY_ALWAYS); - $config = $this->client->getClassConfig('Google_Task_Runner'); + $this->setNextTasksAllowedRetries(2, Google_Task_Runner::TASK_RETRY_ALWAYS); + $config = Google_Client::$retryConfig; $task = new Google_Task_Runner( $config, @@ -405,16 +398,16 @@ public function testTaskWithManualRetries() array($this, 'runNextTask') ); - $this->assertTrue($task->canAttmpt()); + $this->assertTrue($task->canAttempt()); $this->assertTrue($task->attempt()); - $this->assertTrue($task->canAttmpt()); + $this->assertTrue($task->canAttempt()); $this->assertTrue($task->attempt()); - $this->assertTrue($task->canAttmpt()); + $this->assertTrue($task->canAttempt()); $this->assertTrue($task->attempt()); - $this->assertFalse($task->canAttmpt()); + $this->assertFalse($task->canAttempt()); $this->assertFalse($task->attempt()); } @@ -444,8 +437,8 @@ public function timeoutProvider() public function customLimitsProvider() { return array( - array(Google_Config::TASK_RETRY_NEVER), - array(Google_Config::TASK_RETRY_ONCE), + array(Google_Task_Runner::TASK_RETRY_NEVER), + array(Google_Task_Runner::TASK_RETRY_ONCE), ); } @@ -539,7 +532,12 @@ private function setTaskConfig(array $config) 'jitter' => .5, 'retries' => 1 ); - $this->client->setClassConfig('Google_Task_Runner', $config); + Google_Client::$retryConfig = $config; + } + + private function setRetryMap(array $retryMap) + { + $this->nextRetryMap = $retryMap; } /** @@ -616,13 +614,11 @@ private function setNextResponsesThrow($count, $message, $code) */ private function setNextResponseThrows($message, $code) { - $map = $this->client->getClassConfig('Google_Service_Exception', 'retry_map'); $this->mockedCalls[$this->mockedCallsCount++] = new Google_Service_Exception( $message, $code, null, - array(), - $map + array() ); return $this; @@ -642,9 +638,10 @@ private function makeRequest() ->method('send') ->will($this->returnCallback(array($this, 'getNextMockedCall'))); - $config = $this->client->getClassConfig('Google_Task_Runner'); - $retryMap = $this->client->getClassConfig('Google_Service_Exception', 'retry_map'); + $config = Google_Client::$retryConfig; + $retryMap = $this->nextRetryMap; + $this->nextRetryMap = null; return Google_Http_REST::execute($http, $request, $config, $retryMap); } @@ -719,17 +716,21 @@ private function setNextTasksAllowedRetries($count, $allowedRetries) */ private function runTask() { - $config = $this->client->getClassConfig('Google_Task_Runner'); + $config = Google_Client::$retryConfig; $task = new Google_Task_Runner( $config, '', array($this, 'runNextTask') ); + if (null !== $this->nextRetryMap) { + $task->setRetryMap($this->nextRetryMap); + $this->nextRetryMap = null; + } + $exception = $this->getMockBuilder('Google_Service_Exception') - ->disableOriginalConstructor() - ->setMethods(array('allowedRetries')) - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); $exceptionCount = 0; $exceptionCalls = array(); @@ -740,13 +741,7 @@ private function runTask() } } - $exception->expects($this->exactly($exceptionCount)) - ->method('allowedRetries') - ->will( - new PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls( - $exceptionCalls - ) - ); + $task->setRetryMap($exceptionCalls); return $task->run(); } diff --git a/tests/Google/Utils/URITemplateTest b/tests/Google/Utils/URITemplateTest.php similarity index 100% rename from tests/Google/Utils/URITemplateTest rename to tests/Google/Utils/URITemplateTest.php diff --git a/tests/OAuthHelper.php b/tests/OAuthHelper.php deleted file mode 100644 index 944c47c8c..000000000 --- a/tests/OAuthHelper.php +++ /dev/null @@ -1,50 +0,0 @@ -setScopes( - array( - "https://www.googleapis.com/auth/plus.me", - "https://www.googleapis.com/auth/urlshortener", - "https://www.googleapis.com/auth/tasks", - "https://www.googleapis.com/auth/adsense", - "https://www.googleapis.com/auth/youtube" - ) -); -$client->setRedirectUri("urn:ietf:wg:oauth:2.0:oob"); -// Visit https://code.google.com/apis/console to -// generate your oauth2_client_id, oauth2_client_secret, and to -// register your oauth2_redirect_uri. -$clientId = getenv('GCLOUD_CLIENT_ID') ? getenv('GCLOUD_CLIENT_ID') : null; -$clientSecret = getenv('GCLOUD_CLIENT_SECRET') ? getenv('GCLOUD_CLIENT_SECRET') : null; -if (!($clientId && $clientSecret)) { - die("fetching a token requires GCLOUD_CLIENT_ID and GCLOUD_CLIENT_SECRET to be set\n"); -} -$client->setClientId($clientId); -$client->setClientSecret($clientSecret); -$authUrl = $client->createAuthUrl(); - -`open '$authUrl'`; -echo "\nPlease enter the auth code:\n"; -$authCode = trim(fgets(STDIN)); - -$accessToken = $client->authenticate($authCode); -$file = dirname(__FILE__) . DIRECTORY_SEPARATOR . '.accessToken'; -file_put_contents($file, $accessToken); - -echo "successfully loaded access token\n"; \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 327b974f9..95fdcb79b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -15,7 +15,7 @@ * limitations under the License. */ -require_once dirname(__FILE__) . '/../src/Google/autoload.php'; -require_once dirname(__FILE__) . '/BaseTest.php'; +require_once __DIR__ . '/../vendor/autoload.php'; +require_once __DIR__ . '/BaseTest.php'; date_default_timezone_set('UTC'); diff --git a/tests/clearToken.php b/tests/clearToken.php new file mode 100644 index 000000000..1990ec84a --- /dev/null +++ b/tests/clearToken.php @@ -0,0 +1,8 @@ +getCache()->get('access_token')); +$test->getCache()->delete('access_token'); + +echo "SUCCESS\n"; \ No newline at end of file diff --git a/tests/config/test.ini b/tests/config/test.ini index 1f55277d4..61be46410 100644 --- a/tests/config/test.ini +++ b/tests/config/test.ini @@ -1,7 +1,6 @@ ; Test.ini file application_name = My Test application auth_class = Google_Auth_OAuth2 -[classes] -Google_Auth_OAuth2[client_id] = 12345.apps.googleusercontent.com -Google_Auth_OAuth2[client_secret] = gjfiwnGinpena3 -Google_Auth_OAuth2[redirect_uri] = http://example.com \ No newline at end of file +client_id = 12345.apps.googleusercontent.com +client_secret = gjfiwnGinpena3 +redirect_uri = http://example.com \ No newline at end of file