Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

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