Failed Conditions
Pull Request — bugsquish (#573)
by Simon
03:02 queued 49s
created

PageBan::handlePostMethodForSetBan()   C

Complexity

Conditions 15
Paths 145

Size

Total Lines 80
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
eloc 48
dl 0
loc 80
ccs 0
cts 59
cp 0
rs 5.5416
c 0
b 0
f 0
cc 15
nc 145
nop 0
crap 240

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\Pages;
10
11
use Exception;
12
use SmartyException;
13
use Waca\DataObjects\Ban;
14
use Waca\DataObjects\Request;
15
use Waca\DataObjects\User;
16
use Waca\Exceptions\AccessDeniedException;
17
use Waca\Exceptions\ApplicationLogicException;
18
use Waca\Helpers\BanHelper;
19
use Waca\Helpers\Logger;
20
use Waca\Helpers\SearchHelpers\UserSearchHelper;
21
use Waca\SessionAlert;
22
use Waca\Tasks\InternalPageBase;
23
use Waca\WebRequest;
24
25
class PageBan extends InternalPageBase
26
{
27
    /**
28
     * Main function for this page, when no specific actions are called.
29
     */
30
    protected function main()
31
    {
32
        $this->assignCSRFToken();
33
        $this->setHtmlTitle('Bans');
34
35
        $bans = Ban::getActiveBans($this->getDatabase());
36
37
        $this->setupBanList($bans);
38
        $this->setTemplate('bans/main.tpl');
39
    }
40
41
    protected function show()
42
    {
43
        $this->assignCSRFToken();
44
        $this->setHtmlTitle('Bans');
45
46
        $rawIdList = WebRequest::getString('id');
47
        if ($rawIdList === null) {
48
            $this->redirect('bans');
49
50
            return;
51
        }
52
53
        $idList = explode(',',$rawIdList);
54
55
        $bans = Ban::getByIdList($idList, $this->getDatabase());
56
57
        $this->setupBanList($bans);
58
        $this->setTemplate('bans/showbans.tpl');
59
    }
60
61
    /**
62
     * Entry point for the ban set action
63
     * @throws SmartyException
64
     * @throws Exception
65
     */
66
    protected function set()
67
    {
68
        $this->setHtmlTitle('Bans');
69
70
        // dual-mode action
71
        if (WebRequest::wasPosted()) {
72
            try {
73
                $this->handlePostMethodForSetBan();
74
            }
75
            catch (ApplicationLogicException $ex) {
76
                SessionAlert::error($ex->getMessage());
77
                $this->redirect("bans", "set");
78
            }
79
        }
80
        else {
81
            $this->handleGetMethodForSetBan();
82
        }
83
    }
84
85
    /**
86
     * Entry point for the ban remove action
87
     *
88
     * @throws AccessDeniedException
89
     * @throws ApplicationLogicException
90
     * @throws SmartyException
91
     */
92
    protected function remove()
93
    {
94
        $this->setHtmlTitle('Bans');
95
96
        $ban = $this->getBanForUnban();
97
98
        $banHelper = new BanHelper($this->getDatabase(), $this->getXffTrustProvider(), $this->getSecurityManager());
99
        if (!$banHelper->canUnban($ban)) {
100
            // triggered when a user tries to unban a ban they can't see the entirety of.
101
            // there's no UI way to get to this, so a raw exception is fine.
102
            throw new AccessDeniedException($this->getSecurityManager());
103
        }
104
105
        // dual mode
106
        if (WebRequest::wasPosted()) {
107
            $this->validateCSRFToken();
108
            $unbanReason = WebRequest::postString('unbanreason');
109
110
            if ($unbanReason === null || trim($unbanReason) === "") {
111
                SessionAlert::error('No unban reason specified');
112
                $this->redirect("bans", "remove", array('id' => $ban->getId()));
113
            }
114
115
            // set optimistic locking from delete form page load
116
            $updateVersion = WebRequest::postInt('updateversion');
117
            $ban->setUpdateVersion($updateVersion);
118
119
            $database = $this->getDatabase();
120
            $ban->setActive(false);
121
            $ban->save();
122
123
            Logger::unbanned($database, $ban, $unbanReason);
124
125
            SessionAlert::quick('Disabled ban.');
126
            $this->getNotificationHelper()->unbanned($ban, $unbanReason);
127
128
            $this->redirect('bans');
129
        }
130
        else {
131
            $this->assignCSRFToken();
132
            $this->assign('ban', $ban);
133
            $this->setTemplate('bans/unban.tpl');
134
        }
135
    }
136
137
    /**
138
     * @throws ApplicationLogicException
139
     */
140
    private function getBanDuration()
141
    {
142
        $duration = WebRequest::postString('duration');
143
        if ($duration === "other") {
144
            $duration = strtotime(WebRequest::postString('otherduration'));
145
146
            if (!$duration) {
147
                throw new ApplicationLogicException('Invalid ban time');
148
            }
149
            elseif (time() > $duration) {
150
                throw new ApplicationLogicException('Ban time has already expired!');
151
            }
152
153
            return $duration;
154
        }
155
        elseif ($duration === "-1") {
156
            return null;
157
        }
158
        else {
159
            $duration = WebRequest::postInt('duration') + time();
160
161
            return $duration;
162
        }
163
    }
164
165
    /**
166
     * Handles the POST method on the set action
167
     *
168
     * @throws ApplicationLogicException
169
     * @throws Exception
170
     */
171
    private function handlePostMethodForSetBan()
172
    {
173
        $this->validateCSRFToken();
174
        $database = $this->getDatabase();
175
        $user = User::getCurrent($database);
176
177
        // Checks whether there is a reason entered for ban.
178
        $reason = WebRequest::postString('banreason');
179
        if ($reason === null || trim($reason) === "") {
180
            throw new ApplicationLogicException('You must specify a ban reason');
181
        }
182
183
        // ban targets
184
        $targetName = WebRequest::postString('banName');
185
        $targetIp = WebRequest::postString('banIP');
186
        $targetEmail = WebRequest::postString('banEmail');
187
        $targetUseragent = WebRequest::postString('banUseragent');
188
        $targetMask = null;
189
190
        // check the user is allowed to use provided targets
191
        if(!$this->barrierTest('name', $user, 'BanType')) {
192
            $targetName = null;
193
        }
194
        if(!$this->barrierTest('ip', $user, 'BanType')) {
195
            $targetIp = null;
196
        }
197
        if(!$this->barrierTest('email', $user, 'BanType')) {
198
            $targetEmail = null;
199
        }
200
        if(!$this->barrierTest('useragent', $user, 'BanType')) {
201
            $targetUseragent = null;
202
        }
203
204
        // Checks whether there is a target entered to ban.
205
        if ($targetName === null && $targetIp === null && $targetEmail === null && $targetUseragent === null) {
206
            throw new ApplicationLogicException('You must specify a target to be banned');
207
        }
208
209
        // Validate ban duration
210
        $duration = $this->getBanDuration();
211
212
        // handle CIDR ranges
213
        if ($targetIp !== null) {
214
            if (strpos($targetIp, '/') !== false) {
215
                $ipParts = explode('/', $targetIp, 2);
216
                $targetIp = $ipParts[0];
217
                $targetMask = $ipParts[1];
218
            } else {
219
                $targetMask = filter_var($targetIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? 128 : 32;
220
            }
221
222
            $this->validateIpBan($targetIp, $targetMask);
223
        }
224
225
        $banHelper = new BanHelper($this->getDatabase(), $this->getXffTrustProvider(), $this->getSecurityManager());
226
        if (count($banHelper->getBansByTarget($targetName, $targetEmail, $targetIp, $targetMask, $targetUseragent)) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $targetMask can also be of type string; however, parameter $mask of Waca\Helpers\BanHelper::getBansByTarget() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

226
        if (count($banHelper->getBansByTarget($targetName, $targetEmail, $targetIp, /** @scrutinizer ignore-type */ $targetMask, $targetUseragent)) > 0) {
Loading history...
227
            throw new ApplicationLogicException('This target is already banned!');
228
        }
229
230
        $ban = new Ban();
231
        $ban->setDatabase($database);
232
        $ban->setActive(true);
233
234
        $ban->setName($targetName);
235
        $ban->setIp($targetIp, $targetMask);
0 ignored issues
show
Bug introduced by
It seems like $targetMask can also be of type string; however, parameter $mask of Waca\DataObjects\Ban::setIp() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

235
        $ban->setIp($targetIp, /** @scrutinizer ignore-type */ $targetMask);
Loading history...
236
        $ban->setEmail($targetEmail);
237
        $ban->setUseragent($targetUseragent);
238
239
        $ban->setUser($user->getId());
240
        $ban->setReason($reason);
241
        $ban->setDuration($duration);
242
243
        $ban->save();
244
245
        Logger::banned($database, $ban, $reason);
246
247
        $this->getNotificationHelper()->banned($ban);
248
        SessionAlert::quick('Ban has been set.');
249
250
        $this->redirect('bans');
251
    }
252
253
    /**
254
     * Handles the GET method on the set action
255
     * @throws Exception
256
     */
257
    protected function handleGetMethodForSetBan()
258
    {
259
        $this->setTemplate('bans/banform.tpl');
260
        $this->assignCSRFToken();
261
262
        $database = $this->getDatabase();
263
264
        $user = User::getCurrent($database);
265
        $this->setupSecurity($user);
266
267
        $banType = WebRequest::getString('type');
268
        $banRequest = WebRequest::getInt('request');
269
270
        // if the parameters are null, skip loading a request.
271
        if ($banType === null || $banRequest === null || $banRequest === 0) {
272
            return;
273
        }
274
275
        // Attempt to resolve the correct target
276
        /** @var Request $request */
277
        $request = Request::getById($banRequest, $database);
278
        if ($request === false) {
0 ignored issues
show
introduced by
The condition $request === false is always false.
Loading history...
279
            $this->assign('bantarget', '');
280
281
            return;
282
        }
283
284
        switch ($banType) {
285
            case 'EMail':
286
                if ($this->barrierTest('email', $user, 'BanType')) {
287
                    $this->assign('banEmail', $request->getEmail());
288
                }
289
                break;
290
            case 'IP':
291
                if ($this->barrierTest('ip', $user, 'BanType')) {
292
                    $this->assign('banIP', $this->getXffTrustProvider()
293
                        ->getTrustedClientIp($request->getIp(), $request->getForwardedIp()));
294
                }
295
                break;
296
            case 'Name':
297
                if ($this->barrierTest('username', $user, 'BanType')) {
298
                    $this->assign('banName', $request->getEmail());
299
                }
300
                break;
301
            case 'UA':
302
                if ($this->barrierTest('useragent', $user, 'BanType')) {
303
                    $this->assign('banUseragent', $request->getEmail());
304
                }
305
                break;
306
        }
307
    }
308
309
    /**
310
     * @return Ban
311
     * @throws ApplicationLogicException
312
     */
313
    private function getBanForUnban()
314
    {
315
        $banId = WebRequest::getInt('id');
316
        if ($banId === null || $banId === 0) {
317
            throw new ApplicationLogicException("The ban ID appears to be missing. This is probably a bug.");
318
        }
319
320
        $database = $this->getDatabase();
321
        $this->setupSecurity(User::getCurrent($database));
322
        $ban = Ban::getActiveId($banId, $database);
323
324
        if ($ban === false) {
0 ignored issues
show
introduced by
The condition $ban === false is always false.
Loading history...
325
            throw new ApplicationLogicException("The specified ban is not currently active, or doesn't exist.");
326
        }
327
328
        return $ban;
329
    }
330
331
    /**
332
     * @param $user
333
     */
334
    protected function setupSecurity($user): void
335
    {
336
        $this->assign('canSeeIpBan', $this->barrierTest('ip', $user, 'BanType'));
337
        $this->assign('canSeeNameBan', $this->barrierTest('name', $user, 'BanType'));
338
        $this->assign('canSeeEmailBan', $this->barrierTest('email', $user, 'BanType'));
339
        $this->assign('canSeeUseragentBan', $this->barrierTest('useragent', $user, 'BanType'));
340
    }
341
342
    /**
343
     * @param string $targetIp
344
     * @param        $targetMask
345
     *
346
     * @throws ApplicationLogicException
347
     */
348
    private function validateIpBan(string $targetIp, $targetMask): void
349
    {
350
        // validate this is an IP
351
        if (!filter_var($targetIp, FILTER_VALIDATE_IP)) {
352
            throw new ApplicationLogicException("Not a valid IP address");
353
        }
354
355
        // validate CIDR ranges
356
        if (filter_var($targetIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && ($targetMask < 0 || $targetMask > 128)) {
357
            throw new ApplicationLogicException("CIDR mask out of range for IPv6");
358
        }
359
360
        if (filter_var($targetIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && ($targetMask < 0 || $targetMask > 32)) {
361
            throw new ApplicationLogicException("CIDR mask out of range for IPv4");
362
        }
363
364
        $squidIpList = $this->getSiteConfiguration()->getSquidList();
365
        if (in_array($targetIp, $squidIpList)) {
366
            throw new ApplicationLogicException("This IP address is on the protected list of proxies, and cannot be banned.");
367
        }
368
    }
369
370
    /**
371
     * @param array $bans
372
     */
373
    protected function setupBanList(array $bans): void
374
    {
375
        $userIds = array_map(
376
            function(Ban $entry) {
377
                return $entry->getUser();
378
            },
379
            $bans);
380
        $userList = UserSearchHelper::get($this->getDatabase())->inIds($userIds)->fetchMap('username');
381
382
        $user = User::getCurrent($this->getDatabase());
383
        $this->assign('canSet', $this->barrierTest('set', $user));
384
        $this->assign('canRemove', $this->barrierTest('remove', $user));
385
386
        $this->setupSecurity($user);
387
388
        $this->assign('usernames', $userList);
389
        $this->assign('activebans', $bans);
390
391
        $banHelper = new BanHelper($this->getDatabase(), $this->getXffTrustProvider(), $this->getSecurityManager());
392
        $this->assign('banHelper', $banHelper);
393
    }
394
}
395