Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 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 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Class for loading/storing issuers from the DB.
  19   *
  20   * @package    core
  21   * @copyright  2017 Damyon Wiese
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace core\oauth2;
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  use core\persistent;
  29  use lang_string;
  30  
  31  /**
  32   * Class for loading/storing issuer from the DB
  33   *
  34   * @copyright  2017 Damyon Wiese
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class issuer extends persistent {
  38  
  39      const TABLE = 'oauth2_issuer';
  40  
  41      /**
  42       * Return the definition of the properties of this model.
  43       *
  44       * @return array
  45       */
  46      protected static function define_properties() {
  47          return array(
  48              'name' => array(
  49                  'type' => PARAM_TEXT
  50              ),
  51              'image' => array(
  52                  'type' => PARAM_URL,
  53                  'null' => NULL_ALLOWED,
  54                  'default' => null
  55              ),
  56              'clientid' => array(
  57                  'type' => PARAM_RAW_TRIMMED,
  58                  'default' => ''
  59              ),
  60              'clientsecret' => array(
  61                  'type' => PARAM_RAW_TRIMMED,
  62                  'default' => ''
  63              ),
  64              'baseurl' => array(
  65                  'type' => PARAM_URL,
  66                  'default' => ''
  67              ),
  68              'enabled' => array(
  69                  'type' => PARAM_BOOL,
  70                  'default' => true
  71              ),
  72              'showonloginpage' => array(
  73                  'type' => PARAM_BOOL,
  74                  'default' => false
  75              ),
  76              'basicauth' => array(
  77                  'type' => PARAM_BOOL,
  78                  'default' => false
  79              ),
  80              'scopessupported' => array(
  81                  'type' => PARAM_RAW,
  82                  'null' => NULL_ALLOWED,
  83                  'default' => null
  84              ),
  85              'loginscopes' => array(
  86                  'type' => PARAM_RAW,
  87                  'default' => 'openid profile email'
  88              ),
  89              'loginscopesoffline' => array(
  90                  'type' => PARAM_RAW,
  91                  'default' => 'openid profile email'
  92              ),
  93              'loginparams' => array(
  94                  'type' => PARAM_RAW,
  95                  'default' => ''
  96              ),
  97              'loginparamsoffline' => array(
  98                  'type' => PARAM_RAW,
  99                  'default' => ''
 100              ),
 101              'alloweddomains' => array(
 102                  'type' => PARAM_RAW,
 103                  'default' => ''
 104              ),
 105              'sortorder' => array(
 106                  'type' => PARAM_INT,
 107                  'default' => 0,
 108              ),
 109              'requireconfirmation' => array(
 110                  'type' => PARAM_BOOL,
 111                  'default' => true
 112              )
 113          );
 114      }
 115  
 116      /**
 117       * Hook to execute before validate.
 118       *
 119       * @return void
 120       */
 121      protected function before_validate() {
 122          if (($this->get('id') && $this->get('sortorder') === null) || !$this->get('id')) {
 123              $this->set('sortorder', $this->count_records());
 124          }
 125      }
 126  
 127      /**
 128       * Helper the get a named service endpoint.
 129       * @param string $type
 130       * @return string|false
 131       */
 132      public function get_endpoint_url($type) {
 133          $endpoint = endpoint::get_record([
 134              'issuerid' => $this->get('id'),
 135              'name' => $type . '_endpoint'
 136          ]);
 137  
 138          if ($endpoint) {
 139              return $endpoint->get('url');
 140          }
 141          return false;
 142      }
 143  
 144      /**
 145       * Perform matching against the list of allowed login domains for this issuer.
 146       *
 147       * @param string $email The email to check.
 148       * @return boolean
 149       */
 150      public function is_valid_login_domain($email) {
 151          if (empty($this->get('alloweddomains'))) {
 152              return true;
 153          }
 154  
 155          $validdomains = explode(',', $this->get('alloweddomains'));
 156  
 157          $parts = explode('@', $email, 2);
 158          $emaildomain = '';
 159          if (count($parts) > 1) {
 160              $emaildomain = $parts[1];
 161          }
 162  
 163          return \core\ip_utils::is_domain_in_allowed_list($emaildomain, $validdomains);
 164      }
 165  
 166      /**
 167       * Does this OAuth service support user authentication?
 168       * @return boolean
 169       */
 170      public function is_authentication_supported() {
 171          return (!empty($this->get_endpoint_url('userinfo')));
 172      }
 173  
 174      /**
 175       * Return true if this issuer looks like it has been configured.
 176       *
 177       * @return boolean
 178       */
 179      public function is_configured() {
 180          return (!empty($this->get('clientid')) && !empty($this->get('clientsecret')));
 181      }
 182  
 183      /**
 184       * Do we have a refresh token for a system account?
 185       * @return boolean
 186       */
 187      public function is_system_account_connected() {
 188          if (!$this->is_configured()) {
 189              return false;
 190          }
 191          $sys = system_account::get_record(['issuerid' => $this->get('id')]);
 192          if (empty($sys) || empty($sys->get('refreshtoken'))) {
 193              return false;
 194          }
 195  
 196          $scopes = api::get_system_scopes_for_issuer($this);
 197  
 198          $grantedscopes = $sys->get('grantedscopes');
 199  
 200          $scopes = explode(' ', $scopes);
 201  
 202          foreach ($scopes as $scope) {
 203              if (!empty($scope)) {
 204                  if (strpos(' ' . $grantedscopes . ' ', ' ' . $scope . ' ') === false) {
 205                      // We have not been granted all the scopes that are required.
 206                      return false;
 207                  }
 208              }
 209          }
 210  
 211          return true;
 212      }
 213  
 214      /**
 215       * Custom validator for end point URLs.
 216       * Because we send Bearer tokens we must ensure SSL.
 217       *
 218       * @param string $value The value to check.
 219       * @return lang_string|boolean
 220       */
 221      protected function validate_baseurl($value) {
 222          global $CFG;
 223          include_once($CFG->dirroot . '/lib/validateurlsyntax.php');
 224          if (!empty($value) && !validateUrlSyntax($value, 'S+')) {
 225              return new lang_string('sslonlyaccess', 'error');
 226          }
 227          return true;
 228      }
 229  }