Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

   1  <?php
   2  /*
   3   * Copyright 2014 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   * Authentication via built-in Compute Engine service accounts.
  24   * The instance must be pre-configured with a service account
  25   * and the appropriate scopes.
  26   * @author Jonathan Parrott <jon.wayne.parrott@gmail.com>
  27   */
  28  class Google_Auth_ComputeEngine extends Google_Auth_Abstract
  29  {
  30    const METADATA_AUTH_URL =
  31        'http://metadata/computeMetadata/v1/instance/service-accounts/default/token';
  32    private $client;
  33    private $token;
  34  
  35    public function __construct(Google_Client $client, $config = null)
  36    {
  37      $this->client = $client;
  38    }
  39  
  40    /**
  41     * Perform an authenticated / signed apiHttpRequest.
  42     * This function takes the apiHttpRequest, calls apiAuth->sign on it
  43     * (which can modify the request in what ever way fits the auth mechanism)
  44     * and then calls apiCurlIO::makeRequest on the signed request
  45     *
  46     * @param Google_Http_Request $request
  47     * @return Google_Http_Request The resulting HTTP response including the
  48     * responseHttpCode, responseHeaders and responseBody.
  49     */
  50    public function authenticatedRequest(Google_Http_Request $request)
  51    {
  52      $request = $this->sign($request);
  53      return $this->client->getIo()->makeRequest($request);
  54    }
  55  
  56    /**
  57     * @param string $token
  58     * @throws Google_Auth_Exception
  59     */
  60    public function setAccessToken($token)
  61    {
  62      $token = json_decode($token, true);
  63      if ($token == null) {
  64        throw new Google_Auth_Exception('Could not json decode the token');
  65      }
  66      if (! isset($token['access_token'])) {
  67        throw new Google_Auth_Exception("Invalid token format");
  68      }
  69      $token['created'] = time();
  70      $this->token = $token;
  71    }
  72  
  73    public function getAccessToken()
  74    {
  75      return json_encode($this->token);
  76    }
  77  
  78    /**
  79     * Acquires a new access token from the compute engine metadata server.
  80     * @throws Google_Auth_Exception
  81     */
  82    public function acquireAccessToken()
  83    {
  84      $request = new Google_Http_Request(
  85          self::METADATA_AUTH_URL,
  86          'GET',
  87          array(
  88            'Metadata-Flavor' => 'Google'
  89          )
  90      );
  91      $request->disableGzip();
  92      $response = $this->client->getIo()->makeRequest($request);
  93  
  94      if ($response->getResponseHttpCode() == 200) {
  95        $this->setAccessToken($response->getResponseBody());
  96        $this->token['created'] = time();
  97        return $this->getAccessToken();
  98      } else {
  99        throw new Google_Auth_Exception(
 100            sprintf(
 101                "Error fetching service account access token, message: '%s'",
 102                $response->getResponseBody()
 103            ),
 104            $response->getResponseHttpCode()
 105        );
 106      }
 107    }
 108  
 109    /**
 110     * Include an accessToken in a given apiHttpRequest.
 111     * @param Google_Http_Request $request
 112     * @return Google_Http_Request
 113     * @throws Google_Auth_Exception
 114     */
 115    public function sign(Google_Http_Request $request)
 116    {
 117      if ($this->isAccessTokenExpired()) {
 118        $this->acquireAccessToken();
 119      }
 120  
 121      $this->client->getLogger()->debug('Compute engine service account authentication');
 122  
 123      $request->setRequestHeaders(
 124          array('Authorization' => 'Bearer ' . $this->token['access_token'])
 125      );
 126  
 127      return $request;
 128    }
 129  
 130    /**
 131     * Returns if the access_token is expired.
 132     * @return bool Returns True if the access_token is expired.
 133     */
 134    public function isAccessTokenExpired()
 135    {
 136      if (!$this->token || !isset($this->token['created'])) {
 137        return true;
 138      }
 139  
 140      // If the token is set to expire in the next 30 seconds.
 141      $expired = ($this->token['created']
 142          + ($this->token['expires_in'] - 30)) < time();
 143  
 144      return $expired;
 145    }
 146  }