diff --git a/plugin/xapi/admin.php b/plugin/xapi/admin.php new file mode 100644 index 0000000000..6234c85abc --- /dev/null +++ b/plugin/xapi/admin.php @@ -0,0 +1,196 @@ +getId()}"; + } + + $form = new FormValidator('frm_xapi_auth', 'post', $action); + $form->addText('username', get_lang('Username'), true); + $form->addText('password', get_lang('Password'), true); + $form->addCheckBox('enabled', get_lang('Enabled'), get_lang('Yes')); + + $form->addButtonSave(get_lang('Save')); + + if (null != $auth) { + $form->setDefaults( + [ + 'username' => $auth->getUsername(), + 'password' => $auth->getPassword(), + 'enabled' => $auth->isEnabled(), + ] + ); + } + + return $form; +} + +switch ($request->query->getAlpha('action')) { + case 'add': + $form = createForm(); + + if ($form->validate()) { + $values = $form->exportValues(); + + $auth = new LrsAuth(); + $auth + ->setUsername($values['username']) + ->setPassword($values['password']) + ->setEnabled(isset($values['enabled'])) + ->setCreatedAt( + api_get_utc_datetime(null, false, true) + ); + + $em->persist($auth); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('ItemAdded'), 'success') + ); + + header('Location: '.$pageBaseUrl); + exit; + } + + $pageActions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + $pageBaseUrl + ); + $pageContent = $form->returnForm(); + break; + case 'edit': + $auth = $em->find(LrsAuth::class, $request->query->getInt('id')); + + if (null == $auth) { + api_not_allowed(true); + } + + $form = createForm($auth); + + if ($form->validate()) { + $values = $form->exportValues(); + + $auth + ->setUsername($values['username']) + ->setPassword($values['password']) + ->setEnabled(isset($values['enabled'])) + ->setCreatedAt( + api_get_utc_datetime(null, false, true) + ); + + $em->persist($auth); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('ItemUpdated'), 'success') + ); + + header('Location: '.$pageBaseUrl); + exit; + } + + $pageActions = Display::url( + Display::return_icon('back.png', get_lang('Back'), [], ICON_SIZE_MEDIUM), + $pageBaseUrl + ); + $pageContent = $form->returnForm(); + break; + case 'delete': + $auth = $em->find(LrsAuth::class, $request->query->getInt('id')); + + if (null == $auth) { + api_not_allowed(true); + } + + $em->remove($auth); + $em->flush(); + + Display::addFlash( + Display::return_message(get_lang('ItemDeleted'), 'success') + ); + + header('Location: '.$pageBaseUrl); + exit; + case 'list': + default: + $pageActions = Display::url( + Display::return_icon('add.png', get_lang('Add'), [], ICON_SIZE_MEDIUM), + $pageBaseUrl.'?action=add' + ); + $pageContent = Display::return_message(get_lang('NoData'), 'warning'); + + $auths = $em->getRepository(LrsAuth::class)->findAll(); + + if (count($auths) > 0) { + $row = 0; + + $table = new HTML_Table(['class' => 'table table-striped table-hover']); + $table->setHeaderContents($row, 0, get_lang('Username')); + $table->setHeaderContents($row, 1, get_lang('Password')); + $table->setHeaderContents($row, 2, get_lang('Enabled')); + $table->setHeaderContents($row, 3, get_lang('CreatedAt')); + $table->setHeaderContents($row, 4, get_lang('Actions')); + + foreach ($auths as $auth) { + $row++; + + $actions = [ + Display::url( + Display::return_icon('edit.png', get_lang('Edit')), + $pageBaseUrl.'?action=edit&id='.$auth->getId() + ), + Display::url( + Display::return_icon('delete.png', get_lang('Edit')), + $pageBaseUrl.'?action=delete&id='.$auth->getId() + ) + ]; + + $table->setCellContents($row, 0, $auth->getUsername()); + $table->setCellContents($row, 1, $auth->getPassword()); + $table->setCellContents($row, 2, $auth->isEnabled() ? get_lang('Yes') : get_lang('No')); + $table->setCellContents($row, 3, api_convert_and_format_date($auth->getCreatedAt())); + $table->setCellContents($row, 4, implode(PHP_EOL, $actions)); + } + + $pageContent = $table->toHtml(); + } + break; +} + +$interbreadcrumb[] = [ + 'name' => get_lang('Administration'), + 'url' => api_get_path(WEB_CODE_PATH).'admin/index.php', +]; + +$view = new Template($plugin->get_title()); +$view->assign('actions', Display::toolbarAction('xapi_actions', [$pageActions])); +$view->assign('content', $pageContent); +$view->display_one_col_template(); diff --git a/plugin/xapi/src/Entity/LrsAuth.php b/plugin/xapi/src/Entity/LrsAuth.php new file mode 100644 index 0000000000..9df4a4ab35 --- /dev/null +++ b/plugin/xapi/src/Entity/LrsAuth.php @@ -0,0 +1,135 @@ +id; + } + + /** + * @return string + */ + public function getUsername(): string + { + return $this->username; + } + + /** + * @param string $username + * + * @return LrsAuth + */ + public function setUsername(string $username): LrsAuth + { + $this->username = $username; + + return $this; + } + + /** + * @return string + */ + public function getPassword(): string + { + return $this->password; + } + + /** + * @param string $password + * + * @return LrsAuth + */ + public function setPassword(string $password): LrsAuth + { + $this->password = $password; + + return $this; + } + + /** + * @return bool + */ + public function isEnabled(): bool + { + return $this->enabled; + } + + /** + * @param bool $enabled + * + * @return LrsAuth + */ + public function setEnabled(bool $enabled): LrsAuth + { + $this->enabled = $enabled; + + return $this; + } + + /** + * @return \DateTime + */ + public function getCreatedAt(): DateTime + { + return $this->createdAt; + } + + /** + * @param \DateTime $createdAt + * + * @return LrsAuth + */ + public function setCreatedAt(DateTime $createdAt): LrsAuth + { + $this->createdAt = $createdAt; + + return $this; + } +} diff --git a/plugin/xapi/src/Lrs/LrsRequest.php b/plugin/xapi/src/Lrs/LrsRequest.php index fbd7ac9a28..a77ab758d1 100644 --- a/plugin/xapi/src/Lrs/LrsRequest.php +++ b/plugin/xapi/src/Lrs/LrsRequest.php @@ -6,7 +6,10 @@ namespace Chamilo\PluginBundle\XApi\Lrs; use Symfony\Component\HttpFoundation\Request as HttpRequest; use Symfony\Component\HttpFoundation\Response as HttpResponse; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Xabbuh\XApi\Common\Exception\AccessDeniedException; /** * Class LrsRequest. @@ -30,6 +33,8 @@ class LrsRequest public function send() { + $this->validAuth(); + $version = $this->request->headers->get('X-Experience-API-Version'); if (null === $version) { @@ -101,4 +106,44 @@ class LrsRequest return false; } + + /** + * @return bool + */ + private function validAuth() + { + if (!$this->request->headers->has('Authorization')) { + throw new AccessDeniedHttpException(); + } + + $authHeader = $this->request->headers->get('Authorization'); + + $parts = explode('Basic ', $authHeader, 2); + + if (empty($parts[1])) { + throw new AccessDeniedHttpException(); + } + + $authDecoded = base64_decode($parts[1]); + + $parts = explode(':', $authDecoded, 2); + + if (empty($parts) || count($parts) !== 2) { + throw new AccessDeniedHttpException(); + } + + list($username, $password) = $parts; + + $auth = \Database::getManager() + ->getRepository(\LrsAuth::class) + ->findOneBy( + ['username' => $username, 'password' => $password] + ); + + if (null == $auth) { + throw new AccessDeniedHttpException(); + } + + return true; + } } diff --git a/plugin/xapi/src/XApiPlugin.php b/plugin/xapi/src/XApiPlugin.php index 46d6412fb9..f39e046665 100644 --- a/plugin/xapi/src/XApiPlugin.php +++ b/plugin/xapi/src/XApiPlugin.php @@ -13,7 +13,6 @@ use Http\Adapter\Guzzle6\Client; use Http\Message\MessageFactory\GuzzleMessageFactory; use Ramsey\Uuid\Uuid; use Xabbuh\XApi\Client\XApiClientBuilder; -use Xabbuh\XApi\Client\XApiClientBuilderInterface; use Xabbuh\XApi\Model\IRI; /** @@ -104,6 +103,7 @@ class XApiPlugin extends Plugin implements HookPluginInterface [ 'xapi_shared_statement', 'xapi_tool_launch', + 'xapi_lrs_auth', 'xapi_attachment', 'xapi_object', @@ -166,6 +166,7 @@ class XApiPlugin extends Plugin implements HookPluginInterface [ $em->getClassMetadata(SharedStatement::class), $em->getClassMetadata(ToolLaunch::class), + $em->getClassMetadata(LrsAuth::class), ] ); @@ -431,6 +432,7 @@ class XApiPlugin extends Plugin implements HookPluginInterface [ $em->getClassMetadata(SharedStatement::class), $em->getClassMetadata(ToolLaunch::class), + $em->getClassMetadata(LrsAuth::class), ] ); @@ -513,5 +515,19 @@ class XApiPlugin extends Plugin implements HookPluginInterface Database::getManager() ->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link') ->execute(['category' => 'plugin', 'link' => 'xapi/tincan/index.php%']); + + Database::getManager() + ->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.category = :category AND t.link LIKE :link') + ->execute(['category' => 'plugin', 'link' => 'xapi/cmi5/index.php%']); + } + + /** + * @inheritDoc + */ + public function getAdminUrl() + { + $webPath = api_get_path(WEB_PLUGIN_PATH).$this->get_name(); + + return "$webPath/admin.php"; } }