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