See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 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 context_system; 31 use curl; 32 use stdClass; 33 use moodle_exception; 34 use moodle_url; 35 36 37 /** 38 * Static list of api methods for system oauth2 configuration. 39 * 40 * @copyright 2017 Damyon Wiese 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class api { 44 45 /** 46 * Build a google ready OAuth 2 service. 47 * @return \core\oauth2\issuer 48 */ 49 private static function init_google() { 50 $record = (object) [ 51 'name' => 'Google', 52 'image' => 'https://accounts.google.com/favicon.ico', 53 'baseurl' => 'https://accounts.google.com/', 54 'loginparamsoffline' => 'access_type=offline&prompt=consent', 55 'showonloginpage' => true 56 ]; 57 58 $issuer = new issuer(0, $record); 59 return $issuer; 60 } 61 62 /** 63 * Create endpoints for google issuers. 64 * @param issuer $issuer issuer the endpoints should be created for. 65 * @return mixed 66 * @throws \coding_exception 67 * @throws \core\invalid_persistent_exception 68 */ 69 private static function create_endpoints_for_google($issuer) { 70 71 $record = (object) [ 72 'issuerid' => $issuer->get('id'), 73 'name' => 'discovery_endpoint', 74 'url' => 'https://accounts.google.com/.well-known/openid-configuration' 75 ]; 76 $endpoint = new endpoint(0, $record); 77 $endpoint->create(); 78 return $issuer; 79 } 80 81 /** 82 * Build a facebook ready OAuth 2 service. 83 * @return \core\oauth2\issuer 84 */ 85 private static function init_facebook() { 86 // Facebook is a custom setup. 87 $record = (object) [ 88 'name' => 'Facebook', 89 'image' => 'https://facebookbrand.com/wp-content/uploads/2016/05/flogo_rgb_hex-brc-site-250.png', 90 'baseurl' => '', 91 'loginscopes' => 'public_profile email', 92 'loginscopesoffline' => 'public_profile email', 93 'showonloginpage' => true 94 ]; 95 96 $issuer = new issuer(0, $record); 97 return $issuer; 98 } 99 100 /** 101 * Create endpoints for facebook issuers. 102 * @param issuer $issuer issuer the endpoints should be created for. 103 * @return mixed 104 * @throws \coding_exception 105 * @throws \core\invalid_persistent_exception 106 */ 107 private static function create_endpoints_for_facebook($issuer) { 108 // The Facebook API version. 109 $apiversion = '2.12'; 110 // The Graph API URL. 111 $graphurl = 'https://graph.facebook.com/v' . $apiversion; 112 // User information fields that we want to fetch. 113 $infofields = [ 114 'id', 115 'first_name', 116 'last_name', 117 'link', 118 'picture.type(large)', 119 'name', 120 'email', 121 ]; 122 $endpoints = [ 123 'authorization_endpoint' => sprintf('https://www.facebook.com/v%s/dialog/oauth', $apiversion), 124 'token_endpoint' => $graphurl . '/oauth/access_token', 125 'userinfo_endpoint' => $graphurl . '/me?fields=' . implode(',', $infofields) 126 ]; 127 128 foreach ($endpoints as $name => $url) { 129 $record = (object) [ 130 'issuerid' => $issuer->get('id'), 131 'name' => $name, 132 'url' => $url 133 ]; 134 $endpoint = new endpoint(0, $record); 135 $endpoint->create(); 136 } 137 138 // Create the field mappings. 139 $mapping = [ 140 'name' => 'alternatename', 141 'last_name' => 'lastname', 142 'email' => 'email', 143 'first_name' => 'firstname', 144 'picture-data-url' => 'picture', 145 'link' => 'url', 146 ]; 147 foreach ($mapping as $external => $internal) { 148 $record = (object) [ 149 'issuerid' => $issuer->get('id'), 150 'externalfield' => $external, 151 'internalfield' => $internal 152 ]; 153 $userfieldmapping = new user_field_mapping(0, $record); 154 $userfieldmapping->create(); 155 } 156 return $issuer; 157 } 158 159 /** 160 * Build a microsoft ready OAuth 2 service. 161 * @return \core\oauth2\issuer 162 */ 163 private static function init_microsoft() { 164 // Microsoft is a custom setup. 165 $record = (object) [ 166 'name' => 'Microsoft', 167 'image' => 'https://www.microsoft.com/favicon.ico', 168 'baseurl' => '', 169 'loginscopes' => 'openid profile email user.read', 170 'loginscopesoffline' => 'openid profile email user.read offline_access', 171 'showonloginpage' => true 172 ]; 173 174 $issuer = new issuer(0, $record); 175 return $issuer; 176 } 177 178 /** 179 * Create endpoints for microsoft issuers. 180 * @param issuer $issuer issuer the endpoints should be created for. 181 * @return mixed 182 * @throws \coding_exception 183 * @throws \core\invalid_persistent_exception 184 */ 185 private static function create_endpoints_for_microsoft($issuer) { 186 187 $endpoints = [ 188 'authorization_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', 189 'token_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token', 190 'userinfo_endpoint' => 'https://graph.microsoft.com/v1.0/me/', 191 'userpicture_endpoint' => 'https://graph.microsoft.com/v1.0/me/photo/$value', 192 ]; 193 194 foreach ($endpoints as $name => $url) { 195 $record = (object) [ 196 'issuerid' => $issuer->get('id'), 197 'name' => $name, 198 'url' => $url 199 ]; 200 $endpoint = new endpoint(0, $record); 201 $endpoint->create(); 202 } 203 204 // Create the field mappings. 205 $mapping = [ 206 'givenName' => 'firstname', 207 'surname' => 'lastname', 208 'userPrincipalName' => 'email', 209 'displayName' => 'alternatename', 210 'officeLocation' => 'address', 211 'mobilePhone' => 'phone1', 212 'preferredLanguage' => 'lang' 213 ]; 214 foreach ($mapping as $external => $internal) { 215 $record = (object) [ 216 'issuerid' => $issuer->get('id'), 217 'externalfield' => $external, 218 'internalfield' => $internal 219 ]; 220 $userfieldmapping = new user_field_mapping(0, $record); 221 $userfieldmapping->create(); 222 } 223 return $issuer; 224 } 225 226 /** 227 * Build a nextcloud ready OAuth 2 service. 228 * @return \core\oauth2\issuer 229 */ 230 private static function init_nextcloud() { 231 // Nextcloud has a custom baseurl. Thus, the creation of endpoints has to be done later. 232 $record = (object) [ 233 'name' => 'Nextcloud', 234 'image' => 'https://nextcloud.com/wp-content/themes/next/assets/img/common/favicon.png?x16328', 235 'basicauth' => 1, 236 ]; 237 238 $issuer = new issuer(0, $record); 239 240 return $issuer; 241 } 242 243 /** 244 * Create endpoints for nextcloud issuers. 245 * @param issuer $issuer issuer the endpoints should be created for. 246 * @return mixed 247 * @throws \coding_exception 248 * @throws \core\invalid_persistent_exception 249 */ 250 private static function create_endpoints_for_nextcloud($issuer) { 251 $baseurl = $issuer->get('baseurl'); 252 // Add trailing slash to baseurl, if needed. 253 if (substr($baseurl, -1) !== '/') { 254 $baseurl .= '/'; 255 } 256 257 $endpoints = [ 258 // Baseurl will be prepended later. 259 'authorization_endpoint' => 'index.php/apps/oauth2/authorize', 260 'token_endpoint' => 'index.php/apps/oauth2/api/v1/token', 261 'userinfo_endpoint' => 'ocs/v2.php/cloud/user?format=json', 262 'webdav_endpoint' => 'remote.php/webdav/', 263 'ocs_endpoint' => 'ocs/v1.php/apps/files_sharing/api/v1/shares', 264 ]; 265 266 foreach ($endpoints as $name => $url) { 267 $record = (object) [ 268 'issuerid' => $issuer->get('id'), 269 'name' => $name, 270 'url' => $baseurl . $url, 271 ]; 272 $endpoint = new \core\oauth2\endpoint(0, $record); 273 $endpoint->create(); 274 } 275 276 // Create the field mappings. 277 $mapping = [ 278 'ocs-data-email' => 'email', 279 'ocs-data-id' => 'username', 280 ]; 281 foreach ($mapping as $external => $internal) { 282 $record = (object) [ 283 'issuerid' => $issuer->get('id'), 284 'externalfield' => $external, 285 'internalfield' => $internal 286 ]; 287 $userfieldmapping = new \core\oauth2\user_field_mapping(0, $record); 288 $userfieldmapping->create(); 289 } 290 } 291 292 /** 293 * Initializes a record for one of the standard issuers to be displayed in the settings. 294 * The issuer is not yet created in the database. 295 * @param string $type One of google, facebook, microsoft, nextcloud 296 * @return \core\oauth2\issuer 297 */ 298 public static function init_standard_issuer($type) { 299 require_capability('moodle/site:config', context_system::instance()); 300 if ($type == 'google') { 301 return self::init_google(); 302 } else if ($type == 'microsoft') { 303 return self::init_microsoft(); 304 } else if ($type == 'facebook') { 305 return self::init_facebook(); 306 } else if ($type == 'nextcloud') { 307 return self::init_nextcloud(); 308 } else { 309 throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); 310 } 311 } 312 313 /** 314 * Create endpoints for standard issuers, based on the issuer created from submitted data. 315 * @param string $type One of google, facebook, microsoft, nextcloud 316 * @param issuer $issuer issuer the endpoints should be created for. 317 * @return \core\oauth2\issuer 318 */ 319 public static function create_endpoints_for_standard_issuer($type, $issuer) { 320 require_capability('moodle/site:config', context_system::instance()); 321 if ($type == 'google') { 322 $issuer = self::create_endpoints_for_google($issuer); 323 self::discover_endpoints($issuer); 324 return $issuer; 325 } else if ($type == 'microsoft') { 326 return self::create_endpoints_for_microsoft($issuer); 327 } else if ($type == 'facebook') { 328 return self::create_endpoints_for_facebook($issuer); 329 } else if ($type == 'nextcloud') { 330 return self::create_endpoints_for_nextcloud($issuer); 331 } else { 332 throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); 333 } 334 } 335 336 /** 337 * Create one of the standard issuers. 338 * @param string $type One of google, facebook, microsoft, or nextcloud 339 * @param string|false $baseurl Baseurl (only required for nextcloud) 340 * @return \core\oauth2\issuer 341 */ 342 public static function create_standard_issuer($type, $baseurl = false) { 343 require_capability('moodle/site:config', context_system::instance()); 344 if ($type == 'google') { 345 $issuer = self::init_google(); 346 $issuer->create(); 347 return self::create_endpoints_for_google($issuer); 348 } else if ($type == 'microsoft') { 349 $issuer = self::init_microsoft(); 350 $issuer->create(); 351 return self::create_endpoints_for_microsoft($issuer); 352 } else if ($type == 'facebook') { 353 $issuer = self::init_facebook(); 354 $issuer->create(); 355 return self::create_endpoints_for_facebook($issuer); 356 } else if ($type == 'nextcloud') { 357 if (!$baseurl) { 358 throw new moodle_exception('Nextcloud service type requires the baseurl parameter.'); 359 } 360 $issuer = self::init_nextcloud(); 361 $issuer->set('baseurl', $baseurl); 362 $issuer->create(); 363 return self::create_endpoints_for_nextcloud($issuer); 364 } else { 365 throw new moodle_exception('OAuth 2 service type not recognised: ' . $type); 366 } 367 } 368 369 370 /** 371 * List all the issuers, ordered by the sortorder field 372 * @return \core\oauth2\issuer[] 373 */ 374 public static function get_all_issuers() { 375 return issuer::get_records([], 'sortorder'); 376 } 377 378 /** 379 * Get a single issuer by id. 380 * 381 * @param int $id 382 * @return \core\oauth2\issuer 383 */ 384 public static function get_issuer($id) { 385 return new issuer($id); 386 } 387 388 /** 389 * Get a single endpoint by id. 390 * 391 * @param int $id 392 * @return \core\oauth2\endpoint 393 */ 394 public static function get_endpoint($id) { 395 return new endpoint($id); 396 } 397 398 /** 399 * Get a single user field mapping by id. 400 * 401 * @param int $id 402 * @return \core\oauth2\user_field_mapping 403 */ 404 public static function get_user_field_mapping($id) { 405 return new user_field_mapping($id); 406 } 407 408 /** 409 * Get the system account for an installed OAuth service. 410 * Never ever ever expose this to a webservice because it contains the refresh token which grants API access. 411 * 412 * @param \core\oauth2\issuer $issuer 413 * @return system_account|false 414 */ 415 public static function get_system_account(issuer $issuer) { 416 return system_account::get_record(['issuerid' => $issuer->get('id')]); 417 } 418 419 /** 420 * Get the full list of system scopes required by an oauth issuer. 421 * This includes the list required for login as well as any scopes injected by the oauth2_system_scopes callback in plugins. 422 * 423 * @param \core\oauth2\issuer $issuer 424 * @return string 425 */ 426 public static function get_system_scopes_for_issuer($issuer) { 427 $scopes = $issuer->get('loginscopesoffline'); 428 429 $pluginsfunction = get_plugins_with_function('oauth2_system_scopes', 'lib.php'); 430 foreach ($pluginsfunction as $plugintype => $plugins) { 431 foreach ($plugins as $pluginfunction) { 432 // Get additional scopes from the plugin. 433 $pluginscopes = $pluginfunction($issuer); 434 if (empty($pluginscopes)) { 435 continue; 436 } 437 438 // Merge the additional scopes with the existing ones. 439 $additionalscopes = explode(' ', $pluginscopes); 440 441 foreach ($additionalscopes as $scope) { 442 if (!empty($scope)) { 443 if (strpos(' ' . $scopes . ' ', ' ' . $scope . ' ') === false) { 444 $scopes .= ' ' . $scope; 445 } 446 } 447 } 448 } 449 } 450 451 return $scopes; 452 } 453 454 /** 455 * Get an authenticated oauth2 client using the system account. 456 * This call uses the refresh token to get an access token. 457 * 458 * @param \core\oauth2\issuer $issuer 459 * @return \core\oauth2\client|false An authenticated client (or false if the token could not be upgraded) 460 * @throws moodle_exception Request for token upgrade failed for technical reasons 461 */ 462 public static function get_system_oauth_client(issuer $issuer) { 463 $systemaccount = self::get_system_account($issuer); 464 if (empty($systemaccount)) { 465 return false; 466 } 467 // Get all the scopes! 468 $scopes = self::get_system_scopes_for_issuer($issuer); 469 470 $client = new \core\oauth2\client($issuer, null, $scopes, true); 471 472 if (!$client->is_logged_in()) { 473 if (!$client->upgrade_refresh_token($systemaccount)) { 474 return false; 475 } 476 } 477 return $client; 478 } 479 480 /** 481 * Get an authenticated oauth2 client using the current user account. 482 * This call does the redirect dance back to the current page after authentication. 483 * 484 * @param \core\oauth2\issuer $issuer The desired OAuth issuer 485 * @param moodle_url $currenturl The url to the current page. 486 * @param string $additionalscopes The additional scopes required for authorization. 487 * @return \core\oauth2\client 488 */ 489 public static function get_user_oauth_client(issuer $issuer, moodle_url $currenturl, $additionalscopes = '') { 490 $client = new \core\oauth2\client($issuer, $currenturl, $additionalscopes); 491 492 return $client; 493 } 494 495 /** 496 * Get the list of defined endpoints for this OAuth issuer 497 * 498 * @param \core\oauth2\issuer $issuer The desired OAuth issuer 499 * @return \core\oauth2\endpoint[] 500 */ 501 public static function get_endpoints(issuer $issuer) { 502 return endpoint::get_records(['issuerid' => $issuer->get('id')]); 503 } 504 505 /** 506 * Get the list of defined mapping from OAuth user fields to moodle user fields. 507 * 508 * @param \core\oauth2\issuer $issuer The desired OAuth issuer 509 * @return \core\oauth2\user_field_mapping[] 510 */ 511 public static function get_user_field_mappings(issuer $issuer) { 512 return user_field_mapping::get_records(['issuerid' => $issuer->get('id')]); 513 } 514 515 /** 516 * Guess an image from the discovery URL. 517 * 518 * @param \core\oauth2\issuer $issuer The desired OAuth issuer 519 */ 520 protected static function guess_image($issuer) { 521 if (empty($issuer->get('image')) && !empty($issuer->get('baseurl'))) { 522 $baseurl = parse_url($issuer->get('baseurl')); 523 $imageurl = $baseurl['scheme'] . '://' . $baseurl['host'] . '/favicon.ico'; 524 $issuer->set('image', $imageurl); 525 $issuer->update(); 526 } 527 } 528 529 /** 530 * If the discovery endpoint exists for this issuer, try and determine the list of valid endpoints. 531 * 532 * @param issuer $issuer 533 * @return int The number of discovered services. 534 */ 535 protected static function discover_endpoints($issuer) { 536 $curl = new curl(); 537 538 if (empty($issuer->get('baseurl'))) { 539 return 0; 540 } 541 542 $url = $issuer->get_endpoint_url('discovery'); 543 if (!$url) { 544 $url = $issuer->get('baseurl') . '/.well-known/openid-configuration'; 545 } 546 547 if (!$json = $curl->get($url)) { 548 $msg = 'Could not discover end points for identity issuer' . $issuer->get('name'); 549 throw new moodle_exception($msg); 550 } 551 552 if ($msg = $curl->error) { 553 throw new moodle_exception('Could not discover service endpoints: ' . $msg); 554 } 555 556 $info = json_decode($json); 557 if (empty($info)) { 558 $msg = 'Could not discover end points for identity issuer' . $issuer->get('name'); 559 throw new moodle_exception($msg); 560 } 561 562 foreach (endpoint::get_records(['issuerid' => $issuer->get('id')]) as $endpoint) { 563 if ($endpoint->get('name') != 'discovery_endpoint') { 564 $endpoint->delete(); 565 } 566 } 567 568 foreach ($info as $key => $value) { 569 if (substr_compare($key, '_endpoint', - strlen('_endpoint')) === 0) { 570 $record = new stdClass(); 571 $record->issuerid = $issuer->get('id'); 572 $record->name = $key; 573 $record->url = $value; 574 575 $endpoint = new endpoint(0, $record); 576 $endpoint->create(); 577 } 578 579 if ($key == 'scopes_supported') { 580 $issuer->set('scopessupported', implode(' ', $value)); 581 $issuer->update(); 582 } 583 } 584 585 // We got to here - must be a decent OpenID connect service. Add the default user field mapping list. 586 foreach (user_field_mapping::get_records(['issuerid' => $issuer->get('id')]) as $userfieldmapping) { 587 $userfieldmapping->delete(); 588 } 589 590 // Create the field mappings. 591 $mapping = [ 592 'given_name' => 'firstname', 593 'middle_name' => 'middlename', 594 'family_name' => 'lastname', 595 'email' => 'email', 596 'website' => 'url', 597 'nickname' => 'alternatename', 598 'picture' => 'picture', 599 'address' => 'address', 600 'phone' => 'phone1', 601 'locale' => 'lang' 602 ]; 603 foreach ($mapping as $external => $internal) { 604 $record = (object) [ 605 'issuerid' => $issuer->get('id'), 606 'externalfield' => $external, 607 'internalfield' => $internal 608 ]; 609 $userfieldmapping = new user_field_mapping(0, $record); 610 $userfieldmapping->create(); 611 } 612 613 return endpoint::count_records(['issuerid' => $issuer->get('id')]); 614 } 615 616 /** 617 * Take the data from the mform and update the issuer. 618 * 619 * @param stdClass $data 620 * @return \core\oauth2\issuer 621 */ 622 public static function update_issuer($data) { 623 require_capability('moodle/site:config', context_system::instance()); 624 $issuer = new issuer(0, $data); 625 626 // Will throw exceptions on validation failures. 627 $issuer->update(); 628 629 // Perform service discovery. 630 self::discover_endpoints($issuer); 631 self::guess_image($issuer); 632 return $issuer; 633 } 634 635 /** 636 * Take the data from the mform and create the issuer. 637 * 638 * @param stdClass $data 639 * @return \core\oauth2\issuer 640 */ 641 public static function create_issuer($data) { 642 require_capability('moodle/site:config', context_system::instance()); 643 $issuer = new issuer(0, $data); 644 645 // Will throw exceptions on validation failures. 646 $issuer->create(); 647 648 // Perform service discovery. 649 self::discover_endpoints($issuer); 650 self::guess_image($issuer); 651 return $issuer; 652 } 653 654 /** 655 * Take the data from the mform and update the endpoint. 656 * 657 * @param stdClass $data 658 * @return \core\oauth2\endpoint 659 */ 660 public static function update_endpoint($data) { 661 require_capability('moodle/site:config', context_system::instance()); 662 $endpoint = new endpoint(0, $data); 663 664 // Will throw exceptions on validation failures. 665 $endpoint->update(); 666 667 return $endpoint; 668 } 669 670 /** 671 * Take the data from the mform and create the endpoint. 672 * 673 * @param stdClass $data 674 * @return \core\oauth2\endpoint 675 */ 676 public static function create_endpoint($data) { 677 require_capability('moodle/site:config', context_system::instance()); 678 $endpoint = new endpoint(0, $data); 679 680 // Will throw exceptions on validation failures. 681 $endpoint->create(); 682 return $endpoint; 683 } 684 685 /** 686 * Take the data from the mform and update the user field mapping. 687 * 688 * @param stdClass $data 689 * @return \core\oauth2\user_field_mapping 690 */ 691 public static function update_user_field_mapping($data) { 692 require_capability('moodle/site:config', context_system::instance()); 693 $userfieldmapping = new user_field_mapping(0, $data); 694 695 // Will throw exceptions on validation failures. 696 $userfieldmapping->update(); 697 698 return $userfieldmapping; 699 } 700 701 /** 702 * Take the data from the mform and create the user field mapping. 703 * 704 * @param stdClass $data 705 * @return \core\oauth2\user_field_mapping 706 */ 707 public static function create_user_field_mapping($data) { 708 require_capability('moodle/site:config', context_system::instance()); 709 $userfieldmapping = new user_field_mapping(0, $data); 710 711 // Will throw exceptions on validation failures. 712 $userfieldmapping->create(); 713 return $userfieldmapping; 714 } 715 716 /** 717 * Reorder this identity issuer. 718 * 719 * Requires moodle/site:config capability at the system context. 720 * 721 * @param int $id The id of the identity issuer to move. 722 * @return boolean 723 */ 724 public static function move_up_issuer($id) { 725 require_capability('moodle/site:config', context_system::instance()); 726 $current = new issuer($id); 727 728 $sortorder = $current->get('sortorder'); 729 if ($sortorder == 0) { 730 return false; 731 } 732 733 $sortorder = $sortorder - 1; 734 $current->set('sortorder', $sortorder); 735 736 $filters = array('sortorder' => $sortorder); 737 $children = issuer::get_records($filters, 'id'); 738 foreach ($children as $needtoswap) { 739 $needtoswap->set('sortorder', $sortorder + 1); 740 $needtoswap->update(); 741 } 742 743 // OK - all set. 744 $result = $current->update(); 745 746 return $result; 747 } 748 749 /** 750 * Reorder this identity issuer. 751 * 752 * Requires moodle/site:config capability at the system context. 753 * 754 * @param int $id The id of the identity issuer to move. 755 * @return boolean 756 */ 757 public static function move_down_issuer($id) { 758 require_capability('moodle/site:config', context_system::instance()); 759 $current = new issuer($id); 760 761 $max = issuer::count_records(); 762 if ($max > 0) { 763 $max--; 764 } 765 766 $sortorder = $current->get('sortorder'); 767 if ($sortorder >= $max) { 768 return false; 769 } 770 $sortorder = $sortorder + 1; 771 $current->set('sortorder', $sortorder); 772 773 $filters = array('sortorder' => $sortorder); 774 $children = issuer::get_records($filters); 775 foreach ($children as $needtoswap) { 776 $needtoswap->set('sortorder', $sortorder - 1); 777 $needtoswap->update(); 778 } 779 780 // OK - all set. 781 $result = $current->update(); 782 783 return $result; 784 } 785 786 /** 787 * Disable an identity issuer. 788 * 789 * Requires moodle/site:config capability at the system context. 790 * 791 * @param int $id The id of the identity issuer to disable. 792 * @return boolean 793 */ 794 public static function disable_issuer($id) { 795 require_capability('moodle/site:config', context_system::instance()); 796 $issuer = new issuer($id); 797 798 $issuer->set('enabled', 0); 799 return $issuer->update(); 800 } 801 802 803 /** 804 * Enable an identity issuer. 805 * 806 * Requires moodle/site:config capability at the system context. 807 * 808 * @param int $id The id of the identity issuer to enable. 809 * @return boolean 810 */ 811 public static function enable_issuer($id) { 812 require_capability('moodle/site:config', context_system::instance()); 813 $issuer = new issuer($id); 814 815 $issuer->set('enabled', 1); 816 return $issuer->update(); 817 } 818 819 /** 820 * Delete an identity issuer. 821 * 822 * Requires moodle/site:config capability at the system context. 823 * 824 * @param int $id The id of the identity issuer to delete. 825 * @return boolean 826 */ 827 public static function delete_issuer($id) { 828 require_capability('moodle/site:config', context_system::instance()); 829 $issuer = new issuer($id); 830 831 $systemaccount = self::get_system_account($issuer); 832 if ($systemaccount) { 833 $systemaccount->delete(); 834 } 835 $endpoints = self::get_endpoints($issuer); 836 if ($endpoints) { 837 foreach ($endpoints as $endpoint) { 838 $endpoint->delete(); 839 } 840 } 841 842 // Will throw exceptions on validation failures. 843 return $issuer->delete(); 844 } 845 846 /** 847 * Delete an endpoint. 848 * 849 * Requires moodle/site:config capability at the system context. 850 * 851 * @param int $id The id of the endpoint to delete. 852 * @return boolean 853 */ 854 public static function delete_endpoint($id) { 855 require_capability('moodle/site:config', context_system::instance()); 856 $endpoint = new endpoint($id); 857 858 // Will throw exceptions on validation failures. 859 return $endpoint->delete(); 860 } 861 862 /** 863 * Delete a user_field_mapping. 864 * 865 * Requires moodle/site:config capability at the system context. 866 * 867 * @param int $id The id of the user_field_mapping to delete. 868 * @return boolean 869 */ 870 public static function delete_user_field_mapping($id) { 871 require_capability('moodle/site:config', context_system::instance()); 872 $userfieldmapping = new user_field_mapping($id); 873 874 // Will throw exceptions on validation failures. 875 return $userfieldmapping->delete(); 876 } 877 878 /** 879 * Perform the OAuth dance and get a refresh token. 880 * 881 * Requires moodle/site:config capability at the system context. 882 * 883 * @param \core\oauth2\issuer $issuer 884 * @param moodle_url $returnurl The url to the current page (we will be redirected back here after authentication). 885 * @return boolean 886 */ 887 public static function connect_system_account($issuer, $returnurl) { 888 require_capability('moodle/site:config', context_system::instance()); 889 890 // We need to authenticate with an oauth 2 client AS a system user and get a refresh token for offline access. 891 $scopes = self::get_system_scopes_for_issuer($issuer); 892 893 // Allow callbacks to inject non-standard scopes to the auth request. 894 895 $client = new client($issuer, $returnurl, $scopes, true); 896 897 if (!optional_param('response', false, PARAM_BOOL)) { 898 $client->log_out(); 899 } 900 901 if (optional_param('error', '', PARAM_RAW)) { 902 return false; 903 } 904 905 if (!$client->is_logged_in()) { 906 redirect($client->get_login_url()); 907 } 908 909 $refreshtoken = $client->get_refresh_token(); 910 if (!$refreshtoken) { 911 return false; 912 } 913 914 $systemaccount = self::get_system_account($issuer); 915 if ($systemaccount) { 916 $systemaccount->delete(); 917 } 918 919 $userinfo = $client->get_userinfo(); 920 921 $record = new stdClass(); 922 $record->issuerid = $issuer->get('id'); 923 $record->refreshtoken = $refreshtoken; 924 $record->grantedscopes = $scopes; 925 $record->email = isset($userinfo['email']) ? $userinfo['email'] : ''; 926 $record->username = $userinfo['username']; 927 928 $systemaccount = new system_account(0, $record); 929 930 $systemaccount->create(); 931 932 $client->log_out(); 933 return true; 934 } 935 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body