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 resource
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
27namespace mod_lti\local\ltiservice;
28
29defined('MOODLE_INTERNAL') || die();
30
31global $CFG;
32require_once($CFG->dirroot . '/mod/lti/locallib.php');
33
34
35/**
36 * The mod_lti\local\ltiservice\resource_base class.
37 *
38 * @package    mod_lti
39 * @since      Moodle 2.8
40 * @copyright  2014 Vital Source Technologies http://vitalsource.com
41 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42 */
43abstract class resource_base {
44
45    /**  HTTP Post method */
46    const HTTP_POST = 'POST';
47    /**  HTTP Get method */
48    const HTTP_GET = 'GET';
49    /**  HTTP Put method */
50    const HTTP_PUT = 'PUT';
51    /**  HTTP Delete method */
52    const HTTP_DELETE = 'DELETE';
53
54    /** @var service_base Service associated with this resource. */
55    private $service;
56    /** @var string Type for this resource. */
57    protected $type;
58    /** @var string ID for this resource. */
59    protected $id;
60    /** @var string Template for this resource. */
61    protected $template;
62    /** @var array Custom parameter substitution variables associated with this resource. */
63    protected $variables;
64    /** @var array Media types supported by this resource. */
65    protected $formats;
66    /** @var array HTTP actions supported by this resource. */
67    protected $methods;
68    /** @var array Template variables parsed from the resource template. */
69    protected $params;
70
71
72    /**
73     * Class constructor.
74     *
75     * @param service_base $service Service instance
76     */
77    public function __construct($service) {
78
79        $this->service = $service;
80        $this->type = 'RestService';
81        $this->id = null;
82        $this->template = null;
83        $this->methods = array();
84        $this->variables = array();
85        $this->formats = array();
86        $this->methods = array();
87        $this->params = null;
88
89    }
90
91    /**
92     * Get the resource ID.
93     *
94     * @return string
95     */
96    public function get_id() {
97
98        return $this->id;
99
100    }
101
102    /**
103     * Get the resource template.
104     *
105     * @return string
106     */
107    public function get_template() {
108
109        return $this->template;
110
111    }
112
113    /**
114     * Get the resource path.
115     *
116     * @return string
117     */
118    public function get_path() {
119
120        return $this->get_template();
121
122    }
123
124    /**
125     * Get the resource type.
126     *
127     * @return string
128     */
129    public function get_type() {
130
131        return $this->type;
132
133    }
134
135    /**
136     * Get the resource's service.
137     *
138     * @return mixed
139     */
140    public function get_service() {
141
142        return $this->service;
143
144    }
145
146    /**
147     * Get the resource methods.
148     *
149     * @return array
150     */
151    public function get_methods() {
152
153        return $this->methods;
154
155    }
156
157    /**
158     * Get the resource media types.
159     *
160     * @return array
161     */
162    public function get_formats() {
163
164        return $this->formats;
165
166    }
167
168    /**
169     * Get the resource template variables.
170     *
171     * @return array
172     */
173    public function get_variables() {
174
175        return $this->variables;
176
177    }
178
179    /**
180     * Get the resource fully qualified endpoint.
181     *
182     * @return string
183     */
184    public function get_endpoint() {
185
186        $this->parse_template();
187        $template = preg_replace('/[\(\)]/', '', $this->get_template());
188        $url = $this->get_service()->get_service_path() . $template;
189        foreach ($this->params as $key => $value) {
190            $url = str_replace('{' . $key . '}', $value, $url);
191        }
192        $toolproxy = $this->get_service()->get_tool_proxy();
193        if (!empty($toolproxy)) {
194            $url = str_replace('{config_type}', 'toolproxy', $url);
195            $url = str_replace('{tool_proxy_id}', $toolproxy->guid, $url);
196        } else {
197            $url = str_replace('{config_type}', 'tool', $url);
198            $url = str_replace('{tool_proxy_id}', $this->get_service()->get_type()->id, $url);
199        }
200
201        return $url;
202
203    }
204
205    /**
206     * Execute the request for this resource.
207     *
208     * @param response $response  Response object for this request.
209     */
210    public abstract function execute($response);
211
212    /**
213     * Check to make sure the request is valid.
214     *
215     * @param int $typeid                   The typeid we want to use
216     * @param string $body                  Body of HTTP request message
217     * @param string[] $scopes              Array of scope(s) required for incoming request
218     *
219     * @return boolean
220     */
221    public function check_tool($typeid, $body = null, $scopes = null) {
222
223        $ok = $this->get_service()->check_tool($typeid, $body, $scopes);
224        if ($ok) {
225            if ($this->get_service()->get_tool_proxy()) {
226                $toolproxyjson = $this->get_service()->get_tool_proxy()->toolproxy;
227            }
228            if (!empty($toolproxyjson)) {
229                // Check tool proxy to ensure service being requested is included.
230                $toolproxy = json_decode($toolproxyjson);
231                if (!empty($toolproxy) && isset($toolproxy->security_contract->tool_service)) {
232                    $contexts = lti_get_contexts($toolproxy);
233                    $tpservices = $toolproxy->security_contract->tool_service;
234                    foreach ($tpservices as $service) {
235                        $fqid = lti_get_fqid($contexts, $service->service);
236                        $id = explode('#', $fqid, 2);
237                        if ($this->get_id() === $id[1]) {
238                            $ok = true;
239                            break;
240                        }
241                    }
242                }
243                if (!$ok) {
244                    debugging('Requested service not permitted: ' . $this->get_id(), DEBUG_DEVELOPER);
245                }
246            } else {
247                // Check that the scope required for the service request is included in those granted for the
248                // access token being used.
249                $permittedscopes = $this->get_service()->get_permitted_scopes();
250                $ok = is_null($permittedscopes) || empty($scopes) || !empty(array_intersect($permittedscopes, $scopes));
251            }
252        }
253
254        return $ok;
255
256    }
257
258    /**
259     * Check to make sure the request is valid.
260     *
261     * @param string $toolproxyguid Consumer key
262     * @param string $body          Body of HTTP request message
263     *
264     * @return boolean
265     * @deprecated since Moodle 3.7 MDL-62599 - please do not use this function any more.
266     * @see resource_base::check_tool()
267     */
268    public function check_tool_proxy($toolproxyguid, $body = null) {
269
270        debugging('check_tool_proxy() is deprecated to allow LTI 1 connections to support services. ' .
271                  'Please use resource_base::check_tool() instead.', DEBUG_DEVELOPER);
272        $ok = false;
273        if ($this->get_service()->check_tool_proxy($toolproxyguid, $body)) {
274            $toolproxyjson = $this->get_service()->get_tool_proxy()->toolproxy;
275            if (empty($toolproxyjson)) {
276                $ok = true;
277            } else {
278                $toolproxy = json_decode($toolproxyjson);
279                if (!empty($toolproxy) && isset($toolproxy->security_contract->tool_service)) {
280                    $contexts = lti_get_contexts($toolproxy);
281                    $tpservices = $toolproxy->security_contract->tool_service;
282                    foreach ($tpservices as $service) {
283                        $fqid = lti_get_fqid($contexts, $service->service);
284                        $id = explode('#', $fqid, 2);
285                        if ($this->get_id() === $id[1]) {
286                            $ok = true;
287                            break;
288                        }
289                    }
290                }
291                if (!$ok) {
292                    debugging('Requested service not included in tool proxy: ' . $this->get_id());
293                }
294            }
295        }
296
297        return $ok;
298
299    }
300
301    /**
302     * Check to make sure the request is valid.
303     *
304     * @param int $typeid                   The typeid we want to use
305     * @param int $contextid                The course we are at
306     * @param string $permissionrequested   The permission to be checked
307     * @param string $body                  Body of HTTP request message
308     *
309     * @return boolean
310     * @deprecated since Moodle 3.7 MDL-62599 - please do not use this function any more.
311     * @see resource_base::check_tool()
312     */
313    public function check_type($typeid, $contextid, $permissionrequested, $body = null) {
314        debugging('check_type() is deprecated to allow LTI 1 connections to support services. ' .
315                  'Please use resource_base::check_tool() instead.', DEBUG_DEVELOPER);
316        $ok = false;
317        if ($this->get_service()->check_type($typeid, $contextid, $body)) {
318            $neededpermissions = $this->get_permissions($typeid);
319            foreach ($neededpermissions as $permission) {
320                if ($permission == $permissionrequested) {
321                    $ok = true;
322                    break;
323                }
324            }
325            if (!$ok) {
326                debugging('Requested service ' . $permissionrequested . ' not included in tool type: ' . $typeid,
327                    DEBUG_DEVELOPER);
328            }
329        }
330        return $ok;
331    }
332
333    /**
334     * get permissions from the config of the tool for that resource
335     *
336     * @param int $ltitype Type of LTI
337     * @return array with the permissions related to this resource by the $ltitype or empty if none.
338     * @deprecated since Moodle 3.7 MDL-62599 - please do not use this function any more.
339     * @see resource_base::check_tool()
340     */
341    public function get_permissions($ltitype) {
342        debugging('get_permissions() is deprecated to allow LTI 1 connections to support services. ' .
343                  'Please use resource_base::check_tool() instead.', DEBUG_DEVELOPER);
344        return array();
345    }
346
347    /**
348     * Parse a value for custom parameter substitution variables.
349     *
350     * @param string $value String to be parsed
351     *
352     * @return string
353     */
354    public function parse_value($value) {
355
356        return $value;
357
358    }
359
360    /**
361     * Parse the template for variables.
362     *
363     * @return array
364     */
365    protected function parse_template() {
366
367        if (empty($this->params)) {
368            $this->params = array();
369            if (!empty($_SERVER['PATH_INFO'])) {
370                $path = explode('/', $_SERVER['PATH_INFO']);
371                $template = preg_replace('/\([0-9a-zA-Z_\-,\/]+\)/', '', $this->get_template());
372                $parts = explode('/', $template);
373                for ($i = 0; $i < count($parts); $i++) {
374                    if ((substr($parts[$i], 0, 1) == '{') && (substr($parts[$i], -1) == '}')) {
375                        $value = '';
376                        if ($i < count($path)) {
377                            $value = $path[$i];
378                        }
379                        $this->params[substr($parts[$i], 1, -1)] = $value;
380                    }
381                }
382            }
383        }
384
385        return $this->params;
386
387    }
388
389}
390