src/LtiBundle/Controller/ServiceController.php
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\LtiBundle\Controller;
use Chamilo\CoreBundle\Controller\BaseController;
use Chamilo\LtiBundle\Component\OutcomeDeleteRequest;
use Chamilo\LtiBundle\Component\OutcomeReadRequest;
use Chamilo\LtiBundle\Component\OutcomeReplaceRequest;
use Chamilo\LtiBundle\Component\OutcomeResponse;
use Chamilo\LtiBundle\Component\OutcomeUnsupportedRequest;
use Chamilo\LtiBundle\Entity\ExternalTool;
use Doctrine\Persistence\ManagerRegistry;
use OAuthUtil;
use SimpleXMLElement;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
class ServiceController extends BaseController
{
public function __construct(
private ManagerRegistry $managerRegistry,
private TranslatorInterface $translator,
) {}
#[Route(path: '/lti/os', name: 'chamilo_lti_os')]
public function outcomeService(Request $request): Response
{
$em = $this->managerRegistry->getManager();
$toolRepo = $em->getRepository(ExternalTool::class);
$headers = $request->headers;
if (empty($headers->get('authorization'))) {
throw $this->createAccessDeniedException();
}
$authParams = OAuthUtil::split_header($headers['Authorization']);
if (empty($authParams) || empty($authParams['oauth_consumer_key']) || empty($authParams['oauth_signature'])) {
throw $this->createAccessDeniedException();
}
$course = $this->getCourse();
$tools = $toolRepo->findBy([
'consumerKey' => $authParams['oauth_consumer_key'],
]);
$url = $this->generateUrl('chamilo_lti_os', [
'code' => $course->getCode(),
]);
$toolIsFound = false;
/** @var ExternalTool $tool */
foreach ($tools as $tool) {
$signatureIsValid = $this->compareRequestSignature(
$url,
$authParams['oauth_consumer_key'],
$authParams['oauth_signature'],
$tool
);
if ($signatureIsValid) {
$toolIsFound = true;
break;
}
}
if (!$toolIsFound) {
throw $this->createNotFoundException('External tool not found. Signature is not valid');
}
$body = file_get_contents('php://input');
$bodyHash = base64_encode(sha1($body, true));
if ($bodyHash !== $authParams['oauth_body_hash']) {
throw $this->createAccessDeniedException('Request is not valid.');
}
$process = $this->processServiceRequest();
$response = new Response($process);
$response->headers->set('Content-Type', 'application/xml');
return $response;
}
private function processServiceRequest(): ?OutcomeResponse
{
$requestContent = file_get_contents('php://input');
if (empty($requestContent)) {
return null;
}
$xml = new SimpleXMLElement($requestContent);
if (empty($xml)) {
return null;
}
$bodyChildren = $xml->imsx_POXBody->children();
if (empty($bodyChildren)) {
return null;
}
$name = $bodyChildren->getName();
switch ($name) {
case 'replaceResultRequest':
$serviceRequest = new OutcomeReplaceRequest($xml);
break;
case 'readResultRequest':
$serviceRequest = new OutcomeReadRequest($xml);
break;
case 'deleteResultRequest':
$serviceRequest = new OutcomeDeleteRequest($xml);
break;
default:
$name = str_replace(['ResultRequest', 'Request'], '', $name);
$serviceRequest = new OutcomeUnsupportedRequest($xml, $name);
break;
}
$serviceRequest->setEntityManager($this->managerRegistry->getManager());
$serviceRequest->setTranslator($this->translator);
return $serviceRequest->process();
}
}