ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
class.ilLTIConsumerContentGUI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
31 {
32  public const CMD_LAUNCH = 'launch';
33  public const CMD_SHOW_EMBEDDED = 'showEmbedded';
34 
36 
38 
39  private \ILIAS\DI\Container $dic;
40 
41  private ilLanguage $lng;
42 
43  private ilObjUser $user;
44 
45  public function __construct(ilObjLTIConsumer $object)
46  {
47  global $DIC;
48  $this->dic = $DIC;
49  $this->lng = $DIC->language();
50  $this->user = $DIC->user();
51  $this->object = $object;
52  }
53 
57  public function executeCommand(): void
58  {
59  global $DIC; /* @var \ILIAS\DI\Container $DIC */
60 
61  if ($this->object->getProvider()->getAvailability() == ilLTIConsumeProvider::AVAILABILITY_NONE) {
62  throw new ilLtiConsumerException('access denied!');
63  }
64  $command = $DIC->ctrl()->getCmd(self::CMD_LAUNCH);
65  $this->{$command}();
66  }
67 
72  protected function launch(): void
73  {
74  if ($this->object->getProvider()->getLtiVersion() == "LTI-1p0") {
75  if ($this->object->isLaunchMethodEmbedded()) {
76  $tpl = new ilTemplate('tpl.lti_content.html', true, true, 'components/ILIAS/LTIConsumer');
77  $tpl->setVariable("EMBEDDED_IFRAME_SRC", $this->dic->ctrl()->getLinkTarget(
78  $this,
79  self::CMD_SHOW_EMBEDDED
80  ));
81  $this->dic->ui()->mainTemplate()->setContent($tpl->get());
82  } else {
83  $this->dic->toolbar()->addText($this->getStartButtonTxt11());
84  }
85  } else {
86 
87  if ($this->object->isLaunchMethodEmbedded() && (ilSession::get('lti13_login_data') == null)) {
88  $tpl = new ilTemplate('tpl.lti_content.html', true, true, 'components/ILIAS/LTIConsumer');
89  $tpl->setVariable("EMBEDDED_IFRAME_SRC", $this->dic->ctrl()->getLinkTarget(
90  $this,
91  self::CMD_SHOW_EMBEDDED
92  ));
93  $this->dic->ui()->mainTemplate()->setContent($tpl->get());
94  } else {
95  if (ilSession::get('lti13_login_data') != null) {
96  $form = $this->getLoginLTI13Form();
97  if ($form == null) {
98  // $this->dic->ui()->mainTemplate()->setOnScreenMessage('failure', 'initialLogin Error: ' . $err, true);
99  $this->dic->ui()->mainTemplate()->setOnScreenMessage('failure', 'initialLogin Error: ', true);
100  } else {
101  $response = $this->dic->http()->response()->withBody(ILIAS\Filesystem\Stream\Streams::ofString($form));
102  $this->dic->http()->saveResponse($response);
103  $this->dic->http()->sendResponse();
104  $this->dic->http()->close();
105  }
106  } elseif (!$this->object->isLaunchMethodEmbedded()) {
107  $this->dic->toolbar()->addText($this->getStartButtonTxt13());
108  }
109  }
110  }
111  }
112 
113  protected function getLoginLTI13Form(): ?string
114  {
115  $loginData = ilSession::get('lti13_login_data');
116  ilSession::clear('lti13_login_data');
117  $err = $this->validateLTI13InitalLogin($loginData);
118  if ($err !== null) {
119  return null;
120  } else {
121  $this->initCmixUser();
122  $params = $this->getLaunchParametersLTI13($loginData['redirect_uri'], $this->object->getProvider()->getClientId(), $this->object->getProvider()->getId(), $loginData['nonce']);
123  if (isset($loginData['state'])) {
124  $params['state'] = $loginData['state'];
125  }
126  ilSession::clear('lti_message_hint');
127  $r = '<form action="' . $loginData['redirect_uri'] . "\" name=\"ltiAuthForm\" id=\"ltiAuthForm\" " .
128  "method=\"post\" enctype=\"application/x-www-form-urlencoded\">\n";
129  if (!empty($params)) {
130  foreach ($params as $key => $value) {
131  $key = htmlspecialchars($key);
132  $value = htmlspecialchars($value);
133  $r .= " <input type=\"hidden\" name=\"{$key}\" value=\"{$value}\"/>\n";
134  }
135  }
136  $r .= "</form>\n";
137  $r .= "<script type=\"text/javascript\">\n" .
138  "//<![CDATA[\n" .
139  "document.ltiAuthForm.submit();\n" .
140  "//]]>\n" .
141  "</script>\n";
142  return $r;
143  }
144  return null;
145  }
146 
147  protected function getStartButtonTxt11(): string
148  {
149  if ($this->object->getOfflineStatus() ||
150  $this->object->isLaunchMethodEmbedded() ||
151  $this->object->getProvider()->getAvailability() == ilLTIConsumeProvider::AVAILABILITY_NONE) {
152  return "";
153  }
154 
155  $cmixUser = new ilCmiXapiUser(
156  $this->object->getId(),
157  $this->user->getId(),
158  $this->object->getProvider()->getPrivacyIdent()
159  );
160  $user_ident = $cmixUser->getUsrIdent();
161  if ($user_ident == '' || $user_ident == null) {
162  $user_ident = ilCmiXapiUser::getIdent($this->object->getProvider()->getPrivacyIdent(), $this->dic->user());
163  $cmixUser->setUsrIdent($user_ident);
164  $cmixUser->save();
165  }
166  $ilLTIConsumerLaunch = new ilLTIConsumerLaunch($this->object->getRefId());
167  $context = $ilLTIConsumerLaunch->getContext();
168  $contextType = $ilLTIConsumerLaunch::getLTIContextType($context["type"]);
169  $contextId = (string) $context["id"];
170  $contextTitle = $context["title"];
171 
173  $this->dic->user()->getId(),
174  $this->object->getRefId(),
175  $this->object->getId()
176  );
177 
178  $returnUrl = !$this->object->isLaunchMethodOwnWin() ? '' : str_replace(
179  '&amp;',
180  '&',
181  ilObjLTIConsumer::getIliasHttpPath() . "/" . $this->dic->ctrl()->getLinkTarget($this, "", "", false)
182  );
183 
184  $launchParameters = $this->object->buildLaunchParameters(
185  $cmixUser,
186  $token,
187  $contextType,
188  $contextId,
189  $contextTitle,
190  $returnUrl
191  );
192 
193  $target = $this->object->getLaunchMethod() == "newWin" ? "_blank" : "_self";
194  $button = '<input class="btn btn-default ilPre" type="button" onClick="ltilaunch()" value = "' . $this->lng->txt("show_content") . '" />';
195  $output = '<form id="lti_launch_form" name="lti_launch_form" action="' . $this->object->getProvider()->getProviderUrl() . '" method="post" target="' . $target . '" encType="application/x-www-form-urlencoded">';
196  foreach ($launchParameters as $field => $value) {
197  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', $field, $value) . "\n";
198  }
199  $output .= $button;
200  $output .= '</form>';
201  $output .= '<span id ="lti_launched" style="display:none">' . $this->lng->txt("launched") . '</span>';
202  $output .= '<script type="text/javascript">
203  function ltilaunch() {
204  document.lti_launch_form.submit();
205  document.getElementById("lti_launch_form").style.display = "none";
206  document.getElementById("lti_launched").style.display = "inline";
207  }</script>';
208  return($output);
209  }
210 
211  protected function getStartButtonTxt13(): string
212  {
213  if ($this->object->getOfflineStatus() ||
214  $this->object->isLaunchMethodEmbedded() ||
215  $this->object->getProvider()->getAvailability() == ilLTIConsumeProvider::AVAILABILITY_NONE) {
216  return "";
217  }
218  $this->initCmixUser();
219  $user_ident = $this->cmixUser->getUsrIdent();
220  $ilLTIConsumerLaunch = new ilLTIConsumerLaunch($this->object->getRefId());
221  $context = $ilLTIConsumerLaunch->getContext();
222  $contextType = $ilLTIConsumerLaunch::getLTIContextType($context["type"]);
223  $contextId = $context["id"];
224  $contextTitle = $context["title"];
225 
227  $this->dic->user()->getId(),
228  $this->object->getRefId(),
229  $this->object->getId()
230  );
231 
232  $returnUrl = !$this->object->isLaunchMethodOwnWin() ? '' : str_replace(
233  '&amp;',
234  '&',
235  ilObjLTIConsumer::getIliasHttpPath() . "/" . $this->dic->ctrl()->getLinkTarget($this, "", "", false)
236  );
237 
238  $target = $this->object->getLaunchMethod() == "newWin" ? "_blank" : "_self";
239  $button = '<input class="btn btn-default ilPre" type="button" onClick="ltilaunch()" value = "' . $this->lng->txt("show_content") . '" />';
240  $ltiMessageHint = (string) $this->object->getRefId() . ":" . CLIENT_ID;
241  ilSession::set('lti_message_hint', $ltiMessageHint);
242  $output = '<form id="lti_launch_form" name="lti_launch_form" action="' . $this->object->getProvider()->getInitiateLogin() . '" method="post" target="' . $target . '" encType="application/x-www-form-urlencoded">';
243 
244  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'iss', ilObjLTIConsumer::getIliasHttpPath()) . "\n";
245  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'target_link_uri', $this->object->getProvider()->getProviderUrl()) . "\n";
246  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'login_hint', $user_ident) . "\n";
247  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'lti_message_hint', $ltiMessageHint) . "\n";
248  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'client_id', $this->object->getProvider()->getClientId()) . "\n";
249  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'lti_deployment_id', $this->object->getProvider()->getId()) . "\n";
250  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'launch_presentation_return_url', $returnUrl) . "\n";
251  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'lis_result_sourcedid', $token) . "\n";
252  $output .= $button;
253  $output .= '</form>';
254  $output .= '<span id ="lti_launched" style="display:none">' . $this->lng->txt("launched") . '</span>';
255  $output .= '<script type="text/javascript">
256  function ltilaunch() {
257  document.lti_launch_form.submit();
258  document.getElementById("lti_launch_form").style.display = "none";
259  document.getElementById("lti_launched").style.display = "inline";
260  }</script>';
261  //dump($output);exit();
262  return($output);
263  }
264 
265  // TODO: merge with getStartButtonTxt13 (paramter)
266  protected function getEmbeddedAutoStartFormular(): string
267  {
268  $this->initCmixUser();
269  $user_ident = $this->cmixUser->getUsrIdent();
270  $ilLTIConsumerLaunch = new ilLTIConsumerLaunch($this->object->getRefId());
271  $context = $ilLTIConsumerLaunch->getContext();
272  $contextType = $ilLTIConsumerLaunch::getLTIContextType($context["type"]);
273  $contextId = $context["id"];
274  $contextTitle = $context["title"];
275 
276  $target = "_self";
277  $output = '';
278  $ltiMessageHint = (string) $this->object->getRefId() . ":" . CLIENT_ID;
279  ilSession::set('lti_message_hint', $ltiMessageHint);
280  $output = '<form id="lti_launch_form" name="lti_launch_form" action="' . $this->object->getProvider()->getInitiateLogin() . '" method="post" target="' . $target . '" encType="application/x-www-form-urlencoded">';
281  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'iss', ilObjLTIConsumer::getIliasHttpPath()) . "\n";
282  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'target_link_uri', $this->object->getProvider()->getProviderUrl()) . "\n";
283  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'login_hint', $user_ident) . "\n";
284  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'lti_message_hint', $ltiMessageHint) . "\n";
285  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'client_id', $this->object->getProvider()->getClientId()) . "\n";
286  $output .= sprintf('<input type="hidden" name="%s" value="%s" />', 'lti_deployment_id', $this->object->getProvider()->getId()) . "\n";
287  $output .= '</form>';
288 
289  $output .= "<script type=\"text/javascript\">\n" .
290  "//<![CDATA[\n" .
291  "document.lti_launch_form.submit();\n" .
292  "//]]>\n" .
293  "</script>\n";
294 
295  return($output);
296  }
297 
298 
302  protected function showEmbedded(): void
303  {
304  if ($this->object->getProvider()->getLtiVersion() == "LTI-1p0") {
305  $this->initCmixUser();
306  $tpl = new ilTemplate('tpl.lti_embedded.html', true, true, 'components/ILIAS/LTIConsumer');
307  foreach ($this->getLaunchParameters() as $field => $value) {
308  $tpl->setCurrentBlock('launch_parameter');
309  $tpl->setVariable('LAUNCH_PARAMETER', $field);
310  $tpl->setVariable('LAUNCH_PARAM_VALUE', $value);
311  $tpl->parseCurrentBlock();
312  }
313 
314  $v = DEVMODE ? '?vers=' . time() : '?vers=' . ILIAS_VERSION_NUMERIC;
315  $tpl->setVariable("DELOS_CSS_HREF", 'assets/css/delos.css' . $v);
316  $tpl->setVariable("JQUERY_SRC", 'assets/js/jquery.js' . $v);
317 
318  $tpl->setVariable("LOADER_ICON_SRC", ilUtil::getImagePath("media/loader.svg"));
319  $tpl->setVariable('LAUNCH_URL', $this->object->getProvider()->getProviderUrl());
320 
321  #$DIC->ui()->mainTemplate()->getStandardTemplate();
322  #$DIC->ui()->mainTemplate()->setContent($tpl->get());
323 
324  echo $tpl->get();
325  exit; //TODO: no exit
326  } else {
327  $response = $this->dic->http()->response()->withBody(ILIAS\Filesystem\Stream\Streams::ofString($this->getEmbeddedAutoStartFormular()));
328  $this->dic->http()->saveResponse($response);
329  $this->dic->http()->sendResponse();
330  $this->dic->http()->close();
331  }
332  }
333 
334  protected function getLaunchParameters(): array
335  {
336  $ilLTIConsumerLaunch = new ilLTIConsumerLaunch($this->object->getRefId());
337  $launchContext = $ilLTIConsumerLaunch->getContext();
338 
339  $launchContextType = ilLTIConsumerLaunch::getLTIContextType($launchContext["type"]);
340  $launchContextId = (string) $launchContext["id"];
341  $launchContextTitle = $launchContext["title"];
342 
344  $this->dic->user()->getId(),
345  $this->object->getRefId(),
346  $this->object->getId()
347  );
348 
349  return $this->object->buildLaunchParameters(
350  $this->cmixUser,
351  $token,
352  $launchContextType,
353  $launchContextId,
354  $launchContextTitle
355  );
356  }
357 
358  protected function getLaunchParametersLTI13(string $endpoint, string $clientId, int $deploymentId, string $nonce): ?array
359  {
360  $ilLTIConsumerLaunch = new ilLTIConsumerLaunch($this->object->getRefId());
361  $launchContext = $ilLTIConsumerLaunch->getContext();
362 
363  $launchContextType = ilLTIConsumerLaunch::getLTIContextType($launchContext["type"]);
364  $launchContextId = (string) $launchContext["id"];
365  $launchContextTitle = $launchContext["title"];
366 
368  $this->dic->user()->getId(),
369  $this->object->getRefId(),
370  $this->object->getId()
371  );
372 
373  $cmixUser = $this->cmixUser;
374  return $this->object->buildLaunchParametersLTI13(
375  $cmixUser,
376  $token,
377  $endpoint,
378  $clientId,
379  $deploymentId,
380  $nonce,
381  $launchContextType,
382  $launchContextId,
383  $launchContextTitle
384  );
385  }
386 
387  public static function isEmbeddedLaunchRequest(): bool
388  {
389  global $DIC; /* @var \ILIAS\DI\Container $DIC */
390 
391  if ($DIC->ctrl()->getNextClass() != strtolower(self::class)) {
392  return false;
393  }
394 
395  if ($DIC->ctrl()->getCmd() != self::CMD_SHOW_EMBEDDED) {
396  return false;
397  }
398 
399  return true;
400  }
401 
402  protected function initCmixUser(): void
403  {
404  $this->cmixUser = new ilCmiXapiUser($this->object->getId(), $this->dic->user()->getId(), $this->object->getProvider()->getPrivacyIdent());
405  $user_ident = $this->cmixUser->getUsrIdent();
406  if ($user_ident == '' || $user_ident == null) {
407  $user_ident = ilCmiXapiUser::getIdent($this->object->getProvider()->getPrivacyIdent(), $this->dic->user());
408  $this->cmixUser->setUsrIdent($user_ident);
409  $this->cmixUser->save();
410  }
411  }
412 
413  private function validateLTI13InitalLogin(array $loginData): ?string
414  {
415  $scope = $loginData['scope'];
416  $responsetype = $loginData['response_type'];
417  $clientid = $loginData['client_id'];
418  $redirecturi = $loginData['redirect_uri'];
419  $loginhint = $loginData['login_hint'];
420  $ltimessagehint = $loginData['lti_message_hint'];
421  $state = $loginData['state'];
422  $responsemode = $loginData['response_mode'];
423  $nonce = $loginData['nonce'];
424  $prompt = $loginData['prompt'];
425 
426  $ok = !empty($scope) && !empty($responsetype) && !empty($clientid) &&
427  !empty($redirecturi) && !empty($loginhint) &&
428  !empty($nonce) && (ilSession::get('lti_message_hint') != null);
429 
430  if (!$ok) {
431  $error = 'invalid_request';
432  }
433  if ($ok && ($scope !== 'openid')) {
434  $ok = false;
435  $error = 'invalid_scope';
436  }
437  if ($ok && ($responsetype !== 'id_token')) {
438  $ok = false;
439  $error = 'unsupported_response_type';
440  }
441  if ($ok) {
442  list($ref_id, $ilias_client_id) = explode(':', ilSession::get('lti_message_hint'), 2);
443  if ((int) $this->object->getRefId() !== (int) $ref_id) {
444  $ok = false;
445  $error = 'invalid_request';
446  }
447  if ($this->object->getProvider()->getClientId() !== $clientid) {
448  $ok = false;
449  $error = 'unauthorized_client';
450  }
451  }
452 
453  if ($ok) {
454  $cmixUser = new ilCmiXapiUser(
455  $this->object->getId(),
456  $this->user->getId(),
457  $this->object->getProvider()->getPrivacyIdent()
458  );
459  $user_ident = $cmixUser->getUsrIdent();
460  // required?
461  if ($user_ident == '' || $user_ident == null) {
462  $user_ident = ilCmiXapiUser::getIdent($this->object->getProvider()->getPrivacyIdent(), $this->dic->user());
463  $cmixUser->setUsrIdent($user_ident);
464  $cmixUser->save();
465  }
466 
467  if ((string) $loginhint !== $user_ident) {
468  $ok = false;
469  $error = 'access_denied';
470  }
471  }
472  $uris = array_map("trim", explode(",", $this->object->getProvider()->getRedirectionUris()));
473  if (!in_array($redirecturi, $uris)) {
474  $ok = false;
475  $error = 'invalid_request';
476  }
477 
478  if ($ok) {
479  if (isset($responsemode)) {
480  $ok = ($responsemode === 'form_post');
481  if (!$ok) {
482  $error = 'invalid_request';
483  $desc = 'Invalid response_mode';
484  }
485  } else {
486  $ok = false;
487  $error = 'invalid_request';
488  $desc = 'Missing response_mode';
489  }
490  }
491  if ($ok && !empty($prompt) && ($prompt !== 'none')) {
492  $ok = false;
493  $error = 'invalid_request';
494  $desc = 'Invalid prompt';
495  }
496  if ($ok) {
497  return null;
498  } else {
499  return $error;
500  }
501  }
502 
503  // TODO: request_wrapper?
504 
510  protected function getRequestValue(string $key, $default = null)
511  {
512  global $DIC;
513  if (isset($DIC->http()->request()->getQueryParams()[$key])) {
514  return $DIC->http()->request()->getQueryParams()[$key];
515  }
516 
517  if (isset($DIC->http()->request()->getParsedBody()[$key])) {
518  return $DIC->http()->request()->getParsedBody()[$key];
519  }
520 
521  return $default ?? null;
522  }
523 }
static get(string $a_var)
$scope
Definition: ltiregstart.php:51
$context
Definition: webdav.php:31
getRequestValue(string $key, $default=null)
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:31
Interface Observer Contains several chained tasks and infos about them.
getLaunchParametersLTI13(string $endpoint, string $clientId, int $deploymentId, string $nonce)
$clientId
Definition: ltiregend.php:26
$response
Definition: xapitoken.php:90
getContext(?array $a_valid_types=array('crs', 'grp', 'cat', 'root'))
get info about the context in which the link is used The most outer matching course or group is used ...
static fillToken(int $usrId, int $refId, int $objId, int $lrsTypeId=0)
static getIdent(int $userIdentMode, ilObjUser $user)
const ILIAS_VERSION_NUMERIC
$ref_id
Definition: ltiauth.php:66
$token
Definition: xapitoken.php:67
const CLIENT_ID
Definition: constants.php:41
setUsrIdent(string $usrIdent)
global $DIC
Definition: shib_login.php:25
__construct(ilObjLTIConsumer $object)
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
$ltiMessageHint
Definition: ltiauth.php:50
static getLTIContextType(string $a_type)
static clear(string $a_var)
static set(string $a_var, $a_val)
Set a value.
$r