MDL-55742 mod_lti: Remove support for get_magic_quotes_gpc
[moodle.git] / mod / lti / service / toolproxy / classes / local / resource / toolproxy.php
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/>.
17 /**
18  * This file contains a class definition for the Tool Proxy resource
19  *
20  * @package    ltiservice_toolproxy
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  */
27 namespace ltiservice_toolproxy\local\resource;
29 defined('MOODLE_INTERNAL') || die();
31 require_once($CFG->dirroot . '/mod/lti/OAuth.php');
32 require_once($CFG->dirroot . '/mod/lti/TrivialStore.php');
34 // TODO: Switch to core oauthlib once implemented - MDL-30149.
35 use moodle\mod\lti as lti;
37 /**
38  * A resource implementing the Tool Proxy.
39  *
40  * @package    ltiservice_toolproxy
41  * @since      Moodle 2.8
42  * @copyright  2014 Vital Source Technologies http://vitalsource.com
43  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44  */
45 class toolproxy extends \mod_lti\local\ltiservice\resource_base {
47     /**
48      * Class constructor.
49      *
50      * @param ltiservice_toolproxy\local\resource\toolproxy $service Service instance
51      */
52     public function __construct($service) {
54         parent::__construct($service);
55         $this->id = 'ToolProxy.collection';
56         $this->template = '/toolproxy';
57         $this->formats[] = 'application/vnd.ims.lti.v2.toolproxy+json';
58         $this->methods[] = 'POST';
60     }
62     /**
63      * Execute the request for this resource.
64      *
65      * @param mod_lti\local\ltiservice\response $response  Response object for this request.
66      */
67     public function execute($response) {
69         $ok = $this->check_tool_proxy(null, $response->get_request_data());
70         if ($ok) {
71             $toolproxy = $this->get_service()->get_tool_proxy();
72         } else {
73             $toolproxy = null;
74             $response->set_code(401);
75         }
76         $tools = array();
78         // Ensure all required elements are present in the Tool Proxy.
79         if ($ok) {
80             $toolproxyjson = json_decode($response->get_request_data());
81             $ok = !empty($toolproxyjson);
82             if (!$ok) {
83                 debugging('Tool proxy is not properly formed JSON');
84             } else {
85                 $ok = isset($toolproxyjson->tool_profile->product_instance->product_info->product_family->vendor->code);
86                 $ok = $ok && isset($toolproxyjson->security_contract->shared_secret);
87                 $ok = $ok && isset($toolproxyjson->tool_profile->resource_handler);
88                 if (!$ok) {
89                     debugging('One or more missing elements from tool proxy: vendor code, shared secret or resource handlers');
90                 }
91             }
92         }
94         // Check all capabilities requested were offered.
95         if ($ok) {
96             $offeredcapabilities = explode("\n", $toolproxy->capabilityoffered);
97             $resources = $toolproxyjson->tool_profile->resource_handler;
98             $errors = array();
99             foreach ($resources as $resource) {
100                 if (isset($resource->message)) {
101                     foreach ($resource->message as $message) {
102                         if (!in_array($message->message_type, $offeredcapabilities)) {
103                             $errors[] = $message->message_type;
104                         } else if (isset($resource->parameter)) {
105                             foreach ($message->parameter as $parameter) {
106                                 if (isset($parameter->variable) && !in_array($parameter->variable, $offeredcapabilities)) {
107                                     $errors[] = $parameter->variable;
108                                 }
109                             }
110                         }
111                     }
112                 }
113             }
114             if (count($errors) > 0) {
115                 $ok = false;
116                 debugging('Tool proxy contains capabilities which were not offered: ' . implode(', ', $errors));
117             }
118         }
120         // Check all services requested were offered (only tool services currently supported).
121         if ($ok && isset($toolproxyjson->security_contract->tool_service)) {
122             $contexts = lti_get_contexts($toolproxyjson);
123             $profileservice = lti_get_service_by_name('profile');
124             $profileservice->set_tool_proxy($toolproxy);
125             $context = $profileservice->get_service_path() . $profileservice->get_resources()[0]->get_path() . '#';
126             $offeredservices = explode("\n", $toolproxy->serviceoffered);
127             $services = lti_get_services();
128             $tpservices = $toolproxyjson->security_contract->tool_service;
129             $errors = array();
130             foreach ($tpservices as $service) {
131                 $fqid = lti_get_fqid($contexts, $service->service);
132                 if (substr($fqid, 0, strlen($context)) !== $context) {
133                     $errors[] = $service->service;
134                 } else {
135                     $id = explode('#', $fqid, 2);
136                     $aservice = lti_get_service_by_resource_id($services, $id[1]);
137                     $classname = explode('\\', get_class($aservice));
138                     if (empty($aservice) || !in_array($classname[count($classname) - 1], $offeredservices)) {
139                         $errors[] = $service->service;
140                     }
141                 }
142             }
143             if (count($errors) > 0) {
144                 $ok = false;
145                 debugging('Tool proxy contains services which were not offered: ' . implode(', ', $errors));
146             }
147         }
149         // Extract all launchable tools from the resource handlers.
150         if ($ok) {
151             $resources = $toolproxyjson->tool_profile->resource_handler;
152             foreach ($resources as $resource) {
153                 $found = false;
154                 $tool = new \stdClass();
155                 foreach ($resource->message as $message) {
156                     if ($message->message_type == 'basic-lti-launch-request') {
157                         $found = true;
158                         $tool->path = $message->path;
159                         $tool->enabled_capability = $message->enabled_capability;
160                         $tool->parameter = $message->parameter;
161                         break;
162                     }
163                 }
164                 if (!$found) {
165                     continue;
166                 }
168                 $tool->name = $resource->resource_name->default_value;
169                 $tools[] = $tool;
170             }
171             $ok = count($tools) > 0;
172             if (!$ok) {
173                 debugging('No launchable messages found in tool proxy');
174             }
175         }
177         // Add tools and custom parameters.
178         if ($ok) {
179             $baseurl = '';
180             if (isset($toolproxyjson->tool_profile->base_url_choice[0]->default_base_url)) {
181                 $baseurl = $toolproxyjson->tool_profile->base_url_choice[0]->default_base_url;
182             }
183             $securebaseurl = '';
184             if (isset($toolproxyjson->tool_profile->base_url_choice[0]->secure_base_url)) {
185                 $securebaseurl = $toolproxyjson->tool_profile->base_url_choice[0]->secure_base_url;
186             }
187             foreach ($tools as $tool) {
188                 $config = new \stdClass();
189                 $config->lti_toolurl = "{$baseurl}{$tool->path}";
190                 $config->lti_typename = $tool->name;
191                 $config->lti_coursevisible = 1;
192                 $config->lti_forcessl = 0;
194                 $type = new \stdClass();
195                 $type->state = LTI_TOOL_STATE_PENDING;
196                 $type->toolproxyid = $toolproxy->id;
197                 $type->enabledcapability = implode("\n", $tool->enabled_capability);
198                 $type->parameter = self::lti_extract_parameters($tool->parameter);
200                 if (isset($resource->icon_info[0]->default_location->path)) {
201                     $iconpath = $resource->icon_info[0]->default_location->path;
202                     $type->icon = "{$baseurl}{$iconpath}";
203                     if (!empty($securebaseurl)) {
204                         $type->secureicon = "{$securebaseurl}{$iconpath}";
205                     }
206                 }
207                 $ok = $ok && (lti_add_type($type, $config) !== false);
208             }
209             if (isset($toolproxyjson->custom)) {
210                 lti_set_tool_settings($toolproxyjson->custom, $toolproxy->id);
211             }
212         }
214         if (!empty($toolproxy)) {
215             if ($ok) {
216                 // If all went OK accept the tool proxy.
217                 $toolproxy->state = LTI_TOOL_PROXY_STATE_ACCEPTED;
218                 $toolproxy->toolproxy = $response->get_request_data();
219                 $toolproxy->secret = $toolproxyjson->security_contract->shared_secret;
220                 $toolproxy->vendorcode = $toolproxyjson->tool_profile->product_instance->product_info->product_family->vendor->code;
222                 $url = $this->get_endpoint();
223                 $body = <<< EOD
225   "@context" : "http://purl.imsglobal.org/ctx/lti/v2/ToolProxyId",
226   "@type" : "ToolProxy",
227   "@id" : "{$url}",
228   "tool_proxy_guid" : "{$toolproxy->guid}"
230 EOD;
231                 $response->set_code(201);
232                 $response->set_content_type('application/vnd.ims.lti.v2.toolproxy.id+json');
233                 $response->set_body($body);
234             } else {
235                 // Otherwise reject the tool proxy.
236                 $toolproxy->state = LTI_TOOL_PROXY_STATE_REJECTED;
237                 $response->set_code(400);
238             }
239             lti_update_tool_proxy($toolproxy);
240         } else {
241             $response->set_code(400);
242         }
243     }
245     /**
246      * Extracts the message parameters from the tool proxy entry
247      *
248      * @param array $parameters     Parameter section of a message
249      *
250      * @return String  containing parameters
251      */
252     private static function lti_extract_parameters($parameters) {
254         $params = array();
255         foreach ($parameters as $parameter) {
256             if (isset($parameter->variable)) {
257                 $value = '$' . $parameter->variable;
258             } else {
259                 $value = $parameter->fixed;
260                 if (strlen($value) > 0) {
261                     $first = substr($value, 0, 1);
262                     if (($first == '$') || ($first == '\\')) {
263                         $value = '\\' . $value;
264                     }
265                 }
266             }
267             $params[] = "{$parameter->name}={$value}";
268         }
270         return implode("\n", $params);
272     }