Passed
Pull Request — master (#5329)
by Angel Fernando Quiroz
09:14
created

ServiceController::processServiceRequest()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 50
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 27
c 1
b 0
f 0
nc 7
nop 0
dl 0
loc 50
rs 8.5546
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\LtiBundle\Controller;
8
9
use Chamilo\CoreBundle\Controller\BaseController;
10
use Chamilo\LtiBundle\Component\OutcomeDeleteRequest;
11
use Chamilo\LtiBundle\Component\OutcomeReadRequest;
12
use Chamilo\LtiBundle\Component\OutcomeReplaceRequest;
13
use Chamilo\LtiBundle\Component\OutcomeResponse;
14
use Chamilo\LtiBundle\Component\OutcomeUnsupportedRequest;
15
use Chamilo\LtiBundle\Entity\ExternalTool;
16
use Doctrine\Persistence\ManagerRegistry;
17
use OAuthUtil;
18
use SimpleXMLElement;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\Routing\Attribute\Route;
22
use Symfony\Contracts\Translation\TranslatorInterface;
23
24
class ServiceController extends BaseController
25
{
26
    public function __construct(
27
        private ManagerRegistry $managerRegistry,
28
        private TranslatorInterface $translator,
29
    ) {}
30
31
    #[Route(path: '/lti/os', name: 'chamilo_lti_os')]
32
    public function outcomeService(Request $request): Response
33
    {
34
        $em = $this->managerRegistry->getManager();
35
        $toolRepo = $em->getRepository(ExternalTool::class);
36
37
        $headers = $request->headers;
38
39
        if (empty($headers->get('authorization'))) {
40
            throw $this->createAccessDeniedException();
41
        }
42
43
        $authParams = OAuthUtil::split_header($headers['Authorization']);
44
45
        if (empty($authParams) || empty($authParams['oauth_consumer_key']) || empty($authParams['oauth_signature'])) {
46
            throw $this->createAccessDeniedException();
47
        }
48
49
        $course = $this->getCourse();
50
        $tools = $toolRepo->findBy([
51
            'consumerKey' => $authParams['oauth_consumer_key'],
52
        ]);
53
        $url = $this->generateUrl('chamilo_lti_os', [
54
            'code' => $course->getCode(),
55
        ]);
56
57
        $toolIsFound = false;
58
59
        /** @var ExternalTool $tool */
60
        foreach ($tools as $tool) {
61
            $signatureIsValid = $this->compareRequestSignature(
62
                $url,
63
                $authParams['oauth_consumer_key'],
64
                $authParams['oauth_signature'],
65
                $tool
66
            );
67
68
            if ($signatureIsValid) {
69
                $toolIsFound = true;
70
71
                break;
72
            }
73
        }
74
75
        if (!$toolIsFound) {
76
            throw $this->createNotFoundException('External tool not found. Signature is not valid');
77
        }
78
79
        $body = file_get_contents('php://input');
80
        $bodyHash = base64_encode(sha1($body, true));
81
82
        if ($bodyHash !== $authParams['oauth_body_hash']) {
83
            throw $this->createAccessDeniedException('Request is not valid.');
84
        }
85
86
        $process = $this->processServiceRequest();
87
88
        $response = new Response($process);
89
        $response->headers->set('Content-Type', 'application/xml');
90
91
        return $response;
92
    }
93
94
    private function processServiceRequest(): ?OutcomeResponse
95
    {
96
        $requestContent = file_get_contents('php://input');
97
98
        if (empty($requestContent)) {
99
            return null;
100
        }
101
102
        $xml = new SimpleXMLElement($requestContent);
103
104
        if (empty($xml)) {
105
            return null;
106
        }
107
108
        $bodyChildren = $xml->imsx_POXBody->children();
109
110
        if (empty($bodyChildren)) {
111
            return null;
112
        }
113
114
        $name = $bodyChildren->getName();
115
116
        switch ($name) {
117
            case 'replaceResultRequest':
118
                $serviceRequest = new OutcomeReplaceRequest($xml);
119
120
                break;
121
122
            case 'readResultRequest':
123
                $serviceRequest = new OutcomeReadRequest($xml);
124
125
                break;
126
127
            case 'deleteResultRequest':
128
                $serviceRequest = new OutcomeDeleteRequest($xml);
129
130
                break;
131
132
            default:
133
                $name = str_replace(['ResultRequest', 'Request'], '', $name);
134
135
                $serviceRequest = new OutcomeUnsupportedRequest($xml, $name);
136
137
                break;
138
        }
139
140
        $serviceRequest->setEntityManager($this->managerRegistry->getManager());
141
        $serviceRequest->setTranslator($this->translator);
142
143
        return $serviceRequest->process();
144
    }
145
}
146