Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
 * This file contains an abstract definition of an LTI service
19
 *
20
 * @package    mod_lti
21
 * @copyright  2014 Vital Source Technologies http://vitalsource.com
22
 * @author     Stephen Vickers
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
 
26
 
27
namespace mod_lti\local\ltiservice;
28
 
29
defined('MOODLE_INTERNAL') || die();
30
 
31
global $CFG;
32
require_once($CFG->dirroot . '/mod/lti/locallib.php');
33
require_once($CFG->dirroot . '/mod/lti/OAuthBody.php');
34
 
35
// TODO: Switch to core oauthlib once implemented - MDL-30149.
36
use moodle\mod\lti as lti;
37
use stdClass;
38
 
39
 
40
/**
41
 * The mod_lti\local\ltiservice\service_base class.
42
 *
43
 * @package    mod_lti
44
 * @since      Moodle 2.8
45
 * @copyright  2014 Vital Source Technologies http://vitalsource.com
46
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
47
 */
48
abstract class service_base {
49
 
50
    /** Label representing an LTI 2 message type */
51
    const LTI_VERSION2P0 = 'LTI-2p0';
52
    /** Service enabled */
53
    const SERVICE_ENABLED = 1;
54
 
55
    /** @var string ID for the service. */
56
    protected $id;
57
    /** @var string Human readable name for the service. */
58
    protected $name;
59
    /** @var boolean <code>true</code> if requests for this service do not need to be signed. */
60
    protected $unsigned;
61
    /** @var stdClass Tool proxy object for the current service request. */
62
    private $toolproxy;
63
    /** @var stdClass LTI type object for the current service request. */
64
    private $type;
65
    /** @var array LTI type config array for the current service request. */
66
    private $typeconfig;
67
    /** @var array Instances of the resources associated with this service. */
68
    protected $resources;
69
 
70
 
71
    /**
72
     * Class constructor.
73
     */
74
    public function __construct() {
75
 
76
        $this->id = null;
77
        $this->name = null;
78
        $this->unsigned = false;
79
        $this->toolproxy = null;
80
        $this->type = null;
81
        $this->typeconfig = null;
82
        $this->resources = null;
83
 
84
    }
85
 
86
    /**
87
     * Get the service ID.
88
     *
89
     * @return string
90
     */
91
    public function get_id() {
92
 
93
        return $this->id;
94
 
95
    }
96
 
97
    /**
98
     * Get the service compoent ID.
99
     *
100
     * @return string
101
     */
102
    public function get_component_id() {
103
 
104
        return 'ltiservice_' . $this->id;
105
 
106
    }
107
 
108
    /**
109
     * Get the service name.
110
     *
111
     * @return string
112
     */
113
    public function get_name() {
114
 
115
        return $this->name;
116
 
117
    }
118
 
119
    /**
120
     * Get whether the service requests need to be signed.
121
     *
122
     * @return boolean
123
     */
124
    public function is_unsigned() {
125
 
126
        return $this->unsigned;
127
 
128
    }
129
 
130
    /**
131
     * Get the tool proxy object.
132
     *
133
     * @return stdClass
134
     */
135
    public function get_tool_proxy() {
136
 
137
        return $this->toolproxy;
138
 
139
    }
140
 
141
    /**
142
     * Set the tool proxy object.
143
     *
144
     * @param object $toolproxy The tool proxy for this service request
145
     *
146
     * @var stdClass
147
     */
148
    public function set_tool_proxy($toolproxy) {
149
 
150
        $this->toolproxy = $toolproxy;
151
 
152
    }
153
 
154
    /**
155
     * Get the type object.
156
     *
157
     * @return stdClass
158
     */
159
    public function get_type() {
160
 
161
        return $this->type;
162
 
163
    }
164
 
165
    /**
166
     * Set the LTI type object.
167
     *
168
     * @param object $type The LTI type for this service request
169
     *
170
     * @var stdClass
171
     */
172
    public function set_type($type) {
173
 
174
        $this->type = $type;
175
 
176
    }
177
 
178
    /**
179
     * Get the type config array.
180
     *
181
     * @return array|null
182
     */
183
    public function get_typeconfig() {
184
 
185
        return $this->typeconfig;
186
 
187
    }
188
 
189
    /**
190
     * Set the LTI type config object.
191
     *
192
     * @param array $typeconfig The LTI type config for this service request
193
     *
194
     * @var array
195
     */
196
    public function set_typeconfig($typeconfig) {
197
 
198
        $this->typeconfig = $typeconfig;
199
 
200
    }
201
 
202
    /**
203
     * Get the resources for this service.
204
     *
205
     * @return resource_base[]
206
     */
207
    abstract public function get_resources();
208
 
209
    /**
210
     * Get the scope(s) permitted for this service in the context of a particular tool type.
211
     *
212
     * A null value indicates that no scopes are required to access the service.
213
     *
214
     * @return array|null
215
     */
216
    public function get_permitted_scopes() {
217
        return null;
218
    }
219
 
220
    /**
221
     * Get the scope(s) permitted for this service.
222
     *
223
     * A null value indicates that no scopes are required to access the service.
224
     *
225
     * @return array|null
226
     */
227
    public function get_scopes() {
228
        return null;
229
    }
230
 
231
    /**
232
     * Returns the configuration options for this service.
233
     *
234
     * @param \MoodleQuickForm $mform Moodle quickform object definition
235
     */
236
    public function get_configuration_options(&$mform) {
237
 
238
    }
239
 
240
    /**
241
     * Called when a new LTI Instance is added.
242
     *
243
     * @param object $lti LTI Instance.
244
     */
245
    public function instance_added(object $lti): void {
246
 
247
    }
248
 
249
    /**
250
     * Called when a new LTI Instance is updated.
251
     *
252
     * @param object $lti LTI Instance.
253
     */
254
    public function instance_updated(object $lti): void {
255
 
256
    }
257
 
258
    /**
259
     * Called when the launch data is created, offering a possibility to alter the
260
     * target link URI.
261
     *
262
     * @param string $messagetype message type for this launch
263
     * @param string $targetlinkuri current target link uri
264
     * @param null|string $customstr concatenated list of custom parameters
265
     * @param int $courseid
266
     * @param null|object $lti LTI Instance.
267
     *
268
     * @return array containing the target link URL and the custom params string to use.
269
     */
270
    public function override_endpoint(string $messagetype, string $targetlinkuri,
271
            ?string $customstr, int $courseid, ?object $lti = null): array {
272
        return [$targetlinkuri, $customstr];
273
    }
274
 
275
    /**
276
     * Called when a new LTI Instance is deleted.
277
     *
278
     * @param int $id LTI Instance.
279
     */
280
    public function instance_deleted(int $id): void {
281
 
282
    }
283
 
284
    /**
285
     * Set the form data when displaying the LTI Instance form.
286
     *
287
     * @param object $defaultvalues Default form values.
288
     */
289
    public function set_instance_form_values(object $defaultvalues): void {
290
 
291
    }
292
 
293
    /**
294
     * Return an array with the names of the parameters that the service will be saving in the configuration
295
     *
296
     * @return array  Names list of the parameters that the service will be saving in the configuration
297
     * @deprecated since Moodle 3.7 - please do not use this function any more.
298
     */
299
    public function get_configuration_parameter_names() {
300
        debugging('get_configuration_parameter_names() has been deprecated.', DEBUG_DEVELOPER);
301
        return array();
302
    }
303
 
304
    /**
305
     * Default implementation will check for the existence of at least one mod_lti entry for that tool and context.
306
     *
307
     * It may be overridden if other inferences can be done.
308
     *
309
     * Ideally a Site Tool should be explicitly engaged with a course, the check on the presence of a link is a proxy
310
     * to infer a Site Tool engagement until an explicit Site Tool - Course relationship exists.
311
     *
312
     * @param int $typeid The tool lti type id.
313
     * @param int $courseid The course id.
314
     * @return bool returns True if tool is used in context, false otherwise.
315
     */
316
    public function is_used_in_context($typeid, $courseid) {
317
        global $DB;
318
 
319
        $ok = $DB->record_exists('lti', array('course' => $courseid, 'typeid' => $typeid));
320
        return $ok || $DB->record_exists('lti_types', array('course' => $courseid, 'id' => $typeid));
321
    }
322
 
323
    /**
324
     * Checks if there is a site tool or a course tool for this site.
325
     *
326
     * @param int $typeid The tool lti type id.
327
     * @param int $courseid The course id.
328
     * @return bool returns True if tool is allowed in context, false otherwise.
329
     */
330
    public function is_allowed_in_context($typeid, $courseid) {
331
        global $DB;
332
 
333
        // Check if it is a Course tool for this course or a Site tool.
334
        $type = $DB->get_record('lti_types', array('id' => $typeid));
335
 
336
        return $type && ($type->course == $courseid || $type->course == SITEID);
337
    }
338
 
339
    /**
340
     * Return an array of key/values to add to the launch parameters.
341
     *
342
     * @param string $messagetype  'basic-lti-launch-request' or 'ContentItemSelectionRequest'.
343
     * @param string $courseid     The course id.
344
     * @param string $userid       The user id.
345
     * @param string $typeid       The tool lti type id.
346
     * @param string $modlti       The id of the lti activity.
347
     *
348
     * The type is passed to check the configuration and not return parameters for services not used.
349
     *
350
     * @return array Key/value pairs to add as launch parameters.
351
     */
352
    public function get_launch_parameters($messagetype, $courseid, $userid, $typeid, $modlti = null) {
353
        return array();
354
    }
355
 
356
    /**
357
     * Return an array of key/claim mapping allowing LTI 1.1 custom parameters
358
     * to be transformed to LTI 1.3 claims.
359
     *
360
     * @return array Key/value pairs of params to claim mapping.
361
     */
362
    public function get_jwt_claim_mappings(): array {
363
        return [];
364
    }
365
 
366
    /**
367
     * Get the path for service requests.
368
     *
369
     * @return string
370
     */
371
    public static function get_service_path() {
372
 
373
        $url = new \moodle_url('/mod/lti/services.php');
374
 
375
        return $url->out(false);
376
 
377
    }
378
 
379
    /**
380
     * Parse a string for custom substitution parameter variables supported by this service's resources.
381
     *
382
     * @param string $value  Value to be parsed
383
     *
384
     * @return string
385
     */
386
    public function parse_value($value) {
387
 
388
        if (empty($this->resources)) {
389
            $this->resources = $this->get_resources();
390
        }
391
        if (!empty($this->resources)) {
392
            foreach ($this->resources as $resource) {
393
                $value = $resource->parse_value($value);
394
            }
395
        }
396
 
397
        return $value;
398
 
399
    }
400
 
401
    /**
402
     * Check that the request has been properly signed and is permitted.
403
     *
404
     * @param string $typeid    LTI type ID
405
     * @param string $body      Request body (null if none)
406
     * @param string[] $scopes  Array of required scope(s) for incoming request
407
     *
408
     * @return boolean
409
     */
410
    public function check_tool($typeid, $body = null, $scopes = null) {
411
 
412
        $ok = true;
413
        $toolproxy = null;
414
        $consumerkey = lti\get_oauth_key_from_headers($typeid, $scopes);
415
        if ($consumerkey === false) {
416
            $ok = $this->is_unsigned();
417
        } else {
418
            if (empty($typeid) && is_int($consumerkey)) {
419
                $typeid = $consumerkey;
420
            }
421
            if (!empty($typeid)) {
422
                $this->type = lti_get_type($typeid);
423
                $this->typeconfig = lti_get_type_config($typeid);
424
                $ok = !empty($this->type->id);
425
                if ($ok && !empty($this->type->toolproxyid)) {
426
                    $this->toolproxy = lti_get_tool_proxy($this->type->toolproxyid);
427
                }
428
            } else {
429
                $toolproxy = lti_get_tool_proxy_from_guid($consumerkey);
430
                if ($toolproxy !== false) {
431
                    $this->toolproxy = $toolproxy;
432
                }
433
            }
434
        }
435
        if ($ok && is_string($consumerkey)) {
436
            if (!empty($this->toolproxy)) {
437
                $key = $this->toolproxy->guid;
438
                $secret = $this->toolproxy->secret;
439
            } else {
440
                $key = $this->typeconfig['resourcekey'];
441
                $secret = $this->typeconfig['password'];
442
            }
443
            if (!$this->is_unsigned() && ($key == $consumerkey)) {
444
                $ok = $this->check_signature($key, $secret, $body);
445
            } else {
446
                $ok = $this->is_unsigned();
447
            }
448
        }
449
 
450
        return $ok;
451
 
452
    }
453
 
454
    /**
455
     * Check that the request has been properly signed.
456
     *
457
     * @param string $toolproxyguid  Tool Proxy GUID
458
     * @param string $body           Request body (null if none)
459
     *
460
     * @return boolean
461
     * @deprecated since Moodle 3.7 MDL-62599 - please do not use this function any more.
462
     * @see service_base::check_tool()
463
     */
464
    public function check_tool_proxy($toolproxyguid, $body = null) {
465
 
466
        debugging('check_tool_proxy() is deprecated to allow LTI 1 connections to support services. ' .
467
                  'Please use service_base::check_tool() instead.', DEBUG_DEVELOPER);
468
        $ok = false;
469
        $toolproxy = null;
470
        $consumerkey = lti\get_oauth_key_from_headers();
471
        if (empty($toolproxyguid)) {
472
            $toolproxyguid = $consumerkey;
473
        }
474
 
475
        if (!empty($toolproxyguid)) {
476
            $toolproxy = lti_get_tool_proxy_from_guid($toolproxyguid);
477
            if ($toolproxy !== false) {
478
                if (!$this->is_unsigned() && ($toolproxy->guid == $consumerkey)) {
479
                    $ok = $this->check_signature($toolproxy->guid, $toolproxy->secret, $body);
480
                } else {
481
                    $ok = $this->is_unsigned();
482
                }
483
            }
484
        }
485
        if ($ok) {
486
            $this->toolproxy = $toolproxy;
487
        }
488
 
489
        return $ok;
490
 
491
    }
492
 
493
    /**
494
     * Check that the request has been properly signed.
495
     *
496
     * @param int $typeid The tool id
497
     * @param int $courseid The course we are at
498
     * @param string $body Request body (null if none)
499
     *
500
     * @return bool
501
     * @deprecated since Moodle 3.7 MDL-62599 - please do not use this function any more.
502
     * @see service_base::check_tool()
503
     */
504
    public function check_type($typeid, $courseid, $body = null) {
505
        debugging('check_type() is deprecated to allow LTI 1 connections to support services. ' .
506
                  'Please use service_base::check_tool() instead.', DEBUG_DEVELOPER);
507
        $ok = false;
508
        $tool = null;
509
        $consumerkey = lti\get_oauth_key_from_headers();
510
        if (empty($typeid)) {
511
            return $ok;
512
        } else if ($this->is_allowed_in_context($typeid, $courseid)) {
513
            $tool = lti_get_type_type_config($typeid);
514
            if ($tool !== false) {
515
                if (!$this->is_unsigned() && ($tool->lti_resourcekey == $consumerkey)) {
516
                    $ok = $this->check_signature($tool->lti_resourcekey, $tool->lti_password, $body);
517
                } else {
518
                    $ok = $this->is_unsigned();
519
                }
520
            }
521
        }
522
        return $ok;
523
    }
524
 
525
    /**
526
     * Check the request signature.
527
     *
528
     * @param string $consumerkey    Consumer key
529
     * @param string $secret         Shared secret
530
     * @param string $body           Request body
531
     *
532
     * @return boolean
533
     */
534
    private function check_signature($consumerkey, $secret, $body) {
535
 
536
        $ok = true;
537
        try {
538
            // TODO: Switch to core oauthlib once implemented - MDL-30149.
539
            lti\handle_oauth_body_post($consumerkey, $secret, $body);
540
        } catch (\Exception $e) {
541
            debugging($e->getMessage() . "\n");
542
            $ok = false;
543
        }
544
 
545
        return $ok;
546
 
547
    }
548
}