Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 402] [Versions 39 and 403]

   1  <?php
   2  /*
   3   * Copyright 2010 Google Inc.
   4   *
   5   * Licensed under the Apache License, Version 2.0 (the "License");
   6   * you may not use this file except in compliance with the License.
   7   * You may obtain a copy of the License at
   8   *
   9   *     http://www.apache.org/licenses/LICENSE-2.0
  10   *
  11   * Unless required by applicable law or agreed to in writing, software
  12   * distributed under the License is distributed on an "AS IS" BASIS,
  13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14   * See the License for the specific language governing permissions and
  15   * limitations under the License.
  16   */
  17  
  18  if (!class_exists('Google_Client')) {
  19    require_once dirname(__FILE__) . '/autoload.php';
  20  }
  21  
  22  /**
  23   * The Google API Client
  24   * https://github.com/google/google-api-php-client
  25   */
  26  class Google_Client
  27  {
  28    const LIBVER = "1.1.5";
  29    const USER_AGENT_SUFFIX = "google-api-php-client/";
  30    /**
  31     * @var Google_Auth_Abstract $auth
  32     */
  33    private $auth;
  34  
  35    /**
  36     * @var Google_IO_Abstract $io
  37     */
  38    private $io;
  39  
  40    /**
  41     * @var Google_Cache_Abstract $cache
  42     */
  43    private $cache;
  44  
  45    /**
  46     * @var Google_Config $config
  47     */
  48    private $config;
  49  
  50    /**
  51     * @var Google_Logger_Abstract $logger
  52     */
  53    private $logger;
  54  
  55    /**
  56     * @var boolean $deferExecution
  57     */
  58    private $deferExecution = false;
  59  
  60    /** @var array $scopes */
  61    // Scopes requested by the client
  62    protected $requestedScopes = array();
  63  
  64    // definitions of services that are discovered.
  65    protected $services = array();
  66  
  67    // Used to track authenticated state, can't discover services after doing authenticate()
  68    private $authenticated = false;
  69  
  70    /**
  71     * Construct the Google Client.
  72     *
  73     * @param $config Google_Config or string for the ini file to load
  74     */
  75    public function __construct($config = null)
  76    {
  77      if (is_string($config) && strlen($config)) {
  78        $config = new Google_Config($config);
  79      } else if ( !($config instanceof Google_Config)) {
  80        $config = new Google_Config();
  81  
  82        if ($this->isAppEngine()) {
  83          // Automatically use Memcache if we're in AppEngine.
  84          $config->setCacheClass('Google_Cache_Memcache');
  85        }
  86  
  87        if (version_compare(phpversion(), "5.3.4", "<=") || $this->isAppEngine()) {
  88          // Automatically disable compress.zlib, as currently unsupported.
  89          $config->setClassConfig('Google_Http_Request', 'disable_gzip', true);
  90        }
  91      }
  92  
  93      if ($config->getIoClass() == Google_Config::USE_AUTO_IO_SELECTION) {
  94        if (function_exists('curl_version') && function_exists('curl_exec')
  95            && !$this->isAppEngine()) {
  96          $config->setIoClass("Google_IO_Curl");
  97        } else {
  98          $config->setIoClass("Google_IO_Stream");
  99        }
 100      }
 101  
 102      $this->config = $config;
 103    }
 104  
 105    /**
 106     * Get a string containing the version of the library.
 107     *
 108     * @return string
 109     */
 110    public function getLibraryVersion()
 111    {
 112      return self::LIBVER;
 113    }
 114  
 115    /**
 116     * Attempt to exchange a code for an valid authentication token.
 117     * If $crossClient is set to true, the request body will not include
 118     * the request_uri argument
 119     * Helper wrapped around the OAuth 2.0 implementation.
 120     *
 121     * @param $code string code from accounts.google.com
 122     * @param $crossClient boolean, whether this is a cross-client authentication
 123     * @return string token
 124     */
 125    public function authenticate($code, $crossClient = false)
 126    {
 127      $this->authenticated = true;
 128      return $this->getAuth()->authenticate($code, $crossClient);
 129    }
 130    
 131    /**
 132     * Loads a service account key and parameters from a JSON
 133     * file from the Google Developer Console. Uses that and the
 134     * given array of scopes to return an assertion credential for
 135     * use with refreshTokenWithAssertionCredential.
 136     *
 137     * @param string $jsonLocation File location of the project-key.json.
 138     * @param array $scopes The scopes to assert.
 139     * @return Google_Auth_AssertionCredentials.
 140     * @
 141     */
 142    public function loadServiceAccountJson($jsonLocation, $scopes)
 143    {
 144      $data = json_decode(file_get_contents($jsonLocation));
 145      if (isset($data->type) && $data->type == 'service_account') {
 146        // Service Account format.
 147        $cred = new Google_Auth_AssertionCredentials(
 148            $data->client_email,
 149            $scopes,
 150            $data->private_key
 151        );
 152        return $cred;
 153      } else {
 154        throw new Google_Exception("Invalid service account JSON file.");
 155      }
 156    }
 157  
 158    /**
 159     * Set the auth config from the JSON string provided.
 160     * This structure should match the file downloaded from
 161     * the "Download JSON" button on in the Google Developer
 162     * Console.
 163     * @param string $json the configuration json
 164     * @throws Google_Exception
 165     */
 166    public function setAuthConfig($json)
 167    {
 168      $data = json_decode($json);
 169      $key = isset($data->installed) ? 'installed' : 'web';
 170      if (!isset($data->$key)) {
 171        throw new Google_Exception("Invalid client secret JSON file.");
 172      }
 173      $this->setClientId($data->$key->client_id);
 174      $this->setClientSecret($data->$key->client_secret);
 175      if (isset($data->$key->redirect_uris)) {
 176        $this->setRedirectUri($data->$key->redirect_uris[0]);
 177      }
 178    }
 179  
 180    /**
 181     * Set the auth config from the JSON file in the path
 182     * provided. This should match the file downloaded from
 183     * the "Download JSON" button on in the Google Developer
 184     * Console.
 185     * @param string $file the file location of the client json
 186     */
 187    public function setAuthConfigFile($file)
 188    {
 189      $this->setAuthConfig(file_get_contents($file));
 190    }
 191  
 192    /**
 193     * @throws Google_Auth_Exception
 194     * @return array
 195     * @visible For Testing
 196     */
 197    public function prepareScopes()
 198    {
 199      if (empty($this->requestedScopes)) {
 200        throw new Google_Auth_Exception("No scopes specified");
 201      }
 202      $scopes = implode(' ', $this->requestedScopes);
 203      return $scopes;
 204    }
 205  
 206    /**
 207     * Set the OAuth 2.0 access token using the string that resulted from calling createAuthUrl()
 208     * or Google_Client#getAccessToken().
 209     * @param string $accessToken JSON encoded string containing in the following format:
 210     * {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer",
 211     *  "expires_in":3600, "id_token":"TOKEN", "created":1320790426}
 212     */
 213    public function setAccessToken($accessToken)
 214    {
 215      if ($accessToken == 'null') {
 216        $accessToken = null;
 217      }
 218      $this->getAuth()->setAccessToken($accessToken);
 219    }
 220  
 221  
 222  
 223    /**
 224     * Set the authenticator object
 225     * @param Google_Auth_Abstract $auth
 226     */
 227    public function setAuth(Google_Auth_Abstract $auth)
 228    {
 229      $this->config->setAuthClass(get_class($auth));
 230      $this->auth = $auth;
 231    }
 232  
 233    /**
 234     * Set the IO object
 235     * @param Google_IO_Abstract $io
 236     */
 237    public function setIo(Google_IO_Abstract $io)
 238    {
 239      $this->config->setIoClass(get_class($io));
 240      $this->io = $io;
 241    }
 242  
 243    /**
 244     * Set the Cache object
 245     * @param Google_Cache_Abstract $cache
 246     */
 247    public function setCache(Google_Cache_Abstract $cache)
 248    {
 249      $this->config->setCacheClass(get_class($cache));
 250      $this->cache = $cache;
 251    }
 252  
 253    /**
 254     * Set the Logger object
 255     * @param Google_Logger_Abstract $logger
 256     */
 257    public function setLogger(Google_Logger_Abstract $logger)
 258    {
 259      $this->config->setLoggerClass(get_class($logger));
 260      $this->logger = $logger;
 261    }
 262  
 263    /**
 264     * Construct the OAuth 2.0 authorization request URI.
 265     * @return string
 266     */
 267    public function createAuthUrl()
 268    {
 269      $scopes = $this->prepareScopes();
 270      return $this->getAuth()->createAuthUrl($scopes);
 271    }
 272  
 273    /**
 274     * Get the OAuth 2.0 access token.
 275     * @return string $accessToken JSON encoded string in the following format:
 276     * {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer",
 277     *  "expires_in":3600,"id_token":"TOKEN", "created":1320790426}
 278     */
 279    public function getAccessToken()
 280    {
 281      $token = $this->getAuth()->getAccessToken();
 282      // The response is json encoded, so could be the string null.
 283      // It is arguable whether this check should be here or lower
 284      // in the library.
 285      return (null == $token || 'null' == $token || '[]' == $token) ? null : $token;
 286    }
 287  
 288    /**
 289     * Get the OAuth 2.0 refresh token.
 290     * @return string $refreshToken refresh token or null if not available
 291     */
 292    public function getRefreshToken()
 293    {
 294      return $this->getAuth()->getRefreshToken();
 295    }
 296  
 297    /**
 298     * Returns if the access_token is expired.
 299     * @return bool Returns True if the access_token is expired.
 300     */
 301    public function isAccessTokenExpired()
 302    {
 303      return $this->getAuth()->isAccessTokenExpired();
 304    }
 305  
 306    /**
 307     * Set OAuth 2.0 "state" parameter to achieve per-request customization.
 308     * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2
 309     * @param string $state
 310     */
 311    public function setState($state)
 312    {
 313      $this->getAuth()->setState($state);
 314    }
 315  
 316    /**
 317     * @param string $accessType Possible values for access_type include:
 318     *  {@code "offline"} to request offline access from the user.
 319     *  {@code "online"} to request online access from the user.
 320     */
 321    public function setAccessType($accessType)
 322    {
 323      $this->config->setAccessType($accessType);
 324    }
 325  
 326    /**
 327     * @param string $approvalPrompt Possible values for approval_prompt include:
 328     *  {@code "force"} to force the approval UI to appear. (This is the default value)
 329     *  {@code "auto"} to request auto-approval when possible.
 330     */
 331    public function setApprovalPrompt($approvalPrompt)
 332    {
 333      $this->config->setApprovalPrompt($approvalPrompt);
 334    }
 335  
 336    /**
 337     * Set the login hint, email address or sub id.
 338     * @param string $loginHint
 339     */
 340    public function setLoginHint($loginHint)
 341    {
 342        $this->config->setLoginHint($loginHint);
 343    }
 344  
 345    /**
 346     * Set the application name, this is included in the User-Agent HTTP header.
 347     * @param string $applicationName
 348     */
 349    public function setApplicationName($applicationName)
 350    {
 351      $this->config->setApplicationName($applicationName);
 352    }
 353  
 354    /**
 355     * Set the OAuth 2.0 Client ID.
 356     * @param string $clientId
 357     */
 358    public function setClientId($clientId)
 359    {
 360      $this->config->setClientId($clientId);
 361    }
 362  
 363    /**
 364     * Set the OAuth 2.0 Client Secret.
 365     * @param string $clientSecret
 366     */
 367    public function setClientSecret($clientSecret)
 368    {
 369      $this->config->setClientSecret($clientSecret);
 370    }
 371  
 372    /**
 373     * Set the OAuth 2.0 Redirect URI.
 374     * @param string $redirectUri
 375     */
 376    public function setRedirectUri($redirectUri)
 377    {
 378      $this->config->setRedirectUri($redirectUri);
 379    }
 380  
 381    /**
 382     * If 'plus.login' is included in the list of requested scopes, you can use
 383     * this method to define types of app activities that your app will write.
 384     * You can find a list of available types here:
 385     * @link https://developers.google.com/+/api/moment-types
 386     *
 387     * @param array $requestVisibleActions Array of app activity types
 388     */
 389    public function setRequestVisibleActions($requestVisibleActions)
 390    {
 391      if (is_array($requestVisibleActions)) {
 392        $requestVisibleActions = join(" ", $requestVisibleActions);
 393      }
 394      $this->config->setRequestVisibleActions($requestVisibleActions);
 395    }
 396  
 397    /**
 398     * Set the developer key to use, these are obtained through the API Console.
 399     * @see http://code.google.com/apis/console-help/#generatingdevkeys
 400     * @param string $developerKey
 401     */
 402    public function setDeveloperKey($developerKey)
 403    {
 404      $this->config->setDeveloperKey($developerKey);
 405    }
 406  
 407    /**
 408     * Set the hd (hosted domain) parameter streamlines the login process for
 409     * Google Apps hosted accounts. By including the domain of the user, you
 410     * restrict sign-in to accounts at that domain.
 411     * @param $hd string - the domain to use.
 412     */
 413    public function setHostedDomain($hd)
 414    {
 415      $this->config->setHostedDomain($hd);
 416    }
 417  
 418    /**
 419     * Set the prompt hint. Valid values are none, consent and select_account.
 420     * If no value is specified and the user has not previously authorized
 421     * access, then the user is shown a consent screen.
 422     * @param $prompt string
 423     */
 424    public function setPrompt($prompt)
 425    {
 426      $this->config->setPrompt($prompt);
 427    }
 428  
 429    /**
 430     * openid.realm is a parameter from the OpenID 2.0 protocol, not from OAuth
 431     * 2.0. It is used in OpenID 2.0 requests to signify the URL-space for which
 432     * an authentication request is valid.
 433     * @param $realm string - the URL-space to use.
 434     */
 435    public function setOpenidRealm($realm)
 436    {
 437      $this->config->setOpenidRealm($realm);
 438    }
 439  
 440    /**
 441     * If this is provided with the value true, and the authorization request is
 442     * granted, the authorization will include any previous authorizations
 443     * granted to this user/application combination for other scopes.
 444     * @param $include boolean - the URL-space to use.
 445     */
 446    public function setIncludeGrantedScopes($include)
 447    {
 448      $this->config->setIncludeGrantedScopes($include);
 449    }
 450  
 451    /**
 452     * Fetches a fresh OAuth 2.0 access token with the given refresh token.
 453     * @param string $refreshToken
 454     */
 455    public function refreshToken($refreshToken)
 456    {
 457      $this->getAuth()->refreshToken($refreshToken);
 458    }
 459  
 460    /**
 461     * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
 462     * token, if a token isn't provided.
 463     * @throws Google_Auth_Exception
 464     * @param string|null $token The token (access token or a refresh token) that should be revoked.
 465     * @return boolean Returns True if the revocation was successful, otherwise False.
 466     */
 467    public function revokeToken($token = null)
 468    {
 469      return $this->getAuth()->revokeToken($token);
 470    }
 471  
 472    /**
 473     * Verify an id_token. This method will verify the current id_token, if one
 474     * isn't provided.
 475     * @throws Google_Auth_Exception
 476     * @param string|null $token The token (id_token) that should be verified.
 477     * @return Google_Auth_LoginTicket Returns an apiLoginTicket if the verification was
 478     * successful.
 479     */
 480    public function verifyIdToken($token = null)
 481    {
 482      return $this->getAuth()->verifyIdToken($token);
 483    }
 484  
 485    /**
 486     * Verify a JWT that was signed with your own certificates.
 487     *
 488     * @param $id_token string The JWT token
 489     * @param $cert_location array of certificates
 490     * @param $audience string the expected consumer of the token
 491     * @param $issuer string the expected issuer, defaults to Google
 492     * @param [$max_expiry] the max lifetime of a token, defaults to MAX_TOKEN_LIFETIME_SECS
 493     * @return mixed token information if valid, false if not
 494     */
 495    public function verifySignedJwt($id_token, $cert_location, $audience, $issuer, $max_expiry = null)
 496    {
 497      $auth = new Google_Auth_OAuth2($this);
 498      $certs = $auth->retrieveCertsFromLocation($cert_location);
 499      return $auth->verifySignedJwtWithCerts($id_token, $certs, $audience, $issuer, $max_expiry);
 500    }
 501  
 502    /**
 503     * @param $creds Google_Auth_AssertionCredentials
 504     */
 505    public function setAssertionCredentials(Google_Auth_AssertionCredentials $creds)
 506    {
 507      $this->getAuth()->setAssertionCredentials($creds);
 508    }
 509  
 510    /**
 511     * Set the scopes to be requested. Must be called before createAuthUrl().
 512     * Will remove any previously configured scopes.
 513     * @param array $scopes, ie: array('https://www.googleapis.com/auth/plus.login',
 514     * 'https://www.googleapis.com/auth/moderator')
 515     */
 516    public function setScopes($scopes)
 517    {
 518      $this->requestedScopes = array();
 519      $this->addScope($scopes);
 520    }
 521  
 522    /**
 523     * This functions adds a scope to be requested as part of the OAuth2.0 flow.
 524     * Will append any scopes not previously requested to the scope parameter.
 525     * A single string will be treated as a scope to request. An array of strings
 526     * will each be appended.
 527     * @param $scope_or_scopes string|array e.g. "profile"
 528     */
 529    public function addScope($scope_or_scopes)
 530    {
 531      if (is_string($scope_or_scopes) && !in_array($scope_or_scopes, $this->requestedScopes)) {
 532        $this->requestedScopes[] = $scope_or_scopes;
 533      } else if (is_array($scope_or_scopes)) {
 534        foreach ($scope_or_scopes as $scope) {
 535          $this->addScope($scope);
 536        }
 537      }
 538    }
 539  
 540    /**
 541     * Returns the list of scopes requested by the client
 542     * @return array the list of scopes
 543     *
 544     */
 545    public function getScopes()
 546    {
 547       return $this->requestedScopes;
 548    }
 549  
 550    /**
 551     * Declare whether batch calls should be used. This may increase throughput
 552     * by making multiple requests in one connection.
 553     *
 554     * @param boolean $useBatch True if the batch support should
 555     * be enabled. Defaults to False.
 556     */
 557    public function setUseBatch($useBatch)
 558    {
 559      // This is actually an alias for setDefer.
 560      $this->setDefer($useBatch);
 561    }
 562  
 563    /**
 564     * Declare whether making API calls should make the call immediately, or
 565     * return a request which can be called with ->execute();
 566     *
 567     * @param boolean $defer True if calls should not be executed right away.
 568     */
 569    public function setDefer($defer)
 570    {
 571      $this->deferExecution = $defer;
 572    }
 573  
 574    /**
 575     * Helper method to execute deferred HTTP requests.
 576     *
 577     * @param $request Google_Http_Request|Google_Http_Batch
 578     * @throws Google_Exception
 579     * @return object of the type of the expected class or array.
 580     */
 581    public function execute($request)
 582    {
 583      if ($request instanceof Google_Http_Request) {
 584        $request->setUserAgent(
 585            $this->getApplicationName()
 586            . " " . self::USER_AGENT_SUFFIX
 587            . $this->getLibraryVersion()
 588        );
 589        if (!$this->getClassConfig("Google_Http_Request", "disable_gzip")) {
 590          $request->enableGzip();
 591        }
 592        $request->maybeMoveParametersToBody();
 593        return Google_Http_REST::execute($this, $request);
 594      } else if ($request instanceof Google_Http_Batch) {
 595        return $request->execute();
 596      } else {
 597        throw new Google_Exception("Do not know how to execute this type of object.");
 598      }
 599    }
 600  
 601    /**
 602     * Whether or not to return raw requests
 603     * @return boolean
 604     */
 605    public function shouldDefer()
 606    {
 607      return $this->deferExecution;
 608    }
 609  
 610    /**
 611     * @return Google_Auth_Abstract Authentication implementation
 612     */
 613    public function getAuth()
 614    {
 615      if (!isset($this->auth)) {
 616        $class = $this->config->getAuthClass();
 617        $this->auth = new $class($this);
 618      }
 619      return $this->auth;
 620    }
 621  
 622    /**
 623     * @return Google_IO_Abstract IO implementation
 624     */
 625    public function getIo()
 626    {
 627      if (!isset($this->io)) {
 628        $class = $this->config->getIoClass();
 629        $this->io = new $class($this);
 630      }
 631      return $this->io;
 632    }
 633  
 634    /**
 635     * @return Google_Cache_Abstract Cache implementation
 636     */
 637    public function getCache()
 638    {
 639      if (!isset($this->cache)) {
 640        $class = $this->config->getCacheClass();
 641        $this->cache = new $class($this);
 642      }
 643      return $this->cache;
 644    }
 645  
 646    /**
 647     * @return Google_Logger_Abstract Logger implementation
 648     */
 649    public function getLogger()
 650    {
 651      if (!isset($this->logger)) {
 652        $class = $this->config->getLoggerClass();
 653        $this->logger = new $class($this);
 654      }
 655      return $this->logger;
 656    }
 657  
 658    /**
 659     * Retrieve custom configuration for a specific class.
 660     * @param $class string|object - class or instance of class to retrieve
 661     * @param $key string optional - key to retrieve
 662     * @return array
 663     */
 664    public function getClassConfig($class, $key = null)
 665    {
 666      if (!is_string($class)) {
 667        $class = get_class($class);
 668      }
 669      return $this->config->getClassConfig($class, $key);
 670    }
 671  
 672    /**
 673     * Set configuration specific to a given class.
 674     * $config->setClassConfig('Google_Cache_File',
 675     *   array('directory' => '/tmp/cache'));
 676     * @param $class string|object - The class name for the configuration
 677     * @param $config string key or an array of configuration values
 678     * @param $value string optional - if $config is a key, the value
 679     *
 680     */
 681    public function setClassConfig($class, $config, $value = null)
 682    {
 683      if (!is_string($class)) {
 684        $class = get_class($class);
 685      }
 686      $this->config->setClassConfig($class, $config, $value);
 687  
 688    }
 689  
 690    /**
 691     * @return string the base URL to use for calls to the APIs
 692     */
 693    public function getBasePath()
 694    {
 695      return $this->config->getBasePath();
 696    }
 697  
 698    /**
 699     * @return string the name of the application
 700     */
 701    public function getApplicationName()
 702    {
 703      return $this->config->getApplicationName();
 704    }
 705  
 706    /**
 707     * Are we running in Google AppEngine?
 708     * return bool
 709     */
 710    public function isAppEngine()
 711    {
 712      return (isset($_SERVER['SERVER_SOFTWARE']) &&
 713          strpos($_SERVER['SERVER_SOFTWARE'], 'Google App Engine') !== false);
 714    }
 715  }