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 (#758)
by
unknown
03:27
created

PageViewProxy::handleProxyRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 13
c 3
b 0
f 0
dl 0
loc 30
rs 9.8333
cc 1
nc 1
nop 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\Plugin\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
use TYPO3\CMS\Core\Utility\MathUtility;
25
26
/**
27
 * eID image proxy for plugin 'Page View' of the 'dlf' extension
28
 *
29
 * Supported query parameters:
30
 * - `url` (mandatory): The URL to be proxied
31
 * - `header` (optional)
32
 *   - `1`: Request headers and body (via GET)
33
 *   - `2`: Request only headers (via HEAD) and dump them to response body
34
 *
35
 * @author Alexander Bigga <[email protected]>
36
 * @package TYPO3
37
 * @subpackage dlf
38
 * @access public
39
 */
40
class PageViewProxy
41
{
42
    /**
43
     * @var RequestFactory
44
     */
45
    protected $requestFactory;
46
47
    /**
48
     * @var mixed
49
     */
50
    protected $extConf;
51
52
    public function __construct()
53
    {
54
        $this->requestFactory = GeneralUtility::makeInstance(RequestFactory::class);
55
        $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('dlf');
56
    }
57
58
    /**
59
     * Get headers to be sent to the proxied target.
60
     *
61
     * @return array
62
     */
63
    protected function getRequestHeaders(): array
64
    {
65
        return [
66
            'User-Agent' => $this->extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
67
        ];
68
    }
69
70
    /**
71
     * Return a response that is derived from $response and contains CORS
72
     * headers to be sent to the client.
73
     *
74
     * @return ResponseInterface $response
75
     * @return ServerRequestInterface $request The incoming request.
76
     * @return ResponseInterface
77
     */
78
    protected function withCorsResponseHeaders(
79
        ResponseInterface $response,
80
        ServerRequestInterface $request
81
    ): ResponseInterface {
82
        $origin = (string) ($request->getHeaderLine('Origin') ?: '*');
83
84
        return $response
85
            ->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS')
86
            ->withHeader('Access-Control-Allow-Origin', $origin)
87
            ->withHeader('Access-Control-Max-Age', '86400');
88
    }
89
90
    /**
91
     * Takes headers listed in $headerNames from $fromResponse, adds them to
92
     * $toResponse and returns the result.
93
     *
94
     * @param ResponseInterface $fromResponse
95
     * @param ResponseInterface $toResponse
96
     * @param array $headerNames
97
     * @return ResponseInterface
98
     */
99
    protected function copyHeaders(
100
        ResponseInterface $fromResponse,
101
        ResponseInterface $toResponse,
102
        array $headerNames
103
    ) {
104
        $result = $toResponse;
105
106
        foreach ($headerNames as $headerName) {
107
            $headerValues = $fromResponse->getHeader($headerName);
108
            $result = $result->withAddedHeader($headerName, $headerValues);
109
        }
110
111
        return $result;
112
    }
113
114
    /**
115
     * Handle an OPTIONS request.
116
     *
117
     * @param ServerRequestInterface $request
118
     * @return ResponseInterface
119
     */
120
    protected function handleOptions(ServerRequestInterface $request): ResponseInterface
121
    {
122
        // 204 No Content
123
        $response = GeneralUtility::makeInstance(Response::class)
124
            ->withStatus(204);
125
        return $this->withCorsResponseHeaders($response, $request);
126
    }
127
128
    /**
129
     * Handle a GET request.
130
     *
131
     * @param ServerRequestInterface $request
132
     * @return ResponseInterface
133
     */
134
    protected function handleGet(ServerRequestInterface $request): ResponseInterface
135
    {
136
        $queryParams = $request->getQueryParams();
137
        $url = (string) ($queryParams['url'] ?? '');
138
139
        if (!Helper::isValidHttpUrl($url)) {
140
            return new JsonResponse(['message' => 'Did not receive a valid URL.'], 400);
141
        }
142
143
        // This has previously been used as parameter for `GeneralUtility::getUrl()`
144
        // A value of 0 would indicate not to fetch headers, which we'll ignore.
145
        $header = (int) ($queryParams['header'] ?? 1);
146
        $headerOnly = MathUtility::forceIntegerInRange($header, 0, 2, 0) === 2;
147
148
        try {
149
            return $headerOnly
150
                ? $this->handleHeaderRequest($url, $request)
151
                : $this->handleProxyRequest($url, $request);
152
        } catch (\Exception $e) {
153
            return new JsonResponse(['message' => 'Could not fetch resource of given URL.'], 500);
154
        }
155
    }
156
157
    /**
158
     * Handle a request to proxy the target resource.
159
     *
160
     * @param string $url
161
     * @param ServerRequestInterface $request
162
     * @return ResponseInterface
163
     */
164
    protected function handleProxyRequest(
165
        string $url,
166
        ServerRequestInterface $request
167
    ): ResponseInterface {
168
        $targetResponse = $this->requestFactory->request($url, 'GET', [
169
            'headers' => $this->getRequestHeaders(),
170
171
            // For performance, don't download content up-front. Rather, we'll
172
            // download and upload simultaneously.
173
            // https://docs.guzzlephp.org/en/6.5/request-options.html#stream
174
            'stream' => true,
175
176
            // Don't throw exceptions when a non-success status code is
177
            // received. We handle these manually.
178
            'http_errors' => false,
179
        ]);
180
181
        $body = new StdOutStream($targetResponse->getBody());
182
183
        $clientResponse = GeneralUtility::makeInstance(Response::class)
184
            ->withStatus($targetResponse->getStatusCode())
185
            ->withBody($body);
186
187
        $clientResponse = $this->copyHeaders($targetResponse, $clientResponse, [
188
            'Content-Length',
189
            'Content-Type',
190
            'Last-Modified',
191
        ]);
192
193
        return $this->withCorsResponseHeaders($clientResponse, $request);
194
    }
195
196
    /**
197
     * Handle a request to forward headers from target.
198
     *
199
     * @param string $url
200
     * @param ServerRequestInterface $request
201
     * @return ResponseInterface
202
     */
203
    protected function handleHeaderRequest(
204
        string $url,
205
        ServerRequestInterface $request
206
    ): ResponseInterface {
207
        $targetResponse = $this->requestFactory->request($url, 'HEAD', [
208
            'headers' => $this->getRequestHeaders(),
209
        ]);
210
211
        $clientResponse = GeneralUtility::makeInstance(Response::class)
212
            ->withStatus(200);
213
214
        $body = $clientResponse->getBody();
215
        foreach ($targetResponse->getHeaders() as $name => $values) {
216
            $body->write($name . ': ' . implode(', ', $values) . "\n");
217
        }
218
219
        return $this->withCorsResponseHeaders($clientResponse, $request);
220
    }
221
222
    /**
223
     * The main method of the eID script
224
     *
225
     * @access public
226
     *
227
     * @param ServerRequestInterface $request
228
     * @return ResponseInterface
229
     */
230
    public function main(ServerRequestInterface $request)
231
    {
232
        switch ($request->getMethod()) {
233
            case 'OPTIONS':
234
                return $this->handleOptions($request);
235
236
            case 'GET':
237
                return $this->handleGet($request);
238
239
            default:
240
                // 405 Method Not Allowed
241
                return GeneralUtility::makeInstance(Response::class)
242
                    ->withStatus(405);
243
        }
244
    }
245
}
246