MDL-59855 oauth2: More PHPDocs fixes
[moodle.git] / lib / classes / oauth2 / api.php
CommitLineData
60237253
DW
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 *
29911249 20 * @package core
60237253
DW
21 * @copyright 2017 Damyon Wiese
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24namespace core\oauth2;
25
29911249
DW
26defined('MOODLE_INTERNAL') || die();
27
60237253
DW
28require_once($CFG->libdir . '/filelib.php');
29
30use context_system;
31use curl;
32use stdClass;
33use moodle_exception;
34use moodle_url;
35
60237253
DW
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 */
43class api {
44
f9f243f9
DW
45 /**
46 * Create a google ready OAuth 2 service.
bd0b9873 47 * @return \core\oauth2\issuer
f9f243f9 48 */
ddf65b8c 49 private static function create_google() {
60237253
DW
50 $record = (object) [
51 'name' => 'Google',
52 'image' => 'https://accounts.google.com/favicon.ico',
60237253 53 'baseurl' => 'http://accounts.google.com/',
485a22fc 54 'loginparamsoffline' => 'access_type=offline&prompt=consent',
60237253
DW
55 'showonloginpage' => true
56 ];
57
58 $issuer = new issuer(0, $record);
59 $issuer->create();
60
61 $record = (object) [
62 'issuerid' => $issuer->get('id'),
63 'name' => 'discovery_endpoint',
64 'url' => 'https://accounts.google.com/.well-known/openid-configuration'
65 ];
66 $endpoint = new endpoint(0, $record);
67 $endpoint->create();
dc4b5685 68 return $issuer;
ddf65b8c
DW
69 }
70
f9f243f9
DW
71 /**
72 * Create a facebook ready OAuth 2 service.
bd0b9873 73 * @return \core\oauth2\issuer
f9f243f9 74 */
ddf65b8c
DW
75 private static function create_facebook() {
76 // Facebook is a custom setup.
77 $record = (object) [
78 'name' => 'Facebook',
79 'image' => 'https://facebookbrand.com/wp-content/themes/fb-branding/prj-fb-branding/assets/images/fb-art.png',
dc4b5685 80 'baseurl' => '',
ddf65b8c
DW
81 'loginscopes' => 'public_profile email',
82 'loginscopesoffline' => 'public_profile email',
83 'showonloginpage' => true
84 ];
85
86 $issuer = new issuer(0, $record);
87 $issuer->create();
88
89 $endpoints = [
90 'authorization_endpoint' => 'https://www.facebook.com/v2.8/dialog/oauth',
91 'token_endpoint' => 'https://graph.facebook.com/v2.8/oauth/access_token',
92 'userinfo_endpoint' => 'https://graph.facebook.com/v2.8/me?fields=id,first_name,last_name,link,picture,name,email'
93 ];
60237253 94
ddf65b8c
DW
95 foreach ($endpoints as $name => $url) {
96 $record = (object) [
97 'issuerid' => $issuer->get('id'),
98 'name' => $name,
99 'url' => $url
100 ];
101 $endpoint = new endpoint(0, $record);
102 $endpoint->create();
103 }
104
105 // Create the field mappings.
106 $mapping = [
107 'name' => 'alternatename',
108 'last_name' => 'lastname',
109 'email' => 'email',
ddf65b8c
DW
110 'first_name' => 'firstname',
111 'picture-data-url' => 'picture',
112 'link' => 'url',
113 ];
114 foreach ($mapping as $external => $internal) {
115 $record = (object) [
116 'issuerid' => $issuer->get('id'),
117 'externalfield' => $external,
118 'internalfield' => $internal
119 ];
120 $userfieldmapping = new user_field_mapping(0, $record);
121 $userfieldmapping->create();
122 }
dc4b5685 123 return $issuer;
ddf65b8c
DW
124 }
125
f9f243f9
DW
126 /**
127 * Create a microsoft ready OAuth 2 service.
bd0b9873 128 * @return \core\oauth2\issuer
f9f243f9 129 */
ddf65b8c 130 private static function create_microsoft() {
8445556b 131 // Microsoft is a custom setup.
60237253
DW
132 $record = (object) [
133 'name' => 'Microsoft',
134 'image' => 'https://www.microsoft.com/favicon.ico',
dc4b5685 135 'baseurl' => '',
485a22fc
DW
136 'loginscopes' => 'openid profile email user.read',
137 'loginscopesoffline' => 'openid profile email user.read offline_access',
60237253
DW
138 'showonloginpage' => true
139 ];
140
141 $issuer = new issuer(0, $record);
142 $issuer->create();
143
8445556b
DW
144 $endpoints = [
145 'authorization_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
146 'token_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
147 'userinfo_endpoint' => 'https://graph.microsoft.com/v1.0/me/',
148 'userpicture_endpoint' => 'https://graph.microsoft.com/v1.0/me/photo/$value',
60237253 149 ];
60237253 150
8445556b
DW
151 foreach ($endpoints as $name => $url) {
152 $record = (object) [
153 'issuerid' => $issuer->get('id'),
154 'name' => $name,
155 'url' => $url
156 ];
157 $endpoint = new endpoint(0, $record);
158 $endpoint->create();
159 }
60237253 160
8445556b
DW
161 // Create the field mappings.
162 $mapping = [
163 'givenName' => 'firstname',
164 'surname' => 'lastname',
7f158660 165 'userPrincipalName' => 'email',
8445556b
DW
166 'displayName' => 'alternatename',
167 'officeLocation' => 'address',
9165e838 168 'mobilePhone' => 'phone1',
8445556b 169 'preferredLanguage' => 'lang'
60237253 170 ];
8445556b
DW
171 foreach ($mapping as $external => $internal) {
172 $record = (object) [
173 'issuerid' => $issuer->get('id'),
174 'externalfield' => $external,
175 'internalfield' => $internal
176 ];
177 $userfieldmapping = new user_field_mapping(0, $record);
178 $userfieldmapping->create();
179 }
dc4b5685 180 return $issuer;
ddf65b8c
DW
181 }
182
29911249
DW
183 /**
184 * Create one of the standard issuers.
185 * @param string $type One of google, facebook, microsoft
186 * @return \core\oauth2\issuer
187 */
dc4b5685
DW
188 public static function create_standard_issuer($type) {
189 require_capability('moodle/site:config', context_system::instance());
190 if ($type == 'google') {
191 return self::create_google();
192 } else if ($type == 'microsoft') {
193 return self::create_microsoft();
194 } else if ($type == 'facebook') {
195 return self::create_facebook();
196 } else {
197 throw new moodle_exception('OAuth 2 service type not recognised: ' . $type);
198 }
60237253
DW
199 }
200
f9f243f9
DW
201 /**
202 * List all the issuers, ordered by the sortorder field
bd0b9873 203 * @return \core\oauth2\issuer[]
f9f243f9 204 */
60237253
DW
205 public static function get_all_issuers() {
206 return issuer::get_records([], 'sortorder');
207 }
208
f9f243f9
DW
209 /**
210 * Get a single issuer by id.
211 *
212 * @param int $id
bd0b9873 213 * @return \core\oauth2\issuer
f9f243f9 214 */
60237253
DW
215 public static function get_issuer($id) {
216 return new issuer($id);
217 }
218
f9f243f9
DW
219 /**
220 * Get a single endpoint by id.
221 *
222 * @param int $id
bd0b9873 223 * @return \core\oauth2\endpoint
f9f243f9 224 */
8445556b
DW
225 public static function get_endpoint($id) {
226 return new endpoint($id);
227 }
228
f9f243f9
DW
229 /**
230 * Get a single user field mapping by id.
231 *
232 * @param int $id
bd0b9873 233 * @return \core\oauth2\user_field_mapping
f9f243f9 234 */
8445556b
DW
235 public static function get_user_field_mapping($id) {
236 return new user_field_mapping($id);
237 }
238
f9f243f9
DW
239 /**
240 * Get the system account for an installed OAuth service.
241 * Never ever ever expose this to a webservice because it contains the refresh token which grants API access.
242 *
72fd103a 243 * @param \core\oauth2\issuer $issuer
7927138d 244 * @return system_account|false
f9f243f9 245 */
60237253
DW
246 public static function get_system_account(issuer $issuer) {
247 return system_account::get_record(['issuerid' => $issuer->get('id')]);
248 }
249
f9f243f9
DW
250 /**
251 * Get the full list of system scopes required by an oauth issuer.
252 * This includes the list required for login as well as any scopes injected by the oauth2_system_scopes callback in plugins.
253 *
29911249 254 * @param \core\oauth2\issuer $issuer
f9f243f9
DW
255 * @return string
256 */
237fd80c
DW
257 public static function get_system_scopes_for_issuer($issuer) {
258 $scopes = $issuer->get('loginscopesoffline');
259
260 $pluginsfunction = get_plugins_with_function('oauth2_system_scopes', 'lib.php');
261 foreach ($pluginsfunction as $plugintype => $plugins) {
262 foreach ($plugins as $pluginfunction) {
263 // Get additional scopes from the plugin.
264 $pluginscopes = $pluginfunction($issuer);
265 if (empty($pluginscopes)) {
266 continue;
267 }
268
269 // Merge the additional scopes with the existing ones.
270 $additionalscopes = explode(' ', $pluginscopes);
271
272 foreach ($additionalscopes as $scope) {
273 if (!empty($scope)) {
274 if (strpos(' ' . $scopes . ' ', ' ' . $scope . ' ') === false) {
275 $scopes .= ' ' . $scope;
276 }
277 }
278 }
279 }
280 }
281
282 return $scopes;
283 }
284
f9f243f9
DW
285 /**
286 * Get an authenticated oauth2 client using the system account.
287 * This call uses the refresh token to get an access token.
288 *
bd0b9873
JD
289 * @param \core\oauth2\issuer $issuer
290 * @return \core\oauth2\client|false An authenticated client (or false if the token could not be upgraded)
291 * @throws moodle_exception Request for token upgrade failed for technical reasons
f9f243f9 292 */
60237253 293 public static function get_system_oauth_client(issuer $issuer) {
237fd80c
DW
294 $systemaccount = self::get_system_account($issuer);
295 if (empty($systemaccount)) {
296 return false;
297 }
298 // Get all the scopes!
299 $scopes = self::get_system_scopes_for_issuer($issuer);
300
301 $client = new \core\oauth2\client($issuer, null, $scopes, true);
302
303 if (!$client->is_logged_in()) {
931c0234 304 if (!$client->upgrade_refresh_token($systemaccount)) {
237fd80c
DW
305 return false;
306 }
307 }
308 return $client;
60237253
DW
309 }
310
f9f243f9
DW
311 /**
312 * Get an authenticated oauth2 client using the current user account.
313 * This call does the redirect dance back to the current page after authentication.
314 *
bd0b9873 315 * @param \core\oauth2\issuer $issuer The desired OAuth issuer
29911249 316 * @param moodle_url $currenturl The url to the current page.
f9f243f9 317 * @param string $additionalscopes The additional scopes required for authorization.
bd0b9873 318 * @return \core\oauth2\client
f9f243f9 319 */
60237253 320 public static function get_user_oauth_client(issuer $issuer, moodle_url $currenturl, $additionalscopes = '') {
485a22fc 321 $client = new \core\oauth2\client($issuer, $currenturl, $additionalscopes);
60237253 322
60237253
DW
323 return $client;
324 }
325
f9f243f9
DW
326 /**
327 * Get the list of defined endpoints for this OAuth issuer
328 *
bd0b9873
JD
329 * @param \core\oauth2\issuer $issuer The desired OAuth issuer
330 * @return \core\oauth2\endpoint[]
f9f243f9 331 */
60237253 332 public static function get_endpoints(issuer $issuer) {
60237253
DW
333 return endpoint::get_records(['issuerid' => $issuer->get('id')]);
334 }
335
f9f243f9
DW
336 /**
337 * Get the list of defined mapping from OAuth user fields to moodle user fields.
338 *
bd0b9873
JD
339 * @param \core\oauth2\issuer $issuer The desired OAuth issuer
340 * @return \core\oauth2\user_field_mapping[]
f9f243f9 341 */
8445556b
DW
342 public static function get_user_field_mappings(issuer $issuer) {
343 return user_field_mapping::get_records(['issuerid' => $issuer->get('id')]);
344 }
345
f9f243f9
DW
346 /**
347 * Guess an image from the discovery URL.
348 *
bd0b9873 349 * @param \core\oauth2\issuer $issuer The desired OAuth issuer
f9f243f9 350 */
60237253
DW
351 protected static function guess_image($issuer) {
352 if (empty($issuer->get('image'))) {
d0298413 353 $baseurl = parse_url($issuer->get('baseurl'));
60237253
DW
354 $imageurl = $baseurl['scheme'] . '://' . $baseurl['host'] . '/favicon.ico';
355 $issuer->set('image', $imageurl);
356 $issuer->update();
357 }
358 }
359
360 /**
ddf65b8c 361 * If the discovery endpoint exists for this issuer, try and determine the list of valid endpoints.
60237253
DW
362 *
363 * @param issuer $issuer
364 * @return int The number of discovered services.
365 */
366 protected static function discover_endpoints($issuer) {
367 $curl = new curl();
368
ddf65b8c 369 if (empty($issuer->get('baseurl'))) {
60237253
DW
370 return 0;
371 }
372
373 $url = $issuer->get_endpoint_url('discovery');
374 if (!$url) {
485a22fc 375 $url = $issuer->get('baseurl') . '/.well-known/openid-configuration';
60237253
DW
376 }
377
ddf65b8c 378 if (!$json = $curl->get($url)) {
60237253
DW
379 $msg = 'Could not discover end points for identity issuer' . $issuer->get('name');
380 throw new moodle_exception($msg);
381 }
382
383 if ($msg = $curl->error) {
384 throw new moodle_exception('Could not discover service endpoints: ' . $msg);
385 }
386
387 $info = json_decode($json);
388 if (empty($info)) {
389 $msg = 'Could not discover end points for identity issuer' . $issuer->get('name');
390 throw new moodle_exception($msg);
391 }
392
393 foreach (endpoint::get_records(['issuerid' => $issuer->get('id')]) as $endpoint) {
394 if ($endpoint->get('name') != 'discovery_endpoint') {
395 $endpoint->delete();
396 }
397 }
398
399 foreach ($info as $key => $value) {
400 if (substr_compare($key, '_endpoint', - strlen('_endpoint')) === 0) {
401 $record = new stdClass();
402 $record->issuerid = $issuer->get('id');
403 $record->name = $key;
404 $record->url = $value;
405
406 $endpoint = new endpoint(0, $record);
407 $endpoint->create();
408 }
409
410 if ($key == 'scopes_supported') {
411 $issuer->set('scopessupported', implode(' ', $value));
412 $issuer->update();
413 }
414 }
415
8445556b 416 // We got to here - must be a decent OpenID connect service. Add the default user field mapping list.
ddf65b8c
DW
417 foreach (user_field_mapping::get_records(['issuerid' => $issuer->get('id')]) as $userfieldmapping) {
418 $userfieldmapping->delete();
419 }
8445556b
DW
420
421 // Create the field mappings.
422 $mapping = [
423 'given_name' => 'firstname',
424 'middle_name' => 'middlename',
425 'family_name' => 'lastname',
426 'email' => 'email',
8445556b
DW
427 'website' => 'url',
428 'nickname' => 'alternatename',
429 'picture' => 'picture',
430 'address' => 'address',
9165e838 431 'phone' => 'phone1',
8445556b
DW
432 'locale' => 'lang'
433 ];
434 foreach ($mapping as $external => $internal) {
435 $record = (object) [
436 'issuerid' => $issuer->get('id'),
437 'externalfield' => $external,
438 'internalfield' => $internal
439 ];
440 $userfieldmapping = new user_field_mapping(0, $record);
441 $userfieldmapping->create();
442 }
443
60237253
DW
444 return endpoint::count_records(['issuerid' => $issuer->get('id')]);
445 }
446
f9f243f9
DW
447 /**
448 * Take the data from the mform and update the issuer.
449 *
450 * @param stdClass $data
bd0b9873 451 * @return \core\oauth2\issuer
f9f243f9 452 */
60237253
DW
453 public static function update_issuer($data) {
454 require_capability('moodle/site:config', context_system::instance());
455 $issuer = new issuer(0, $data);
456
457 // Will throw exceptions on validation failures.
458 $issuer->update();
459
460 // Perform service discovery.
461 self::discover_endpoints($issuer);
462 self::guess_image($issuer);
8445556b 463 return $issuer;
60237253
DW
464 }
465
f9f243f9
DW
466 /**
467 * Take the data from the mform and create the issuer.
468 *
469 * @param stdClass $data
bd0b9873 470 * @return \core\oauth2\issuer
f9f243f9 471 */
60237253
DW
472 public static function create_issuer($data) {
473 require_capability('moodle/site:config', context_system::instance());
474 $issuer = new issuer(0, $data);
475
476 // Will throw exceptions on validation failures.
477 $issuer->create();
478
479 // Perform service discovery.
480 self::discover_endpoints($issuer);
481 self::guess_image($issuer);
8445556b
DW
482 return $issuer;
483 }
484
f9f243f9
DW
485 /**
486 * Take the data from the mform and update the endpoint.
487 *
488 * @param stdClass $data
bd0b9873 489 * @return \core\oauth2\endpoint
f9f243f9 490 */
8445556b
DW
491 public static function update_endpoint($data) {
492 require_capability('moodle/site:config', context_system::instance());
493 $endpoint = new endpoint(0, $data);
494
495 // Will throw exceptions on validation failures.
496 $endpoint->update();
497
498 return $endpoint;
499 }
500
f9f243f9
DW
501 /**
502 * Take the data from the mform and create the endpoint.
503 *
504 * @param stdClass $data
bd0b9873 505 * @return \core\oauth2\endpoint
f9f243f9 506 */
8445556b
DW
507 public static function create_endpoint($data) {
508 require_capability('moodle/site:config', context_system::instance());
509 $endpoint = new endpoint(0, $data);
510
511 // Will throw exceptions on validation failures.
512 $endpoint->create();
513 return $endpoint;
514 }
515
f9f243f9
DW
516 /**
517 * Take the data from the mform and update the user field mapping.
518 *
519 * @param stdClass $data
bd0b9873 520 * @return \core\oauth2\user_field_mapping
f9f243f9 521 */
8445556b
DW
522 public static function update_user_field_mapping($data) {
523 require_capability('moodle/site:config', context_system::instance());
524 $userfieldmapping = new user_field_mapping(0, $data);
525
526 // Will throw exceptions on validation failures.
527 $userfieldmapping->update();
528
529 return $userfieldmapping;
530 }
531
f9f243f9
DW
532 /**
533 * Take the data from the mform and create the user field mapping.
534 *
535 * @param stdClass $data
bd0b9873 536 * @return \core\oauth2\user_field_mapping
f9f243f9 537 */
8445556b
DW
538 public static function create_user_field_mapping($data) {
539 require_capability('moodle/site:config', context_system::instance());
540 $userfieldmapping = new user_field_mapping(0, $data);
541
542 // Will throw exceptions on validation failures.
543 $userfieldmapping->create();
544 return $userfieldmapping;
60237253
DW
545 }
546
547 /**
548 * Reorder this identity issuer.
549 *
550 * Requires moodle/site:config capability at the system context.
551 *
552 * @param int $id The id of the identity issuer to move.
553 * @return boolean
554 */
555 public static function move_up_issuer($id) {
556 require_capability('moodle/site:config', context_system::instance());
557 $current = new issuer($id);
558
559 $sortorder = $current->get('sortorder');
560 if ($sortorder == 0) {
561 return false;
562 }
563
564 $sortorder = $sortorder - 1;
565 $current->set('sortorder', $sortorder);
566
567 $filters = array('sortorder' => $sortorder);
568 $children = issuer::get_records($filters, 'id');
569 foreach ($children as $needtoswap) {
570 $needtoswap->set('sortorder', $sortorder + 1);
571 $needtoswap->update();
572 }
573
574 // OK - all set.
575 $result = $current->update();
576
577 return $result;
578 }
579
f9f243f9
DW
580 /**
581 * Reorder this identity issuer.
582 *
583 * Requires moodle/site:config capability at the system context.
584 *
585 * @param int $id The id of the identity issuer to move.
586 * @return boolean
587 */
60237253
DW
588 public static function move_down_issuer($id) {
589 require_capability('moodle/site:config', context_system::instance());
590 $current = new issuer($id);
591
592 $max = issuer::count_records();
593 if ($max > 0) {
594 $max--;
595 }
596
597 $sortorder = $current->get('sortorder');
598 if ($sortorder >= $max) {
599 return false;
600 }
601 $sortorder = $sortorder + 1;
602 $current->set('sortorder', $sortorder);
603
604 $filters = array('sortorder' => $sortorder);
605 $children = issuer::get_records($filters);
606 foreach ($children as $needtoswap) {
607 $needtoswap->set('sortorder', $sortorder - 1);
608 $needtoswap->update();
609 }
610
611 // OK - all set.
612 $result = $current->update();
613
614 return $result;
615 }
616
eca128bf
DW
617 /**
618 * Disable an identity issuer.
619 *
620 * Requires moodle/site:config capability at the system context.
621 *
99e3c347 622 * @param int $id The id of the identity issuer to disable.
eca128bf
DW
623 * @return boolean
624 */
625 public static function disable_issuer($id) {
626 require_capability('moodle/site:config', context_system::instance());
627 $issuer = new issuer($id);
628
629 $issuer->set('enabled', 0);
630 return $issuer->update();
631 }
632
633
634 /**
635 * Enable an identity issuer.
636 *
637 * Requires moodle/site:config capability at the system context.
638 *
639 * @param int $id The id of the identity issuer to enable.
640 * @return boolean
641 */
642 public static function enable_issuer($id) {
643 require_capability('moodle/site:config', context_system::instance());
644 $issuer = new issuer($id);
645
646 $issuer->set('enabled', 1);
647 return $issuer->update();
648 }
649
f9f243f9
DW
650 /**
651 * Delete an identity issuer.
652 *
653 * Requires moodle/site:config capability at the system context.
654 *
655 * @param int $id The id of the identity issuer to delete.
656 * @return boolean
657 */
60237253
DW
658 public static function delete_issuer($id) {
659 require_capability('moodle/site:config', context_system::instance());
660 $issuer = new issuer($id);
661
662 $systemaccount = self::get_system_account($issuer);
663 if ($systemaccount) {
664 $systemaccount->delete();
665 }
666 $endpoints = self::get_endpoints($issuer);
667 if ($endpoints) {
668 foreach ($endpoints as $endpoint) {
669 $endpoint->delete();
670 }
671 }
672
673 // Will throw exceptions on validation failures.
8445556b
DW
674 return $issuer->delete();
675 }
676
f9f243f9
DW
677 /**
678 * Delete an endpoint.
679 *
680 * Requires moodle/site:config capability at the system context.
681 *
682 * @param int $id The id of the endpoint to delete.
683 * @return boolean
684 */
8445556b
DW
685 public static function delete_endpoint($id) {
686 require_capability('moodle/site:config', context_system::instance());
687 $endpoint = new endpoint($id);
688
689 // Will throw exceptions on validation failures.
690 return $endpoint->delete();
691 }
692
f9f243f9
DW
693 /**
694 * Delete a user_field_mapping.
695 *
696 * Requires moodle/site:config capability at the system context.
697 *
698 * @param int $id The id of the user_field_mapping to delete.
699 * @return boolean
700 */
8445556b
DW
701 public static function delete_user_field_mapping($id) {
702 require_capability('moodle/site:config', context_system::instance());
703 $userfieldmapping = new user_field_mapping($id);
704
705 // Will throw exceptions on validation failures.
706 return $userfieldmapping->delete();
60237253
DW
707 }
708
f9f243f9
DW
709 /**
710 * Perform the OAuth dance and get a refresh token.
711 *
712 * Requires moodle/site:config capability at the system context.
713 *
bd0b9873 714 * @param \core\oauth2\issuer $issuer
f9f243f9
DW
715 * @param moodle_url $returnurl The url to the current page (we will be redirected back here after authentication).
716 * @return boolean
717 */
60237253
DW
718 public static function connect_system_account($issuer, $returnurl) {
719 require_capability('moodle/site:config', context_system::instance());
720
721 // We need to authenticate with an oauth 2 client AS a system user and get a refresh token for offline access.
931c0234 722 $scopes = self::get_system_scopes_for_issuer($issuer);
60237253
DW
723
724 // Allow callbacks to inject non-standard scopes to the auth request.
725
931c0234 726 $client = new client($issuer, $returnurl, $scopes, true);
60237253
DW
727
728 if (!optional_param('response', false, PARAM_BOOL)) {
729 $client->log_out();
730 }
731
8445556b
DW
732 if (optional_param('error', '', PARAM_RAW)) {
733 return false;
734 }
735
60237253
DW
736 if (!$client->is_logged_in()) {
737 redirect($client->get_login_url());
738 }
739
740 $refreshtoken = $client->get_refresh_token();
741 if (!$refreshtoken) {
742 return false;
743 }
744
745 $systemaccount = self::get_system_account($issuer);
746 if ($systemaccount) {
747 $systemaccount->delete();
748 }
28dddbc1
DW
749
750 $userinfo = $client->get_userinfo();
751
60237253
DW
752 $record = new stdClass();
753 $record->issuerid = $issuer->get('id');
754 $record->refreshtoken = $refreshtoken;
29911249 755 $record->grantedscopes = $scopes;
3fa588c6 756 $record->email = isset($userinfo['email']) ? $userinfo['email'] : '';
28dddbc1 757 $record->username = $userinfo['username'];
60237253
DW
758
759 $systemaccount = new system_account(0, $record);
760
761 $systemaccount->create();
762
763 $client->log_out();
764 return true;
765 }
766}