| 1 | efrain | 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 oauth2 endpoints 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 | require_once($CFG->libdir . '/filelib.php');
 | 
        
           |  |  | 29 |   | 
        
           |  |  | 30 | use stdClass;
 | 
        
           |  |  | 31 | use moodle_url;
 | 
        
           |  |  | 32 | use context_system;
 | 
        
           |  |  | 33 | use moodle_exception;
 | 
        
           |  |  | 34 |   | 
        
           |  |  | 35 | /**
 | 
        
           |  |  | 36 |  * Static list of api methods for system oauth2 configuration.
 | 
        
           |  |  | 37 |  *
 | 
        
           |  |  | 38 |  * @copyright  2017 Damyon Wiese
 | 
        
           |  |  | 39 |  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 40 |  */
 | 
        
           |  |  | 41 | class api {
 | 
        
           |  |  | 42 |   | 
        
           |  |  | 43 |     /**
 | 
        
           |  |  | 44 |      * Initializes a record for one of the standard issuers to be displayed in the settings.
 | 
        
           |  |  | 45 |      * The issuer is not yet created in the database.
 | 
        
           |  |  | 46 |      * @param string $type One of google, facebook, microsoft, nextcloud, imsobv2p1
 | 
        
           |  |  | 47 |      * @return \core\oauth2\issuer
 | 
        
           |  |  | 48 |      */
 | 
        
           |  |  | 49 |     public static function init_standard_issuer($type) {
 | 
        
           |  |  | 50 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 51 |   | 
        
           |  |  | 52 |         $classname = self::get_service_classname($type);
 | 
        
           |  |  | 53 |         if (class_exists($classname)) {
 | 
        
           |  |  | 54 |             return $classname::init();
 | 
        
           |  |  | 55 |         }
 | 
        
           |  |  | 56 |         throw new moodle_exception('OAuth 2 service type not recognised: ' . $type);
 | 
        
           |  |  | 57 |     }
 | 
        
           |  |  | 58 |   | 
        
           |  |  | 59 |     /**
 | 
        
           |  |  | 60 |      * Create endpoints for standard issuers, based on the issuer created from submitted data.
 | 
        
           |  |  | 61 |      * @param string $type One of google, facebook, microsoft, nextcloud, imsobv2p1
 | 
        
           |  |  | 62 |      * @param issuer $issuer issuer the endpoints should be created for.
 | 
        
           |  |  | 63 |      * @return \core\oauth2\issuer
 | 
        
           |  |  | 64 |      */
 | 
        
           |  |  | 65 |     public static function create_endpoints_for_standard_issuer($type, $issuer) {
 | 
        
           |  |  | 66 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 67 |   | 
        
           |  |  | 68 |         $classname = self::get_service_classname($type);
 | 
        
           |  |  | 69 |         if (class_exists($classname)) {
 | 
        
           |  |  | 70 |             $classname::create_endpoints($issuer);
 | 
        
           |  |  | 71 |             return $issuer;
 | 
        
           |  |  | 72 |         }
 | 
        
           |  |  | 73 |         throw new moodle_exception('OAuth 2 service type not recognised: ' . $type);
 | 
        
           |  |  | 74 |     }
 | 
        
           |  |  | 75 |   | 
        
           |  |  | 76 |     /**
 | 
        
           |  |  | 77 |      * Create one of the standard issuers.
 | 
        
           |  |  | 78 |      *
 | 
        
           |  |  | 79 |      * @param string $type One of google, facebook, microsoft, MoodleNet, nextcloud or imsobv2p1
 | 
        
           |  |  | 80 |      * @param string|false $baseurl Baseurl (only required for nextcloud, imsobv2p1 and moodlenet)
 | 
        
           |  |  | 81 |      * @return \core\oauth2\issuer
 | 
        
           |  |  | 82 |      */
 | 
        
           |  |  | 83 |     public static function create_standard_issuer($type, $baseurl = false) {
 | 
        
           |  |  | 84 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 85 |   | 
        
           |  |  | 86 |         switch ($type) {
 | 
        
           |  |  | 87 |             case 'imsobv2p1':
 | 
        
           |  |  | 88 |                 if (!$baseurl) {
 | 
        
           |  |  | 89 |                     throw new moodle_exception('IMS OBv2.1 service type requires the baseurl parameter.');
 | 
        
           |  |  | 90 |                 }
 | 
        
           |  |  | 91 |             case 'nextcloud':
 | 
        
           |  |  | 92 |                 if (!$baseurl) {
 | 
        
           |  |  | 93 |                     throw new moodle_exception('Nextcloud service type requires the baseurl parameter.');
 | 
        
           |  |  | 94 |                 }
 | 
        
           |  |  | 95 |             case 'moodlenet':
 | 
        
           |  |  | 96 |                 if (!$baseurl) {
 | 
        
           |  |  | 97 |                     throw new moodle_exception('MoodleNet service type requires the baseurl parameter.');
 | 
        
           |  |  | 98 |                 }
 | 
        
           |  |  | 99 |             case 'google':
 | 
        
           |  |  | 100 |             case 'facebook':
 | 
        
           |  |  | 101 |             case 'microsoft':
 | 
        
           |  |  | 102 |                 $classname = self::get_service_classname($type);
 | 
        
           |  |  | 103 |                 $issuer = $classname::init();
 | 
        
           |  |  | 104 |                 if ($baseurl) {
 | 
        
           |  |  | 105 |                     $issuer->set('baseurl', $baseurl);
 | 
        
           |  |  | 106 |                 }
 | 
        
           |  |  | 107 |                 $issuer->create();
 | 
        
           |  |  | 108 |                 return self::create_endpoints_for_standard_issuer($type, $issuer);
 | 
        
           |  |  | 109 |         }
 | 
        
           |  |  | 110 |   | 
        
           |  |  | 111 |         throw new moodle_exception('OAuth 2 service type not recognised: ' . $type);
 | 
        
           |  |  | 112 |     }
 | 
        
           |  |  | 113 |   | 
        
           |  |  | 114 |   | 
        
           |  |  | 115 |     /**
 | 
        
           |  |  | 116 |      * List all the issuers, ordered by the sortorder field
 | 
        
           |  |  | 117 |      *
 | 
        
           |  |  | 118 |      * @param bool $includeloginonly also include issuers that are configured to be shown only on login page,
 | 
        
           |  |  | 119 |      *     By default false, in this case the method returns all issuers that can be used in services
 | 
        
           |  |  | 120 |      * @return \core\oauth2\issuer[]
 | 
        
           |  |  | 121 |      */
 | 
        
           |  |  | 122 |     public static function get_all_issuers(bool $includeloginonly = false) {
 | 
        
           |  |  | 123 |         if ($includeloginonly) {
 | 
        
           |  |  | 124 |             return issuer::get_records([], 'sortorder');
 | 
        
           |  |  | 125 |         } else {
 | 
        
           |  |  | 126 |             return array_values(issuer::get_records_select('showonloginpage<>?', [issuer::LOGINONLY], 'sortorder'));
 | 
        
           |  |  | 127 |         }
 | 
        
           |  |  | 128 |     }
 | 
        
           |  |  | 129 |   | 
        
           |  |  | 130 |     /**
 | 
        
           |  |  | 131 |      * Get a single issuer by id.
 | 
        
           |  |  | 132 |      *
 | 
        
           |  |  | 133 |      * @param int $id
 | 
        
           |  |  | 134 |      * @return \core\oauth2\issuer
 | 
        
           |  |  | 135 |      */
 | 
        
           |  |  | 136 |     public static function get_issuer($id) {
 | 
        
           |  |  | 137 |         return new issuer($id);
 | 
        
           |  |  | 138 |     }
 | 
        
           |  |  | 139 |   | 
        
           |  |  | 140 |     /**
 | 
        
           |  |  | 141 |      * Get a single endpoint by id.
 | 
        
           |  |  | 142 |      *
 | 
        
           |  |  | 143 |      * @param int $id
 | 
        
           |  |  | 144 |      * @return \core\oauth2\endpoint
 | 
        
           |  |  | 145 |      */
 | 
        
           |  |  | 146 |     public static function get_endpoint($id) {
 | 
        
           |  |  | 147 |         return new endpoint($id);
 | 
        
           |  |  | 148 |     }
 | 
        
           |  |  | 149 |   | 
        
           |  |  | 150 |     /**
 | 
        
           |  |  | 151 |      * Get a single user field mapping by id.
 | 
        
           |  |  | 152 |      *
 | 
        
           |  |  | 153 |      * @param int $id
 | 
        
           |  |  | 154 |      * @return \core\oauth2\user_field_mapping
 | 
        
           |  |  | 155 |      */
 | 
        
           |  |  | 156 |     public static function get_user_field_mapping($id) {
 | 
        
           |  |  | 157 |         return new user_field_mapping($id);
 | 
        
           |  |  | 158 |     }
 | 
        
           |  |  | 159 |   | 
        
           |  |  | 160 |     /**
 | 
        
           |  |  | 161 |      * Get the system account for an installed OAuth service.
 | 
        
           |  |  | 162 |      * Never ever ever expose this to a webservice because it contains the refresh token which grants API access.
 | 
        
           |  |  | 163 |      *
 | 
        
           |  |  | 164 |      * @param \core\oauth2\issuer $issuer
 | 
        
           |  |  | 165 |      * @return system_account|false
 | 
        
           |  |  | 166 |      */
 | 
        
           |  |  | 167 |     public static function get_system_account(issuer $issuer) {
 | 
        
           |  |  | 168 |         return system_account::get_record(['issuerid' => $issuer->get('id')]);
 | 
        
           |  |  | 169 |     }
 | 
        
           |  |  | 170 |   | 
        
           |  |  | 171 |     /**
 | 
        
           |  |  | 172 |      * Get the full list of system scopes required by an oauth issuer.
 | 
        
           |  |  | 173 |      * This includes the list required for login as well as any scopes injected by the oauth2_system_scopes callback in plugins.
 | 
        
           |  |  | 174 |      *
 | 
        
           |  |  | 175 |      * @param \core\oauth2\issuer $issuer
 | 
        
           |  |  | 176 |      * @return string
 | 
        
           |  |  | 177 |      */
 | 
        
           |  |  | 178 |     public static function get_system_scopes_for_issuer($issuer) {
 | 
        
           |  |  | 179 |         $scopes = $issuer->get('loginscopesoffline');
 | 
        
           |  |  | 180 |   | 
        
           |  |  | 181 |         $pluginsfunction = get_plugins_with_function('oauth2_system_scopes', 'lib.php');
 | 
        
           |  |  | 182 |         foreach ($pluginsfunction as $plugintype => $plugins) {
 | 
        
           |  |  | 183 |             foreach ($plugins as $pluginfunction) {
 | 
        
           |  |  | 184 |                 // Get additional scopes from the plugin.
 | 
        
           |  |  | 185 |                 $pluginscopes = $pluginfunction($issuer);
 | 
        
           |  |  | 186 |                 if (empty($pluginscopes)) {
 | 
        
           |  |  | 187 |                     continue;
 | 
        
           |  |  | 188 |                 }
 | 
        
           |  |  | 189 |   | 
        
           |  |  | 190 |                 // Merge the additional scopes with the existing ones.
 | 
        
           |  |  | 191 |                 $additionalscopes = explode(' ', $pluginscopes);
 | 
        
           |  |  | 192 |   | 
        
           |  |  | 193 |                 foreach ($additionalscopes as $scope) {
 | 
        
           |  |  | 194 |                     if (!empty($scope)) {
 | 
        
           |  |  | 195 |                         if (strpos(' ' . $scopes . ' ', ' ' . $scope . ' ') === false) {
 | 
        
           |  |  | 196 |                             $scopes .= ' ' . $scope;
 | 
        
           |  |  | 197 |                         }
 | 
        
           |  |  | 198 |                     }
 | 
        
           |  |  | 199 |                 }
 | 
        
           |  |  | 200 |             }
 | 
        
           |  |  | 201 |         }
 | 
        
           |  |  | 202 |   | 
        
           |  |  | 203 |         return $scopes;
 | 
        
           |  |  | 204 |     }
 | 
        
           |  |  | 205 |   | 
        
           |  |  | 206 |     /**
 | 
        
           |  |  | 207 |      * Get an authenticated oauth2 client using the system account.
 | 
        
           |  |  | 208 |      * This call uses the refresh token to get an access token.
 | 
        
           |  |  | 209 |      *
 | 
        
           |  |  | 210 |      * @param \core\oauth2\issuer $issuer
 | 
        
           |  |  | 211 |      * @return \core\oauth2\client|false An authenticated client (or false if the token could not be upgraded)
 | 
        
           |  |  | 212 |      * @throws moodle_exception Request for token upgrade failed for technical reasons
 | 
        
           |  |  | 213 |      */
 | 
        
           |  |  | 214 |     public static function get_system_oauth_client(issuer $issuer) {
 | 
        
           |  |  | 215 |         $systemaccount = self::get_system_account($issuer);
 | 
        
           |  |  | 216 |         if (empty($systemaccount)) {
 | 
        
           |  |  | 217 |             return false;
 | 
        
           |  |  | 218 |         }
 | 
        
           |  |  | 219 |         // Get all the scopes!
 | 
        
           |  |  | 220 |         $scopes = self::get_system_scopes_for_issuer($issuer);
 | 
        
           |  |  | 221 |         $class = self::get_client_classname($issuer->get('servicetype'));
 | 
        
           |  |  | 222 |         $client = new $class($issuer, null, $scopes, true);
 | 
        
           |  |  | 223 |   | 
        
           |  |  | 224 |         if (!$client->is_logged_in()) {
 | 
        
           |  |  | 225 |             if (!$client->upgrade_refresh_token($systemaccount)) {
 | 
        
           |  |  | 226 |                 return false;
 | 
        
           |  |  | 227 |             }
 | 
        
           |  |  | 228 |         }
 | 
        
           |  |  | 229 |         return $client;
 | 
        
           |  |  | 230 |     }
 | 
        
           |  |  | 231 |   | 
        
           |  |  | 232 |     /**
 | 
        
           |  |  | 233 |      * Get an authenticated oauth2 client using the current user account.
 | 
        
           |  |  | 234 |      * This call does the redirect dance back to the current page after authentication.
 | 
        
           |  |  | 235 |      *
 | 
        
           |  |  | 236 |      * @param \core\oauth2\issuer $issuer The desired OAuth issuer
 | 
        
           |  |  | 237 |      * @param moodle_url $currenturl The url to the current page.
 | 
        
           |  |  | 238 |      * @param string $additionalscopes The additional scopes required for authorization.
 | 
        
           |  |  | 239 |      * @param bool $autorefresh Should the client support the use of refresh tokens to persist access across sessions.
 | 
        
           |  |  | 240 |      * @return \core\oauth2\client
 | 
        
           |  |  | 241 |      */
 | 
        
           |  |  | 242 |     public static function get_user_oauth_client(issuer $issuer, moodle_url $currenturl, $additionalscopes = '',
 | 
        
           |  |  | 243 |             $autorefresh = false) {
 | 
        
           |  |  | 244 |         $class = self::get_client_classname($issuer->get('servicetype'));
 | 
        
           |  |  | 245 |         $client = new $class($issuer, $currenturl, $additionalscopes, false, $autorefresh);
 | 
        
           |  |  | 246 |   | 
        
           |  |  | 247 |         return $client;
 | 
        
           |  |  | 248 |     }
 | 
        
           |  |  | 249 |   | 
        
           |  |  | 250 |     /**
 | 
        
           |  |  | 251 |      * Get the client classname for an issuer.
 | 
        
           |  |  | 252 |      *
 | 
        
           |  |  | 253 |      * @param string $type The OAuth issuer type (google, facebook...).
 | 
        
           |  |  | 254 |      * @return string The classname for the custom client or core client class if the class for the defined type
 | 
        
           |  |  | 255 |      *                 doesn't exist or null type is defined.
 | 
        
           |  |  | 256 |      */
 | 
        
           |  |  | 257 |     protected static function get_client_classname(?string $type): string {
 | 
        
           |  |  | 258 |         // Default core client class.
 | 
        
           |  |  | 259 |         $classname = 'core\\oauth2\\client';
 | 
        
           |  |  | 260 |   | 
        
           |  |  | 261 |         if (!empty($type)) {
 | 
        
           |  |  | 262 |             $typeclassname = 'core\\oauth2\\client\\' . $type;
 | 
        
           |  |  | 263 |             if (class_exists($typeclassname)) {
 | 
        
           |  |  | 264 |                 $classname = $typeclassname;
 | 
        
           |  |  | 265 |             }
 | 
        
           |  |  | 266 |         }
 | 
        
           |  |  | 267 |   | 
        
           |  |  | 268 |         return $classname;
 | 
        
           |  |  | 269 |     }
 | 
        
           |  |  | 270 |   | 
        
           |  |  | 271 |     /**
 | 
        
           |  |  | 272 |      * Get the list of defined endpoints for this OAuth issuer
 | 
        
           |  |  | 273 |      *
 | 
        
           |  |  | 274 |      * @param \core\oauth2\issuer $issuer The desired OAuth issuer
 | 
        
           |  |  | 275 |      * @return \core\oauth2\endpoint[]
 | 
        
           |  |  | 276 |      */
 | 
        
           |  |  | 277 |     public static function get_endpoints(issuer $issuer) {
 | 
        
           |  |  | 278 |         return endpoint::get_records(['issuerid' => $issuer->get('id')]);
 | 
        
           |  |  | 279 |     }
 | 
        
           |  |  | 280 |   | 
        
           |  |  | 281 |     /**
 | 
        
           |  |  | 282 |      * Get the list of defined mapping from OAuth user fields to moodle user fields.
 | 
        
           |  |  | 283 |      *
 | 
        
           |  |  | 284 |      * @param \core\oauth2\issuer $issuer The desired OAuth issuer
 | 
        
           |  |  | 285 |      * @return \core\oauth2\user_field_mapping[]
 | 
        
           |  |  | 286 |      */
 | 
        
           |  |  | 287 |     public static function get_user_field_mappings(issuer $issuer) {
 | 
        
           |  |  | 288 |         return user_field_mapping::get_records(['issuerid' => $issuer->get('id')]);
 | 
        
           |  |  | 289 |     }
 | 
        
           |  |  | 290 |   | 
        
           |  |  | 291 |     /**
 | 
        
           |  |  | 292 |      * Guess an image from the discovery URL.
 | 
        
           |  |  | 293 |      *
 | 
        
           |  |  | 294 |      * @param \core\oauth2\issuer $issuer The desired OAuth issuer
 | 
        
           |  |  | 295 |      */
 | 
        
           |  |  | 296 |     protected static function guess_image($issuer) {
 | 
        
           |  |  | 297 |         if (empty($issuer->get('image')) && !empty($issuer->get('baseurl'))) {
 | 
        
           |  |  | 298 |             $baseurl = parse_url($issuer->get('baseurl'));
 | 
        
           |  |  | 299 |             $imageurl = $baseurl['scheme'] . '://' . $baseurl['host'] . '/favicon.ico';
 | 
        
           |  |  | 300 |             $issuer->set('image', $imageurl);
 | 
        
           |  |  | 301 |             $issuer->update();
 | 
        
           |  |  | 302 |         }
 | 
        
           |  |  | 303 |     }
 | 
        
           |  |  | 304 |   | 
        
           |  |  | 305 |     /**
 | 
        
           |  |  | 306 |      * Take the data from the mform and update the issuer.
 | 
        
           |  |  | 307 |      *
 | 
        
           |  |  | 308 |      * @param stdClass $data
 | 
        
           |  |  | 309 |      * @return \core\oauth2\issuer
 | 
        
           |  |  | 310 |      */
 | 
        
           |  |  | 311 |     public static function update_issuer($data) {
 | 
        
           |  |  | 312 |         return self::create_or_update_issuer($data, false);
 | 
        
           |  |  | 313 |     }
 | 
        
           |  |  | 314 |   | 
        
           |  |  | 315 |     /**
 | 
        
           |  |  | 316 |      * Take the data from the mform and create the issuer.
 | 
        
           |  |  | 317 |      *
 | 
        
           |  |  | 318 |      * @param stdClass $data
 | 
        
           |  |  | 319 |      * @return \core\oauth2\issuer
 | 
        
           |  |  | 320 |      */
 | 
        
           |  |  | 321 |     public static function create_issuer($data) {
 | 
        
           |  |  | 322 |         return self::create_or_update_issuer($data, true);
 | 
        
           |  |  | 323 |     }
 | 
        
           |  |  | 324 |   | 
        
           |  |  | 325 |     /**
 | 
        
           |  |  | 326 |      * Take the data from the mform and create or update the issuer.
 | 
        
           |  |  | 327 |      *
 | 
        
           |  |  | 328 |      * @param stdClass $data Form data for them issuer to be created/updated.
 | 
        
           |  |  | 329 |      * @param bool $create If true, the issuer will be created; otherwise, it will be updated.
 | 
        
           |  |  | 330 |      * @return issuer The created/updated issuer.
 | 
        
           |  |  | 331 |      */
 | 
        
           |  |  | 332 |     protected static function create_or_update_issuer($data, bool $create): issuer {
 | 
        
           |  |  | 333 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 334 |         $issuer = new issuer($data->id ?? 0, $data);
 | 
        
           |  |  | 335 |   | 
        
           |  |  | 336 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 337 |         if ($create) {
 | 
        
           |  |  | 338 |             $issuer->create();
 | 
        
           |  |  | 339 |   | 
        
           |  |  | 340 |             // Perform service discovery.
 | 
        
           |  |  | 341 |             $classname = self::get_service_classname($issuer->get('servicetype'));
 | 
        
           |  |  | 342 |             $classname::discover_endpoints($issuer);
 | 
        
           |  |  | 343 |             self::guess_image($issuer);
 | 
        
           |  |  | 344 |         } else {
 | 
        
           |  |  | 345 |             $issuer->update();
 | 
        
           |  |  | 346 |         }
 | 
        
           |  |  | 347 |   | 
        
           |  |  | 348 |         return $issuer;
 | 
        
           |  |  | 349 |     }
 | 
        
           |  |  | 350 |   | 
        
           |  |  | 351 |     /**
 | 
        
           |  |  | 352 |      * Get the service classname for an issuer.
 | 
        
           |  |  | 353 |      *
 | 
        
           |  |  | 354 |      * @param string $type The OAuth issuer type (google, facebook...).
 | 
        
           |  |  | 355 |      *
 | 
        
           |  |  | 356 |      * @return string The classname for this issuer or "Custom" service class if the class for the defined type doesn't exist
 | 
        
           |  |  | 357 |      *                 or null type is defined.
 | 
        
           |  |  | 358 |      */
 | 
        
           |  |  | 359 |     protected static function get_service_classname(?string $type): string {
 | 
        
           |  |  | 360 |         // Default custom service class.
 | 
        
           |  |  | 361 |         $classname = 'core\\oauth2\\service\\custom';
 | 
        
           |  |  | 362 |   | 
        
           |  |  | 363 |         if (!empty($type)) {
 | 
        
           |  |  | 364 |             $typeclassname = 'core\\oauth2\\service\\' . $type;
 | 
        
           |  |  | 365 |             if (class_exists($typeclassname)) {
 | 
        
           |  |  | 366 |                 $classname = $typeclassname;
 | 
        
           |  |  | 367 |             }
 | 
        
           |  |  | 368 |         }
 | 
        
           |  |  | 369 |   | 
        
           |  |  | 370 |         return $classname;
 | 
        
           |  |  | 371 |     }
 | 
        
           |  |  | 372 |   | 
        
           |  |  | 373 |     /**
 | 
        
           |  |  | 374 |      * Take the data from the mform and update the endpoint.
 | 
        
           |  |  | 375 |      *
 | 
        
           |  |  | 376 |      * @param stdClass $data
 | 
        
           |  |  | 377 |      * @return \core\oauth2\endpoint
 | 
        
           |  |  | 378 |      */
 | 
        
           |  |  | 379 |     public static function update_endpoint($data) {
 | 
        
           |  |  | 380 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 381 |         $endpoint = new endpoint(0, $data);
 | 
        
           |  |  | 382 |   | 
        
           |  |  | 383 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 384 |         $endpoint->update();
 | 
        
           |  |  | 385 |   | 
        
           |  |  | 386 |         return $endpoint;
 | 
        
           |  |  | 387 |     }
 | 
        
           |  |  | 388 |   | 
        
           |  |  | 389 |     /**
 | 
        
           |  |  | 390 |      * Take the data from the mform and create the endpoint.
 | 
        
           |  |  | 391 |      *
 | 
        
           |  |  | 392 |      * @param stdClass $data
 | 
        
           |  |  | 393 |      * @return \core\oauth2\endpoint
 | 
        
           |  |  | 394 |      */
 | 
        
           |  |  | 395 |     public static function create_endpoint($data) {
 | 
        
           |  |  | 396 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 397 |         $endpoint = new endpoint(0, $data);
 | 
        
           |  |  | 398 |   | 
        
           |  |  | 399 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 400 |         $endpoint->create();
 | 
        
           |  |  | 401 |         return $endpoint;
 | 
        
           |  |  | 402 |     }
 | 
        
           |  |  | 403 |   | 
        
           |  |  | 404 |     /**
 | 
        
           |  |  | 405 |      * Take the data from the mform and update the user field mapping.
 | 
        
           |  |  | 406 |      *
 | 
        
           |  |  | 407 |      * @param stdClass $data
 | 
        
           |  |  | 408 |      * @return \core\oauth2\user_field_mapping
 | 
        
           |  |  | 409 |      */
 | 
        
           |  |  | 410 |     public static function update_user_field_mapping($data) {
 | 
        
           |  |  | 411 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 412 |         $userfieldmapping = new user_field_mapping(0, $data);
 | 
        
           |  |  | 413 |   | 
        
           |  |  | 414 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 415 |         $userfieldmapping->update();
 | 
        
           |  |  | 416 |   | 
        
           |  |  | 417 |         return $userfieldmapping;
 | 
        
           |  |  | 418 |     }
 | 
        
           |  |  | 419 |   | 
        
           |  |  | 420 |     /**
 | 
        
           |  |  | 421 |      * Take the data from the mform and create the user field mapping.
 | 
        
           |  |  | 422 |      *
 | 
        
           |  |  | 423 |      * @param stdClass $data
 | 
        
           |  |  | 424 |      * @return \core\oauth2\user_field_mapping
 | 
        
           |  |  | 425 |      */
 | 
        
           |  |  | 426 |     public static function create_user_field_mapping($data) {
 | 
        
           |  |  | 427 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 428 |         $userfieldmapping = new user_field_mapping(0, $data);
 | 
        
           |  |  | 429 |   | 
        
           |  |  | 430 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 431 |         $userfieldmapping->create();
 | 
        
           |  |  | 432 |         return $userfieldmapping;
 | 
        
           |  |  | 433 |     }
 | 
        
           |  |  | 434 |   | 
        
           |  |  | 435 |     /**
 | 
        
           |  |  | 436 |      * Reorder this identity issuer.
 | 
        
           |  |  | 437 |      *
 | 
        
           |  |  | 438 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 439 |      *
 | 
        
           |  |  | 440 |      * @param int $id The id of the identity issuer to move.
 | 
        
           |  |  | 441 |      * @return boolean
 | 
        
           |  |  | 442 |      */
 | 
        
           |  |  | 443 |     public static function move_up_issuer($id) {
 | 
        
           |  |  | 444 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 445 |         $current = new issuer($id);
 | 
        
           |  |  | 446 |   | 
        
           |  |  | 447 |         $sortorder = $current->get('sortorder');
 | 
        
           |  |  | 448 |         if ($sortorder == 0) {
 | 
        
           |  |  | 449 |             return false;
 | 
        
           |  |  | 450 |         }
 | 
        
           |  |  | 451 |   | 
        
           |  |  | 452 |         $sortorder = $sortorder - 1;
 | 
        
           |  |  | 453 |         $current->set('sortorder', $sortorder);
 | 
        
           |  |  | 454 |   | 
        
           |  |  | 455 |         $filters = array('sortorder' => $sortorder);
 | 
        
           |  |  | 456 |         $children = issuer::get_records($filters, 'id');
 | 
        
           |  |  | 457 |         foreach ($children as $needtoswap) {
 | 
        
           |  |  | 458 |             $needtoswap->set('sortorder', $sortorder + 1);
 | 
        
           |  |  | 459 |             $needtoswap->update();
 | 
        
           |  |  | 460 |         }
 | 
        
           |  |  | 461 |   | 
        
           |  |  | 462 |         // OK - all set.
 | 
        
           |  |  | 463 |         $result = $current->update();
 | 
        
           |  |  | 464 |   | 
        
           |  |  | 465 |         return $result;
 | 
        
           |  |  | 466 |     }
 | 
        
           |  |  | 467 |   | 
        
           |  |  | 468 |     /**
 | 
        
           |  |  | 469 |      * Reorder this identity issuer.
 | 
        
           |  |  | 470 |      *
 | 
        
           |  |  | 471 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 472 |      *
 | 
        
           |  |  | 473 |      * @param int $id The id of the identity issuer to move.
 | 
        
           |  |  | 474 |      * @return boolean
 | 
        
           |  |  | 475 |      */
 | 
        
           |  |  | 476 |     public static function move_down_issuer($id) {
 | 
        
           |  |  | 477 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 478 |         $current = new issuer($id);
 | 
        
           |  |  | 479 |   | 
        
           |  |  | 480 |         $max = issuer::count_records();
 | 
        
           |  |  | 481 |         if ($max > 0) {
 | 
        
           |  |  | 482 |             $max--;
 | 
        
           |  |  | 483 |         }
 | 
        
           |  |  | 484 |   | 
        
           |  |  | 485 |         $sortorder = $current->get('sortorder');
 | 
        
           |  |  | 486 |         if ($sortorder >= $max) {
 | 
        
           |  |  | 487 |             return false;
 | 
        
           |  |  | 488 |         }
 | 
        
           |  |  | 489 |         $sortorder = $sortorder + 1;
 | 
        
           |  |  | 490 |         $current->set('sortorder', $sortorder);
 | 
        
           |  |  | 491 |   | 
        
           |  |  | 492 |         $filters = array('sortorder' => $sortorder);
 | 
        
           |  |  | 493 |         $children = issuer::get_records($filters);
 | 
        
           |  |  | 494 |         foreach ($children as $needtoswap) {
 | 
        
           |  |  | 495 |             $needtoswap->set('sortorder', $sortorder - 1);
 | 
        
           |  |  | 496 |             $needtoswap->update();
 | 
        
           |  |  | 497 |         }
 | 
        
           |  |  | 498 |   | 
        
           |  |  | 499 |         // OK - all set.
 | 
        
           |  |  | 500 |         $result = $current->update();
 | 
        
           |  |  | 501 |   | 
        
           |  |  | 502 |         return $result;
 | 
        
           |  |  | 503 |     }
 | 
        
           |  |  | 504 |   | 
        
           |  |  | 505 |     /**
 | 
        
           |  |  | 506 |      * Disable an identity issuer.
 | 
        
           |  |  | 507 |      *
 | 
        
           |  |  | 508 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 509 |      *
 | 
        
           |  |  | 510 |      * @param int $id The id of the identity issuer to disable.
 | 
        
           |  |  | 511 |      * @return boolean
 | 
        
           |  |  | 512 |      */
 | 
        
           |  |  | 513 |     public static function disable_issuer($id) {
 | 
        
           |  |  | 514 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 515 |         $issuer = new issuer($id);
 | 
        
           |  |  | 516 |   | 
        
           |  |  | 517 |         $issuer->set('enabled', 0);
 | 
        
           |  |  | 518 |         return $issuer->update();
 | 
        
           |  |  | 519 |     }
 | 
        
           |  |  | 520 |   | 
        
           |  |  | 521 |   | 
        
           |  |  | 522 |     /**
 | 
        
           |  |  | 523 |      * Enable an identity issuer.
 | 
        
           |  |  | 524 |      *
 | 
        
           |  |  | 525 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 526 |      *
 | 
        
           |  |  | 527 |      * @param int $id The id of the identity issuer to enable.
 | 
        
           |  |  | 528 |      * @return boolean
 | 
        
           |  |  | 529 |      */
 | 
        
           |  |  | 530 |     public static function enable_issuer($id) {
 | 
        
           |  |  | 531 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 532 |         $issuer = new issuer($id);
 | 
        
           |  |  | 533 |   | 
        
           |  |  | 534 |         $issuer->set('enabled', 1);
 | 
        
           |  |  | 535 |         return $issuer->update();
 | 
        
           |  |  | 536 |     }
 | 
        
           |  |  | 537 |   | 
        
           |  |  | 538 |     /**
 | 
        
           |  |  | 539 |      * Delete an identity issuer.
 | 
        
           |  |  | 540 |      *
 | 
        
           |  |  | 541 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 542 |      *
 | 
        
           |  |  | 543 |      * @param int $id The id of the identity issuer to delete.
 | 
        
           |  |  | 544 |      * @return boolean
 | 
        
           |  |  | 545 |      */
 | 
        
           |  |  | 546 |     public static function delete_issuer($id) {
 | 
        
           |  |  | 547 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 548 |         $issuer = new issuer($id);
 | 
        
           |  |  | 549 |   | 
        
           |  |  | 550 |         $systemaccount = self::get_system_account($issuer);
 | 
        
           |  |  | 551 |         if ($systemaccount) {
 | 
        
           |  |  | 552 |             $systemaccount->delete();
 | 
        
           |  |  | 553 |         }
 | 
        
           |  |  | 554 |         $endpoints = self::get_endpoints($issuer);
 | 
        
           |  |  | 555 |         if ($endpoints) {
 | 
        
           |  |  | 556 |             foreach ($endpoints as $endpoint) {
 | 
        
           |  |  | 557 |                 $endpoint->delete();
 | 
        
           |  |  | 558 |             }
 | 
        
           |  |  | 559 |         }
 | 
        
           |  |  | 560 |   | 
        
           |  |  | 561 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 562 |         return $issuer->delete();
 | 
        
           |  |  | 563 |     }
 | 
        
           |  |  | 564 |   | 
        
           |  |  | 565 |     /**
 | 
        
           |  |  | 566 |      * Delete an endpoint.
 | 
        
           |  |  | 567 |      *
 | 
        
           |  |  | 568 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 569 |      *
 | 
        
           |  |  | 570 |      * @param int $id The id of the endpoint to delete.
 | 
        
           |  |  | 571 |      * @return boolean
 | 
        
           |  |  | 572 |      */
 | 
        
           |  |  | 573 |     public static function delete_endpoint($id) {
 | 
        
           |  |  | 574 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 575 |         $endpoint = new endpoint($id);
 | 
        
           |  |  | 576 |   | 
        
           |  |  | 577 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 578 |         return $endpoint->delete();
 | 
        
           |  |  | 579 |     }
 | 
        
           |  |  | 580 |   | 
        
           |  |  | 581 |     /**
 | 
        
           |  |  | 582 |      * Delete a user_field_mapping.
 | 
        
           |  |  | 583 |      *
 | 
        
           |  |  | 584 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 585 |      *
 | 
        
           |  |  | 586 |      * @param int $id The id of the user_field_mapping to delete.
 | 
        
           |  |  | 587 |      * @return boolean
 | 
        
           |  |  | 588 |      */
 | 
        
           |  |  | 589 |     public static function delete_user_field_mapping($id) {
 | 
        
           |  |  | 590 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 591 |         $userfieldmapping = new user_field_mapping($id);
 | 
        
           |  |  | 592 |   | 
        
           |  |  | 593 |         // Will throw exceptions on validation failures.
 | 
        
           |  |  | 594 |         return $userfieldmapping->delete();
 | 
        
           |  |  | 595 |     }
 | 
        
           |  |  | 596 |   | 
        
           |  |  | 597 |     /**
 | 
        
           |  |  | 598 |      * Perform the OAuth dance and get a refresh token.
 | 
        
           |  |  | 599 |      *
 | 
        
           |  |  | 600 |      * Requires moodle/site:config capability at the system context.
 | 
        
           |  |  | 601 |      *
 | 
        
           |  |  | 602 |      * @param \core\oauth2\issuer $issuer
 | 
        
           |  |  | 603 |      * @param moodle_url $returnurl The url to the current page (we will be redirected back here after authentication).
 | 
        
           |  |  | 604 |      * @return boolean
 | 
        
           |  |  | 605 |      */
 | 
        
           |  |  | 606 |     public static function connect_system_account($issuer, $returnurl) {
 | 
        
           |  |  | 607 |         require_capability('moodle/site:config', context_system::instance());
 | 
        
           |  |  | 608 |   | 
        
           |  |  | 609 |         // We need to authenticate with an oauth 2 client AS a system user and get a refresh token for offline access.
 | 
        
           |  |  | 610 |         $scopes = self::get_system_scopes_for_issuer($issuer);
 | 
        
           |  |  | 611 |   | 
        
           |  |  | 612 |         // Allow callbacks to inject non-standard scopes to the auth request.
 | 
        
           |  |  | 613 |         $class = self::get_client_classname($issuer->get('servicetype'));
 | 
        
           |  |  | 614 |         $client = new $class($issuer, $returnurl, $scopes, true);
 | 
        
           |  |  | 615 |   | 
        
           |  |  | 616 |         if (!optional_param('response', false, PARAM_BOOL)) {
 | 
        
           |  |  | 617 |             $client->log_out();
 | 
        
           |  |  | 618 |         }
 | 
        
           |  |  | 619 |   | 
        
           |  |  | 620 |         if (optional_param('error', '', PARAM_RAW)) {
 | 
        
           |  |  | 621 |             return false;
 | 
        
           |  |  | 622 |         }
 | 
        
           |  |  | 623 |   | 
        
           |  |  | 624 |         if (!$client->is_logged_in()) {
 | 
        
           |  |  | 625 |             redirect($client->get_login_url());
 | 
        
           |  |  | 626 |         }
 | 
        
           |  |  | 627 |   | 
        
           |  |  | 628 |         $refreshtoken = $client->get_refresh_token();
 | 
        
           |  |  | 629 |         if (!$refreshtoken) {
 | 
        
           |  |  | 630 |             return false;
 | 
        
           |  |  | 631 |         }
 | 
        
           |  |  | 632 |   | 
        
           |  |  | 633 |         $systemaccount = self::get_system_account($issuer);
 | 
        
           |  |  | 634 |         if ($systemaccount) {
 | 
        
           |  |  | 635 |             $systemaccount->delete();
 | 
        
           |  |  | 636 |         }
 | 
        
           |  |  | 637 |   | 
        
           |  |  | 638 |         $userinfo = $client->get_userinfo();
 | 
        
           |  |  | 639 |   | 
        
           |  |  | 640 |         $record = new stdClass();
 | 
        
           |  |  | 641 |         $record->issuerid = $issuer->get('id');
 | 
        
           |  |  | 642 |         $record->refreshtoken = $refreshtoken;
 | 
        
           |  |  | 643 |         $record->grantedscopes = $scopes;
 | 
        
           | 1441 | ariadna | 644 |         // Get email.
 | 
        
           |  |  | 645 |         if (isset($userinfo['email'])) {
 | 
        
           |  |  | 646 |             $record->email = $userinfo['email'];
 | 
        
           |  |  | 647 |         } else if ($issuer->get_system_email()) {
 | 
        
           |  |  | 648 |             $record->email = $issuer->get_system_email();
 | 
        
           |  |  | 649 |         } else {
 | 
        
           |  |  | 650 |             $record->email = '';
 | 
        
           |  |  | 651 |         }
 | 
        
           |  |  | 652 |         // Get username.
 | 
        
           |  |  | 653 |         if (isset($userinfo['username'])) {
 | 
        
           |  |  | 654 |             $record->username = $userinfo['username'];
 | 
        
           |  |  | 655 |         } else if ($issuer->get_system_email()) {
 | 
        
           |  |  | 656 |             $record->username = $issuer->get_system_email();
 | 
        
           |  |  | 657 |         }
 | 
        
           | 1 | efrain | 658 |   | 
        
           |  |  | 659 |         $systemaccount = new system_account(0, $record);
 | 
        
           |  |  | 660 |   | 
        
           |  |  | 661 |         $systemaccount->create();
 | 
        
           |  |  | 662 |   | 
        
           |  |  | 663 |         $client->log_out();
 | 
        
           |  |  | 664 |         return true;
 | 
        
           |  |  | 665 |     }
 | 
        
           |  |  | 666 | }
 |