Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#817)
by Beatrycze
03:51
created

PageViewProxy   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 68
dl 0
loc 190
rs 10
c 1
b 0
f 0
wmc 17

7 Methods

Rating   Name   Duplication   Size   Complexity  
A withCorsResponseHeaders() 0 10 2
A __construct() 0 4 1
A copyHeaders() 0 16 3
A handleOptions() 0 6 1
A main() 0 16 4
A handleGet() 0 46 4
A handleHead() 0 25 2
1
<?php
2
3
/**
4
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
5
 *
6
 * This file is part of the Kitodo and TYPO3 projects.
7
 *
8
 * @license GNU General Public License version 3 or later.
9
 * For the full copyright and license information, please read the
10
 * LICENSE.txt file that was distributed with this source code.
11
 */
12
13
namespace Kitodo\Dlf\Eid;
14
15
use Kitodo\Dlf\Common\Helper;
16
use Kitodo\Dlf\Common\StdOutStream;
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
20
use TYPO3\CMS\Core\Http\JsonResponse;
21
use TYPO3\CMS\Core\Http\RequestFactory;
22
use TYPO3\CMS\Core\Http\Response;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25
/**
26
 * eID image proxy for plugin 'Page View' of the 'dlf' extension
27
 *
28
 * Supported query parameters:
29
 * - `url` (mandatory): The URL to be proxied
30
 * - `uHash` (mandatory): HMAC of the URL
31
 *
32
 * @author Alexander Bigga <[email protected]>
33
 * @package TYPO3
34
 * @subpackage dlf
35
 * @access public
36
 */
