Failed Conditions
Push — ipv6 ( 317732...53c11b )
by Richard
35:09 queued 26:44
created

BanHelper::getBans()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 7
rs 10
cc 2
nc 2
nop 1
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 * ACC Development Team. Please see team.json for a list of contributors.     *
5
 *                                                                            *
6
 * This is free and unencumbered software released into the public domain.    *
7
 * Please see LICENSE.md for the full licencing statement.                    *
8
 ******************************************************************************/
9
10
namespace Waca\Helpers;
11
12
use PDO;
13
use Waca\DataObjects\Ban;
14
use Waca\DataObjects\Domain;
15
use Waca\DataObjects\Request;
16
use Waca\DataObjects\User;
17
use Waca\Helpers\Interfaces\IBanHelper;
18
use Waca\PdoDatabase;
19
use Waca\Providers\Interfaces\IXffTrustProvider;
20
use Waca\Security\ISecurityManager;
21
22
class BanHelper implements IBanHelper
23
{
24
    /** @var PdoDatabase */
25
    private $database;
26
    /** @var IXffTrustProvider */
27
    private $xffTrustProvider;
28
    /** @var Ban[][] */
29
    private $banCache = [];
30
31
    private ?ISecurityManager $securityManager;
32
33
    public function __construct(
34
        PdoDatabase $database,
35
        IXffTrustProvider $xffTrustProvider,
36
        ?ISecurityManager $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(
73
        ?string $name,
74
        ?string $email,
75
        ?string $ip,
76
        ?int $mask,
77
        ?string $useragent,
78
        int $domain
79
    ) {
80
        /** @noinspection SqlConstantCondition */
81
        $query = <<<SQL
82
SELECT * FROM ban 
83
WHERE 1 = 1
84
  AND ((name IS NULL AND :nname IS NULL) OR name = :name)
85
  AND ((email IS NULL AND :nemail IS NULL) OR email = :email)
86
  AND ((useragent IS NULL AND :nuseragent IS NULL) OR useragent = :useragent)
87
  AND ((ip IS NULL AND :nip IS NULL) OR ip = INET6_ATON(:ip))
88
  AND ((ipmask IS NULL AND :nipmask IS NULL) OR ipmask = :ipmask)
89
  AND (duration > UNIX_TIMESTAMP() OR duration IS NULL) 
90
  AND active = 1
91
  AND (domain IS NULL OR domain = :domain);
92
SQL;
93
94
        $statement = $this->database->prepare($query);
95
        $statement->execute([
96
            ':name'       => $name,
97
            ':nname'      => $name,
98
            ':email'      => $email,
99
            ':nemail'     => $email,
100
            ':ip'         => $ip,
101
            ':nip'        => $ip,
102
            ':ipmask'     => $mask,
103
            ':nipmask'    => $mask,
104
            ':useragent'  => $useragent,
105
            ':nuseragent' => $useragent,
106
            ':domain'     => $domain,
107
        ]);
108
109
        $result = array();
110
111
        /** @var Ban $v */
112
        foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
113
            $v->setDatabase($this->database);
114
            $result[] = $v;
115
        }
116
117
        return $result;
118
    }
119
120
    public function isActive(Ban $ban): bool
121
    {
122
        if (!$ban->isActive()) {
123
            return false;
124
        }
125
126
        if ($ban->getDuration() !== null && $ban->getDuration() < time()) {
127
            return false;
128
        }
129
130
        return true;
131
    }
132
133
    public function canUnban(Ban $ban): bool
134
    {
135
        if ($this->securityManager === null) {
136
            return false;
137
        }
138
139
        if (!$this->isActive($ban)) {
140
            return false;
141
        }
142
143
        $user = User::getCurrent($this->database);
144
145
        $allowed = true;
146
        $allowed = $allowed && ($ban->getName() === null || $this->securityManager->allows('BanType', 'name', $user) === ISecurityManager::ALLOWED);
147
        $allowed = $allowed && ($ban->getEmail() === null || $this->securityManager->allows('BanType', 'email', $user) === ISecurityManager::ALLOWED);
148
        $allowed = $allowed && ($ban->getIp() === null || $this->securityManager->allows('BanType', 'ip', $user) === ISecurityManager::ALLOWED);
149
        $allowed = $allowed && ($ban->getUseragent() === null || $this->securityManager->allows('BanType', 'useragent', $user) === ISecurityManager::ALLOWED);
150
151
        if ($ban->getDomain() === null) {
152
            $allowed &= $this->securityManager->allows('BanType', 'global', $user) === ISecurityManager::ALLOWED;
153
        }
154
        else {
155
            $currentDomain = Domain::getCurrent($this->database);
156
            $allowed &= $currentDomain->getId() === $ban->getDomain();
157
        }
158
159
        $allowed = $allowed && $this->securityManager->allows('BanVisibility', $ban->getVisibility(), $user) === ISecurityManager::ALLOWED;
160
161
        return $allowed;
162
    }
163
164
    /**
165
     * @param Request $request
166
     *
167
     * @return Ban[]
168
     */
169
    private function getBansForRequestFromDatabase(Request $request): array
170
    {
171
        /** @noinspection SqlConstantCondition - included for clarity of code */
172
        $query = <<<SQL
173
SELECT b.* FROM ban b
174
LEFT JOIN netmask n ON 1 = 1
175
    AND n.cidr = b.ipmask
176
    AND n.protocol = CASE WHEN INET6_ATON(b.ip) IS NOT NULL THEN 6 ELSE 4 END
177
WHERE 1 = 1
178
    AND COALESCE(:name RLIKE name, TRUE)
179
    AND COALESCE(:email RLIKE email, TRUE)
180
    AND COALESCE(:useragent RLIKE useragent, TRUE)
181
    AND (
182
        (INET6_ATON(b.ip) IS NOT NULL AND
183
            (CONV(LEFT(HEX(INET6_ATON(b.ip)), 16), 16, 10) & n.maskh) = (CONV(LEFT(HEX(INET6_ATON(:ip6)), 16), 16, 10) & n.maskh)
184
            AND (CONV(RIGHT(HEX(INET6_ATON(b.ip)), 16), 16, 10) & n.maskl) = (CONV(RIGHT(HEX(INET6_ATON(:ip6)), 16), 16, 10) & n.maskl))
185
        OR
186
        (INET6_ATON(b.ip) IS NULL AND
187
            (CONV(HEX(INET6_ATON(b.ip)), 16, 10) & n.maskl) = (CONV(HEX(INET6_ATON(:ip4)), 16, 10) & n.maskl))
188
    )
189
    AND active = 1
190
    AND (duration > UNIX_TIMESTAMP() OR duration IS NULL)
191
    AND (b.domain IS NULL OR b.domain = :domain)
192
SQL;
193
194
        $statement = $this->database->prepare($query);
0 ignored issues
show
Unused Code introduced by
The assignment to $statement is dead and can be removed.
Loading history...
195
        $trustedIp = $this->xffTrustProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
196
        $isIPv6 = filter_var($trustedIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
197
198
        $params = [
199
    ':name'      => $request->getName(),
200
    ':email'     => $request->getEmail(),
201
    ':useragent' => $request->getUserAgent(),
202
    ':domain'    => $request->getDomain(),
203
    ':ip4'       => $isIPv6 ? '' : $trustedIp,
204
    ':ip6'       => $isIPv6 ? $trustedIp : '',
205
];
206
        die(print_r($params, true));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return array. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
207
        $statement->execute($params);
0 ignored issues
show
Unused Code introduced by
$statement->execute($params) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
208
209
        /** @var Ban[] $result */
210
        $result = [];
211
212
        /** @var Ban $v */
213
        foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
214
            $v->setDatabase($this->database);
215
            $result[] = $v;
216
        }
217
218
        return $result;
219
    }
220
}
221