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.
Completed
Push — master ( e93629...dcd410 )
by Alexander
35s queued 27s
created

PageViewProxy   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 51
c 0
b 0
f 0
dl 0
loc 155
rs 10
wmc 14

6 Methods

Rating   Name   Duplication   Size   Complexity  
A main() 0 13 3
A withCorsResponseHeaders() 0 10 2
A __construct() 0 4 1
A copyHeaders() 0 16 3
A handleGet() 0 47 4
A handleOptions() 0 6 1
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')
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 a GET request.
118
     *
119
     * @param ServerRequestInterface $request
120
     * @return ResponseInterface
121
     */
122
    protected function handleGet(ServerRequestInterface $request): ResponseInterface
123
    {
124
        $queryParams = $request->getQueryParams();
125
126
        $url = (string) ($queryParams['url'] ?? '');
127
        if (!Helper::isValidHttpUrl($url)) {
128
            return new JsonResponse(['message' => 'Did not receive a valid URL.'], 400);
129
        }
130
131
        // get and verify the uHash
132
        $uHash = (string) ($queryParams['uHash'] ?? '');
133
        if (!hash_equals(GeneralUtility::hmac($url, 'PageViewProxy'), $uHash)) {
134
            return new JsonResponse(['message' => 'No valid uHash passed!'], 401);
135
        }
136
137
        try {
138
            $targetResponse = $this->requestFactory->request($url, 'GET', [
139
                'headers' => [
140
                    'User-Agent' => $this->extConf['useragent'] ?? 'Kitodo.Presentation Proxy',
141
                ],
142
143
                // For performance, don't download content up-front. Rather, we'll
144
                // download and upload simultaneously.
145
                // https://docs.guzzlephp.org/en/6.5/request-options.html#stream
146
                'stream' => true,
147
148
                // Don't throw exceptions when a non-success status code is
149
                // received. We handle these manually.
150
                'http_errors' => false,
151
            ]);
152
        } catch (\Exception $e) {
153
            return new JsonResponse(['message' => 'Could not fetch resource of given URL.'], 500);
154
        }
155
156
        $body = new StdOutStream($targetResponse->getBody());
157
158
        $clientResponse = GeneralUtility::makeInstance(Response::class)
159
            ->withStatus($targetResponse->getStatusCode())
160
            ->withBody($body);
161
162
        $clientResponse = $this->copyHeaders($targetResponse, $clientResponse, [
163
            'Content-Length',
164
            'Content-Type',
165
            'Last-Modified',
166
        ]);
167
168
        return $this->withCorsResponseHeaders($clientResponse, $request);
169
    }
170
171
    /**
172
     * The main method of the eID script
173
     *
174
     * @access public
175
     *
176
     * @param ServerRequestInterface $request
177
     * @return ResponseInterface
178
     */
179
    public function main(ServerRequestInterface $request)
180
    {
181
        switch ($request->getMethod()) {
182
            case 'OPTIONS':
183
                return $this->handleOptions($request);
184
185
            case 'GET':
186
                return $this->handleGet($request);
187
188
            default:
189
                // 405 Method Not Allowed
190
                return GeneralUtility::makeInstance(Response::class)
191
                    ->withStatus(405);
192
        }
193
    }
194
}
195