37
class PageViewProxy
38
{
39
    /**
40
     * @var RequestFactory
41
     */
42
    protected $requestFactory;
43
44
    /**
45
     * @var mixed
46
     */
47
    protected $extConf;
48
49
    public function __construct()
50
    {
51
        $this->requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
52
        $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
53
    }
54
55
    /**
56
     * Return a response that is derived from $response and contains CORS
57
     * headers to be sent to the client.
58
     *
59
     * @return ResponseInterface $response
60
     * @return ServerRequestInterface $request The incoming request.
61
     * @return ResponseInterface
62
     */
63
    protected function withCorsResponseHeaders(
64
        ResponseInterface $response,
65
        ServerRequestInterface $request
66
    ): ResponseInterface {
67
        $origin = (string) ($request->getHeaderLine('Origin') ? : '*');
68
69
        return $response
70
            ->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS, HEAD')
71
            ->withHeader('Access-Control-Allow-Origin', $origin)
72
            ->withHeader('Access-Control-Max-Age', '86400');
73
    }
74
75
    /**
76
     * Takes headers listed in $headerNames from $fromResponse, adds them to
77
     * $toResponse and returns the result.
78
     *
79
     * @param ResponseInterface $fromResponse
80
     * @param ResponseInterface $toResponse
81
     * @param array $headerNames
82
     * @return ResponseInterface
83
     */
84
    protected function copyHeaders(
85
        ResponseInterface $fromResponse,
86
        ResponseInterface $toResponse,
87
        array $headerNames
88
    ) {
89
        $result = $toResponse;
90
91
        foreach ($headerNames as $headerName) {
92
            $headerValues = $fromResponse->getHeader($headerName);
93
            // Don't include empty header field when not present
94
            if (!empty($headerValues)) {
95
                $result = $result->withAddedHeader($headerName, $headerValues);
96
            }
97
        }
98
99
        return $result;
100
    }
101
102
    /**
103
     * Handle an OPTIONS request.
104
     *
105
     * @param ServerRequestInterface $request
106
     * @return ResponseInterface
107
     */
108
    protected function handleOptions(ServerRequestInterface $request): ResponseInterface
109
    {
110
        // 204 No Content
111
        $response = GeneralUtility::makeInstance(Response::class)
112
            ->withStatus(204);
113
        return $this->withCorsResponseHeaders($response, $request);
114
    }
115
116
    /**
117
     * Handle an HEAD request.
118
     *
119
     * @param ServerRequestInterface $request
120
     * @return ResponseInterface
121
     */
122
    protected function handleHead(ServerRequestInterface $request): ResponseInterface
123
    {
124
        $queryParams = $request->getQueryParams();
125
126
        $url = (string) ($queryParams['url'] ?? '');
127
        try {
128
            $targetResponse = $this->requestFactory->request($url, 'HEAD', [
129
                'headers' => [
130
                    'User-Agent' => $this->extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
131
                ]
132
            ]);
133
        } catch (\Exception $e) {
134
            return new JsonResponse(['message' => 'Could not fetch resource of given URL.'], 500);
135
        }
136
137
        $clientResponse = GeneralUtility::makeInstance(Response::class)
138
            ->withStatus($targetResponse->getStatusCode());
139
140
        $clientResponse = $this->copyHeaders($targetResponse, $clientResponse, [
141
            'Content-Length',
142
            'Content-Type',
143
            'Last-Modified',
144
        ]);
145
146
        return $this->withCorsResponseHeaders($clientResponse, $request);
147
    }
148
149
    /**
150
     * Handle a GET request.
151
     *
152
     * @param ServerRequestInterface $request
153
     * @return ResponseInterface
154
     */
155
    protected function handleGet(ServerRequestInterface $request): ResponseInterface
156
    {
157
        $queryParams = $request->getQueryParams();
158
159
        $url = (string) ($queryParams['url'] ?? '');
160
        if (!Helper::isValidHttpUrl($url)) {
161
            return new JsonResponse(['message' => 'Did not receive a valid URL.'], 400);
162
        }
163
164
        // get and verify the uHash
165
        $uHash = (string) ($queryParams['uHash'] ?? '');
166
        if (!hash_equals(GeneralUtility::hmac($url, 'PageViewProxy'), $uHash)) {
167
            return new JsonResponse(['message' => 'No valid uHash passed!'], 401);
168
        }
169
        try {
170
            $targetResponse = $this->requestFactory->request($url, 'GET', [
171
                'headers' => [
172
                    'User-Agent' => $this->extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
173
                ],
174
175
                // For performance, don't download content up-front. Rather, we'll
176
                // download and upload simultaneously.
177
                // https://docs.guzzlephp.org/en/6.5/request-options.html#stream
178
                'stream' => true,
179
180
                // Don't throw exceptions when a non-success status code is
181
                // received. We handle these manually.
182
                'http_errors' => false,
183
            ]);
184
        } catch (\Exception $e) {
185
            return new JsonResponse(['message' => 'Could not fetch resource of given URL.'], 500);
186
        }
187
188
        $body = new StdOutStream($targetResponse->getBody());
189
190
        $clientResponse = GeneralUtility::makeInstance(Response::class)
191
            ->withStatus($targetResponse->getStatusCode())
192
            ->withBody($body);
193
194
        $clientResponse = $this->copyHeaders($targetResponse, $clientResponse, [
195
            'Content-Length',
196
            'Content-Type',
197
            'Last-Modified',
198
        ]);
199
200
        return $this->withCorsResponseHeaders($clientResponse, $request);
201
    }
202
203
    /**
204
     * The main method of the eID script
205
     *
206
     * @access public
207
     *
208
     * @param ServerRequestInterface $request
209
     * @return ResponseInterface
210
     */
211
    public function main(ServerRequestInterface $request)
212
    {
213
        switch ($request->getMethod()) {
214
            case 'OPTIONS':
215
                return $this->handleOptions($request);
216
217
            case 'GET':
218
                return $this->handleGet($request);
219
220
            case 'HEAD':
221
                return $this->handleHead($request);
222
223
            default:
224
                // 405 Method Not Allowed
225
                return GeneralUtility::makeInstance(Response::class)
226
                    ->withStatus(405);
227
        }
228
    }
229
}
230