Failed Conditions
Pull Request — bugsquish (#574)
by Simon
05:54 queued 03:23
created

BanHelper::canUnban()   C

Complexity

Conditions 12
Paths 164

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 12
dl 0
loc 21
ccs 0
cts 16
cp 0
rs 6.4333
c 1
b 0
f 0
cc 12
nc 164
nop 1
crap 156

How to fix   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\Helpers;
10
11
use PDO;
12
use Waca\DataObjects\Ban;
13
use Waca\DataObjects\Request;
14
use Waca\DataObjects\User;
15
use Waca\Helpers\Interfaces\IBanHelper;
16
use Waca\PdoDatabase;
17
use Waca\Providers\Interfaces\IXffTrustProvider;
18
use Waca\Security\SecurityManager;
19
20
class BanHelper implements IBanHelper
21
{
22
    /** @var PdoDatabase */
23
    private $database;
24
    /** @var IXffTrustProvider */
25
    private $xffTrustProvider;
26
    /** @var Ban[][] */
27
    private $banCache = [];
28
    /**
29
     * @var null|SecurityManager
30
     */
31
    private $securityManager;
32
33
    public function __construct(
34
        PdoDatabase $database,
35
        IXffTrustProvider $xffTrustProvider,
36
        ?SecurityManager $securityManager
37
    ) {
38
        $this->database = $database;
39
        $this->xffTrustProvider = $xffTrustProvider;
40
        $this->securityManager = $securityManager;
41
    }
42
43
    public function isBlockBanned(Request $request): bool
44
    {
45
        if (!isset($this->banCache[$request->getId()])) {
46
            $this->banCache[$request->getId()] = $this->getBansForRequestFromDatabase($request);
47
        }
48
49
        foreach ($this->banCache[$request->getId()] as $ban) {
50
            if ($ban->getAction() === Ban::ACTION_BLOCK) {
51
                return true;
52
            }
53
        }
54
55
        return false;
56
    }
57
58
    /**
59
     * @param Request $request
60
     *
61
     * @return Ban[]
62
     */
63
    public function getBans(Request $request): array
64
    {
65
        if (!isset($this->banCache[$request->getId()])) {
66
            $this->banCache[$request->getId()] = $this->getBansForRequestFromDatabase($request);
67
        }
68
69
        return $this->banCache[$request->getId()];
70
    }
71
72
    public function getBansByTarget(?string $name, ?string $email, ?string $ip, ?int $mask, ?string $useragent)
73
    {
74
        /** @noinspection SqlConstantCondition */
75
        $query = <<<SQL
76
SELECT * FROM ban 
77
WHERE 1 = 1
78
  AND ((name is null and :nname is null) OR name = :name)
79
  AND ((email is null and :nemail is null) OR email = :email)
80
  AND ((useragent is null and :nuseragent is null) OR useragent = :useragent)
81
  AND ((ip is null and :nip is null) OR ip = inet6_aton(:ip))
82
  AND ((ipmask is null and :nipmask is null) OR ipmask = :ipmask)
83
  AND (duration > UNIX_TIMESTAMP() OR duration is null) 
84
  AND active = 1;
85
SQL;
86
87
        $statement = $this->database->prepare($query);
88
        $statement->execute([
89
            ':name'       => $name,
90
            ':nname'      => $name,
91
            ':email'      => $email,
92
            ':nemail'     => $email,
93
            ':ip'         => $ip,
94
            ':nip'        => $ip,
95
            ':ipmask'     => $mask,
96
            ':nipmask'    => $mask,
97
            ':useragent'  => $useragent,
98
            ':nuseragent' => $useragent,
99
        ]);
100
101
        $result = array();
102
103
        /** @var Ban $v */
104
        foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
105
            $v->setDatabase($this->database);
106
            $result[] = $v;
107
        }
108
109
        return $result;
110
    }
111
112
    public function isActive(Ban $ban): bool
113
    {
114
        if (!$ban->isActive()) {
115
            return false;
116
        }
117
118
        if ($ban->getDuration() !== null && $ban->getDuration() < time()) {
119
            return false;
120
        }
121
122
        return true;
123
    }
124
125
    public function canUnban(Ban $ban): bool
126
    {
127
        if ($this->securityManager === null) {
128
            return false;
129
        }
130
131
        if (!$this->isActive($ban)) {
132
            return false;
133
        }
134
135
        $user = User::getCurrent($this->database);
136
137
        $allowed = true;
138
        $allowed = $allowed && ($ban->getName() === null || $this->securityManager->allows('BanType', 'name', $user) === SecurityManager::ALLOWED);
139
        $allowed = $allowed && ($ban->getEmail() === null || $this->securityManager->allows('BanType', 'email', $user) === SecurityManager::ALLOWED);
140
        $allowed = $allowed && ($ban->getIp() === null || $this->securityManager->allows('BanType', 'ip', $user) === SecurityManager::ALLOWED);
141
        $allowed = $allowed && ($ban->getUseragent() === null || $this->securityManager->allows('BanType', 'useragent', $user) === SecurityManager::ALLOWED);
142
143
        $allowed = $allowed && $this->securityManager->allows('BanVisibility', $ban->getVisibility(), $user) === SecurityManager::ALLOWED;
144
145
        return $allowed;
146
    }
147
148
    /**
149
     * @param Request $request
150
     *
151
     * @return Ban[]
152
     */
153
    private function getBansForRequestFromDatabase(Request $request): array
154
    {
155
        /** @noinspection SqlConstantCondition - included for clarity of code */
156
        $query = <<<SQL
157
select b.* from ban b
158
left join netmask n on 1 = 1
159
    and n.cidr = b.ipmask
160
    and n.protocol = case length(b.ip) when 4 then 4 when 16 then 6 end
161
where 1 = 1
162
    and coalesce(:name rlike name, true)
163
    and coalesce(:email rlike email, true)
164
    and coalesce(:useragent rlike useragent, true)
165
    and case
166
        when length(b.ip) = 4 then
167
          (conv(hex(b.ip), 16, 10) & n.maskl) = (conv(hex(inet6_aton(:ip4)), 16, 10) & n.maskl)
168
        when length(b.ip) = 16 then
169
            (conv(left(hex(b.ip), 16), 16, 10) & n.maskh) = (conv(left(hex(inet6_aton(:ip6h)), 16), 16, 10) & n.maskh)
170
            and (conv(right(hex(b.ip), 16), 16, 10) & n.maskl) = (conv(right(hex(inet6_aton(:ip6l)), 16), 16, 10) & n.maskl)
171
        when length(b.ip) is null then true
172
    end
173
    and active = 1
174
    and (duration > UNIX_TIMESTAMP() or duration is null)
175
SQL;
176
177
        $statement = $this->database->prepare($query);
178
        $trustedIp = $this->xffTrustProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
179
180
        $statement->execute([
181
            ':name'      => $request->getName(),
182
            ':email'     => $request->getEmail(),
183
            ':useragent' => $request->getUserAgent(),
184
            ':ip4'       => $trustedIp,
185
            ':ip6h'      => $trustedIp,
186
            ':ip6l'      => $trustedIp,
187
        ]);
188
189
        /** @var Ban[] $result */
190
        $result = [];
191
192
        /** @var Ban $v */
193
        foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
194
            $v->setDatabase($this->database);
195
            $result[] = $v;
196
        }
197
198
        return $result;
199
    }
200
}
201