Test Setup Failed
Push — master ( 1f25c9...2084e1 )
by Simon
03:27
created

BanHelper::canUnban()   D

Complexity

Conditions 13
Paths 326

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 29
ccs 0
cts 10
cp 0
rs 4.2083
cc 13
nc 326
nop 1
crap 182

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