Passed
Push — rbac ( 82b65f...a43d05 )
by Michael
02:23
created

RequestData::setupForwardedIpData()   B

Complexity

Conditions 10
Paths 26

Size

Total Lines 82
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 37
c 1
b 0
f 1
dl 0
loc 82
rs 7.6666
cc 10
nc 26
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\Fragments;
10
11
use Waca\DataObjects\Request;
12
use Waca\DataObjects\User;
13
use Waca\Exceptions\ApplicationLogicException;
14
use Waca\Helpers\SearchHelpers\RequestSearchHelper;
15
use Waca\Pages\RequestAction\PageBreakReservation;
16
use Waca\PdoDatabase;
17
use Waca\Providers\Interfaces\ILocationProvider;
18
use Waca\Providers\Interfaces\IRDnsProvider;
19
use Waca\Providers\Interfaces\IXffTrustProvider;
20
use Waca\Security\SecurityManager;
21
use Waca\SiteConfiguration;
22
use Waca\WebRequest;
23
24
trait RequestData
25
{
26
    /**
27
     * @var array Array of IP address classed as 'private' by RFC1918.
28
     */
29
    protected static $rfc1918ips = array(
30
        "10.0.0.0"    => "10.255.255.255",
31
        "172.16.0.0"  => "172.31.255.255",
32
        "192.168.0.0" => "192.168.255.255",
33
        "169.254.0.0" => "169.254.255.255",
34
        "127.0.0.0"   => "127.255.255.255",
35
    );
36
37
    /**
38
     * Gets a request object
39
     *
40
     * @param PdoDatabase $database  The database connection
41
     * @param int         $requestId The ID of the request to retrieve
42
     *
43
     * @return Request
44
     * @throws ApplicationLogicException
45
     */
46
    protected function getRequest(PdoDatabase $database, $requestId)
47
    {
48
        if ($requestId === null) {
49
            throw new ApplicationLogicException("No request specified");
50
        }
51
52
        $request = Request::getById($requestId, $database);
53
        if ($request === false || !is_a($request, Request::class)) {
54
            throw new ApplicationLogicException('Could not load the requested request!');
55
        }
56
57
        return $request;
58
    }
59
60
    /**
61
     * Returns a value stating whether the user is allowed to see private data or not
62
     *
63
     * @param Request $request
64
     * @param User    $currentUser
65
     *
66
     * @return bool
67
     * @category Security-Critical
68
     */
69
    protected function isAllowedPrivateData(Request $request, User $currentUser)
70
    {
71
        // Test the main security barrier for private data access using SecurityManager
72
        if ($this->barrierTest('alwaysSeePrivateData', $currentUser, 'RequestData')) {
73
            // Tool admins/check-users can always see private data
74
            return true;
75
        }
76
77
        // reserving user is allowed to see the data
78
        if ($currentUser->getId() === $request->getReserved()
79
            && $request->getReserved() !== null
80
            && $this->barrierTest('seePrivateDataWhenReserved', $currentUser, 'RequestData')
81
        ) {
82
            return true;
83
        }
84
85
        // user has the reveal hash
86
        if (WebRequest::getString('hash') === $request->getRevealHash()
87
            && $this->barrierTest('seePrivateDataWithHash', $currentUser, 'RequestData')
88
        ) {
89
            return true;
90
        }
91
92
        // nope. Not allowed.
93
        return false;
94
    }
95
96
    /**
97
     * Tests the security barrier for a specified action.
98
     *
99
     * Don't use within templates
100
     *
101
     * @param string      $action
102
     *
103
     * @param User        $user
104
     * @param null|string $pageName
105
     *
106
     * @return bool
107
     * @category Security-Critical
108
     */
109
    abstract protected function barrierTest($action, User $user, $pageName = null);
110
111
    /**
112
     * Gets the name of the route that has been passed from the request router.
113
     * @return string
114
     */
115
    abstract protected function getRouteName();
116
117
    /** @return SecurityManager */
118
    abstract protected function getSecurityManager();
119
120
    /**
121
     * Sets the name of the template this page should display.
122
     *
123
     * @param string $name
124
     */
125
    abstract protected function setTemplate($name);
126
127
    /** @return IXffTrustProvider */
128
    abstract protected function getXffTrustProvider();
129
130
    /** @return ILocationProvider */
131
    abstract protected function getLocationProvider();
132
133
    /** @return IRDnsProvider */
134
    abstract protected function getRdnsProvider();
135
136
    /**
137
     * Assigns a Smarty variable
138
     *
139
     * @param  array|string $name  the template variable name(s)
140
     * @param  mixed        $value the value to assign
141
     */
142
    abstract protected function assign($name, $value);
143
144
    /**
145
     * @param int         $requestReservationId
146
     * @param PdoDatabase $database
147
     * @param User        $currentUser
148
     */
149
    protected function setupReservationDetails($requestReservationId, PdoDatabase $database, User $currentUser)
150
    {
151
        $requestIsReserved = $requestReservationId !== null;
152
        $this->assign('requestIsReserved', $requestIsReserved);
153
        $this->assign('requestIsReservedByMe', false);
154
155
        if ($requestIsReserved) {
156
            $this->assign('requestReservedByName', User::getById($requestReservationId, $database)->getUsername());
157
            $this->assign('requestReservedById', $requestReservationId);
158
159
            if ($requestReservationId === $currentUser->getId()) {
160
                $this->assign('requestIsReservedByMe', true);
161
            }
162
        }
163
164
        $this->assign('canBreakReservation', $this->barrierTest('force', $currentUser, PageBreakReservation::class));
165
    }
166
167
    /**
168
     * Adds private request data to Smarty. DO NOT USE WITHOUT FIRST CHECKING THAT THE USER IS AUTHORISED!
169
     *
170
     * @param Request           $request
171
     * @param User              $currentUser
172
     * @param SiteConfiguration $configuration
173
     *
174
     * @param PdoDatabase       $database
175
     */
176
    protected function setupPrivateData(
177
        $request,
178
        User $currentUser,
179
        SiteConfiguration $configuration,
180
        PdoDatabase $database
181
    ) {
182
        $xffProvider = $this->getXffTrustProvider();
183
184
        $relatedEmailRequests = RequestSearchHelper::get($database)
185
            ->byEmailAddress($request->getEmail())
186
            ->withConfirmedEmail()
187
            ->excludingPurgedData($configuration)
188
            ->excludingRequest($request->getId())
189
            ->fetch();
190
191
        $this->assign('requestEmail', $request->getEmail());
192
        $emailDomain = explode("@", $request->getEmail())[1];
193
        $this->assign("emailurl", $emailDomain);
194
        $this->assign('requestRelatedEmailRequestsCount', count($relatedEmailRequests));
195
        $this->assign('requestRelatedEmailRequests', $relatedEmailRequests);
196
197
        $trustedIp = $xffProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
198
        $this->assign('requestTrustedIp', $trustedIp);
199
        $this->assign('requestRealIp', $request->getIp());
200
        $this->assign('requestForwardedIp', $request->getForwardedIp());
201
202
        $trustedIpLocation = $this->getLocationProvider()->getIpLocation($trustedIp);
203
        $this->assign('requestTrustedIpLocation', $trustedIpLocation);
204
205
        $this->assign('requestHasForwardedIp', $request->getForwardedIp() !== null);
206
207
        $relatedIpRequests = RequestSearchHelper::get($database)
208
            ->byIp($trustedIp)
209
            ->withConfirmedEmail()
210
            ->excludingPurgedData($configuration)
211
            ->excludingRequest($request->getId())
212
            ->fetch();
213
214
        $this->assign('requestRelatedIpRequestsCount', count($relatedIpRequests));
215
        $this->assign('requestRelatedIpRequests', $relatedIpRequests);
216
217
        $this->assign('showRevealLink', false);
218
        if ($request->getReserved() === $currentUser->getId() ||
219
            $this->barrierTest('alwaysSeeHash', $currentUser, 'RequestData')
220
        ) {
221
            $this->assign('showRevealLink', true);
222
            $this->assign('revealHash', $request->getRevealHash());
223
        }
224
225
        $this->setupForwardedIpData($request);
226
    }
227
228
    /**
229
     * Adds checkuser request data to Smarty. DO NOT USE WITHOUT FIRST CHECKING THAT THE USER IS AUTHORISED!
230
     *
231
     * @param Request $request
232
     */
233
    protected function setupCheckUserData(Request $request)
234
    {
235
        $this->assign('requestUserAgent', $request->getUserAgent());
236
    }
237
238
    /**
239
     * Sets up the basic data for this request, and adds it to Smarty
240
     *
241
     * @param Request           $request
242
     * @param SiteConfiguration $config
243
     */
244
    protected function setupBasicData(Request $request, SiteConfiguration $config)
245
    {
246
        $this->assign('requestId', $request->getId());
247
        $this->assign('updateVersion', $request->getUpdateVersion());
248
        $this->assign('requestName', $request->getName());
249
        $this->assign('requestDate', $request->getDate());
250
        $this->assign('requestStatus', $request->getStatus());
251
252
        $this->assign('requestIsClosed', !array_key_exists($request->getStatus(), $config->getRequestStates()));
253
    }
254
255
    /**
256
     * Sets up the forwarded IP data for this request and adds it to Smarty
257
     *
258
     * @param Request $request
259
     */
260
    protected function setupForwardedIpData(Request $request)
261
    {
262
        if ($request->getForwardedIp() !== null) {
263
            $requestProxyData = array(); // Initialize array to store data to be output in Smarty template.
264
            $proxyIndex = 0;
265
266
            // Assuming [client] <=> [proxy1] <=> [proxy2] <=> [proxy3] <=> [us], we will see an XFF header of [client],
267
            // [proxy1], [proxy2], and our actual IP will be [proxy3]
268
            $proxies = explode(",", $request->getForwardedIp());
269
            $proxies[] = $request->getIp();
270
271
            // Origin is the supposed "client" IP.
272
            $origin = $proxies[0];
273
            $this->assign("forwardedOrigin", $origin);
274
275
            // We step through the servers in reverse order, from closest to furthest
276
            $proxies = array_reverse($proxies);
277
278
            // By default, we have trust, because the first in the chain is now REMOTE_ADDR, which is hardest to spoof.
279
            $trust = true;
280
281
            /**
282
             * @var int    $index     The zero-based index of the proxy.
283
             * @var string $proxyData The proxy IP address (although possibly not!)
284
             */
285
            foreach ($proxies as $index => $proxyData) {
286
                $proxyAddress = trim($proxyData);
287
                $requestProxyData[$proxyIndex]['ip'] = $proxyAddress;
288
289
                // get data on this IP.
290
                $thisProxyIsTrusted = $this->getXffTrustProvider()->isTrusted($proxyAddress);
291
292
                $proxyIsInPrivateRange = $this->getXffTrustProvider()
293
                    ->ipInRange(self::$rfc1918ips, $proxyAddress);
294
295
                if (!$proxyIsInPrivateRange) {
296
                    $proxyReverseDns = $this->getRdnsProvider()->getReverseDNS($proxyAddress);
297
                    $proxyLocation = $this->getLocationProvider()->getIpLocation($proxyAddress);
298
                }
299
                else {
300
                    // this is going to fail, so why bother trying?
301
                    $proxyReverseDns = false;
302
                    $proxyLocation = false;
303
                }
304
305
                // current trust chain status BEFORE this link
306
                $preLinkTrust = $trust;
307
308
                // is *this* link trusted? Note, this will be true even if there is an untrusted link before this!
309
                $requestProxyData[$proxyIndex]['trustedlink'] = $thisProxyIsTrusted;
310
311
                // set the trust status of the chain to this point
312
                $trust = $trust & $thisProxyIsTrusted;
313
314
                // If this is the origin address, and the chain was trusted before this point, then we can trust
315
                // the origin.
316
                if ($preLinkTrust && $proxyAddress == $origin) {
317
                    // if this is the origin, then we are at the last point in the chain.
318
                    // @todo: this is probably the cause of some bugs when an IP appears twice - we're missing a check
319
                    // to see if this is *really* the last in the chain, rather than just the same IP as it.
320
                    $trust = true;
321
                }
322
323
                $requestProxyData[$proxyIndex]['trust'] = $trust;
324
325
                $requestProxyData[$proxyIndex]['rdnsfailed'] = $proxyReverseDns === false;
326
                $requestProxyData[$proxyIndex]['rdns'] = $proxyReverseDns;
327
                $requestProxyData[$proxyIndex]['routable'] = !$proxyIsInPrivateRange;
328
329
                $requestProxyData[$proxyIndex]['location'] = $proxyLocation;
330
331
                if ($proxyReverseDns === $proxyAddress && $proxyIsInPrivateRange === false) {
332
                    $requestProxyData[$proxyIndex]['rdns'] = null;
333
                }
334
335
                $showLinks = (!$trust || $proxyAddress == $origin) && !$proxyIsInPrivateRange;
336
                $requestProxyData[$proxyIndex]['showlinks'] = $showLinks;
337
338
                $proxyIndex++;
339
            }
340
341
            $this->assign("requestProxyData", $requestProxyData);
342
        }
343
    }
344
}
345