Passed
Pull Request — newinternal (#556)
by Matthew
03:29
created

RequestValidationHelper   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 285
Duplicated Lines 0 %

Test Coverage

Coverage 47.62%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 32
eloc 109
c 4
b 1
f 0
dl 0
loc 285
ccs 50
cts 105
cp 0.4762
rs 9.84

9 Methods

Rating   Name   Duplication   Size   Complexity  
A checkAntiSpoof() 0 9 3
A validateOther() 0 29 3
B validateName() 0 43 8
A __construct() 0 22 1
A userSulExists() 0 20 2
A checkTitleBlacklist() 0 20 3
A nameRequestExists() 0 11 2
A userExists() 0 18 3
B validateEmail() 0 37 7
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\Validation;
10
11
use Exception;
12
use Waca\DataObjects\Request;
13
use Waca\Helpers\HttpHelper;
14
use Waca\Helpers\Interfaces\IBanHelper;
15
use Waca\PdoDatabase;
16
use Waca\Providers\Interfaces\IAntiSpoofProvider;
17
use Waca\Providers\Interfaces\IXffTrustProvider;
18
use Waca\Providers\TorExitProvider;
19
20
/**
21
 * Performs the validation of an incoming request.
22
 */
23
class RequestValidationHelper
24
{
25
    /** @var IBanHelper */
26
    private $banHelper;
27
    /** @var Request */
28
    private $request;
29
    private $emailConfirmation;
30
    /** @var PdoDatabase */
31
    private $database;
32
    /** @var IAntiSpoofProvider */
33
    private $antiSpoofProvider;
34
    /** @var IXffTrustProvider */
35
    private $xffTrustProvider;
36
    /** @var HttpHelper */
37
    private $httpHelper;
38
    /**
39
     * @var string
40
     */
41
    private $mediawikiApiEndpoint;
42
    private $titleBlacklistEnabled;
43
    /**
44
     * @var TorExitProvider
45
     */
46
    private $torExitProvider;
47
48
    /**
49
     * Summary of __construct
50
     *
51
     * @param IBanHelper         $banHelper
52
     * @param Request            $request
53
     * @param string             $emailConfirmation
54
     * @param PdoDatabase        $database
55
     * @param IAntiSpoofProvider $antiSpoofProvider
56
     * @param IXffTrustProvider  $xffTrustProvider
57
     * @param HttpHelper         $httpHelper
58
     * @param string             $mediawikiApiEndpoint
59
     * @param boolean            $titleBlacklistEnabled
60
     * @param TorExitProvider    $torExitProvider
61
     */
62 1
    public function __construct(
63
        IBanHelper $banHelper,
64
        Request $request,
65
        $emailConfirmation,
66
        PdoDatabase $database,
67
        IAntiSpoofProvider $antiSpoofProvider,
68
        IXffTrustProvider $xffTrustProvider,
69
        HttpHelper $httpHelper,
70
        $mediawikiApiEndpoint,
71
        $titleBlacklistEnabled,
72
        TorExitProvider $torExitProvider
73
    ) {
74 1
        $this->banHelper = $banHelper;
75 1
        $this->request = $request;
76 1
        $this->emailConfirmation = $emailConfirmation;
77 1
        $this->database = $database;
78 1
        $this->antiSpoofProvider = $antiSpoofProvider;
79 1
        $this->xffTrustProvider = $xffTrustProvider;
80 1
        $this->httpHelper = $httpHelper;
81 1
        $this->mediawikiApiEndpoint = $mediawikiApiEndpoint;
82 1
        $this->titleBlacklistEnabled = $titleBlacklistEnabled;
83 1
        $this->torExitProvider = $torExitProvider;
84 1
    }
85
86
    /**
87
     * Summary of validateName
88
     * @return ValidationError[]
89
     */
90 1
    public function validateName()
91
    {
92 1
        $errorList = array();
93
94
        // ERRORS
95
        // name is empty
96 1
        if (trim($this->request->getName()) == "") {
97
            $errorList[ValidationError::NAME_EMPTY] = new ValidationError(ValidationError::NAME_EMPTY);
98
        }
99
100
        // name is banned
101 1
        $ban = $this->banHelper->nameIsBanned($this->request->getName());
102 1
        if ($ban != false) {
0 ignored issues
show
introduced by
The condition $ban != false is always true.
Loading history...
103
            $errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
104
        }
105
106
        // username already exists
107 1
        if ($this->userExists()) {
108
            $errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS);
109
        }
110
111
        // username part of SUL account
112 1
        if ($this->userSulExists()) {
113
            // using same error slot as name exists - it's the same sort of error, and we probably only want to show one.
114
            $errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS_SUL);
115
        }
116
117
        // username is numbers
118 1
        if (preg_match("/^[0-9]+$/", $this->request->getName()) === 1) {
119
            $errorList[ValidationError::NAME_NUMONLY] = new ValidationError(ValidationError::NAME_NUMONLY);
120
        }
121
122
        // username can't contain #@/<>[]|{}
123 1
        if (preg_match("/[" . preg_quote("#@/<>[]|{}", "/") . "]/", $this->request->getName()) === 1) {
124
            $errorList[ValidationError::NAME_INVALIDCHAR] = new ValidationError(ValidationError::NAME_INVALIDCHAR);
125
        }
126
127
        // existing non-closed request for this name
128 1
        if ($this->nameRequestExists()) {
129
            $errorList[ValidationError::OPEN_REQUEST_NAME] = new ValidationError(ValidationError::OPEN_REQUEST_NAME);
130
        }
131
132 1
        return $errorList;
133
    }
134
135
    /**
136
     * Summary of validateEmail
137
     * @return ValidationError[]
138
     */
139
    public function validateEmail()
140
    {
141
        $errorList = array();
142
143
        // ERRORS
144
145
        // Email is banned
146
        $ban = $this->banHelper->emailIsBanned($this->request->getEmail());
147
        if ($ban != false) {
0 ignored issues
show
introduced by
The condition $ban != false is always true.
Loading history...
148
            $errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
149
        }
150
151
        // email addresses must match
152
        if ($this->request->getEmail() != $this->emailConfirmation) {
153
            $errorList[ValidationError::EMAIL_MISMATCH] = new ValidationError(ValidationError::EMAIL_MISMATCH);
154
        }
155
156
        // email address must be validly formed
157
        if (trim($this->request->getEmail()) == "") {
158
            $errorList[ValidationError::EMAIL_EMPTY] = new ValidationError(ValidationError::EMAIL_EMPTY);
159
        }
160
161
        // email address must be validly formed
162
        if (!filter_var($this->request->getEmail(), FILTER_VALIDATE_EMAIL)) {
163
            if (trim($this->request->getEmail()) != "") {
164
                $errorList[ValidationError::EMAIL_INVALID] = new ValidationError(ValidationError::EMAIL_INVALID);
165
            }
166
        }
167
168
        // email address can't be wikimedia/wikipedia .com/org
169
        if (preg_match('/.*@.*wiki(m.dia|p.dia)\.(org|com)/i', $this->request->getEmail()) === 1) {
170
            $errorList[ValidationError::EMAIL_WIKIMEDIA] = new ValidationError(ValidationError::EMAIL_WIKIMEDIA);
171
        }
172
173
        // WARNINGS
174
175
        return $errorList;
176
    }
177
178
    /**
179
     * Summary of validateOther
180
     * @return ValidationError[]
181
     */
182
    public function validateOther()
183
    {
184
        $errorList = array();
185
186
        $trustedIp = $this->xffTrustProvider->getTrustedClientIp($this->request->getIp(),
187
            $this->request->getForwardedIp());
188
189
        // ERRORS
190
191
        // TOR nodes
192
        if ($this->torExitProvider->isTorExit($trustedIp)) {
193
            $errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED_TOR);
194
        }
195
196
        // IP banned
197
        $ban = $this->banHelper->ipIsBanned($trustedIp);
198
        if ($ban != false) {
0 ignored issues
show
introduced by
The condition $ban != false is always true.
Loading history...
199
            $errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
200
        }
201
202
        // WARNINGS
203
204
        // Antispoof check
205
        $this->checkAntiSpoof();
206
207
        // Blacklist check
208
        $this->checkTitleBlacklist();
209
210
        return $errorList;
211
    }
212
213
    private function checkAntiSpoof()
214
    {
215
        try {
216
            if (count($this->antiSpoofProvider->getSpoofs($this->request->getName())) > 0) {
217
                // If there were spoofs an Admin should handle the request.
218
                $this->request->setStatus("Flagged users");
219
            }
220
        }
221
        catch (Exception $ex) {
222
            // logme
223
        }
224
    }
225
226
    private function checkTitleBlacklist()
227
    {
228
        if ($this->titleBlacklistEnabled == 1) {
229
            $apiResult = $this->httpHelper->get(
230
                $this->mediawikiApiEndpoint,
231
                array(
232
                    'action'       => 'titleblacklist',
233
                    'tbtitle'      => $this->request->getName(),
234
                    'tbaction'     => 'new-account',
235
                    'tbnooverride' => true,
236
                    'format'       => 'php',
237
                )
238
            );
239
240
            $data = unserialize($apiResult);
241
242
            $requestIsOk = $data['titleblacklist']['result'] == "ok";
243
244
            if (!$requestIsOk) {
245
                $this->request->setStatus("Flagged users");
246
            }
247
        }
248
    }
249
250 1
    private function userExists()
251
    {
252 1
        $userExists = $this->httpHelper->get(
253 1
            $this->mediawikiApiEndpoint,
254
            array(
255 1
                'action'  => 'query',
256 1
                'list'    => 'users',
257 1
                'ususers' => $this->request->getName(),
258 1
                'format'  => 'php',
259
            )
260
        );
261
262 1
        $ue = unserialize($userExists);
263 1
        if (!isset ($ue['query']['users']['0']['missing']) && isset ($ue['query']['users']['0']['userid'])) {
264
            return true;
265
        }
266
267 1
        return false;
268
    }
269
270 1
    private function userSulExists()
271
    {
272 1
        $requestName = $this->request->getName();
273
274 1
        $userExists = $this->httpHelper->get(
275 1
            $this->mediawikiApiEndpoint,
276
            array(
277 1
                'action'  => 'query',
278 1
                'meta'    => 'globaluserinfo',
279 1
                'guiuser' => $requestName,
280 1
                'format'  => 'php',
281
            )
282
        );
283
284 1
        $ue = unserialize($userExists);
285 1
        if (isset ($ue['query']['globaluserinfo']['id'])) {
286
            return true;
287
        }
288
289 1
        return false;
290
    }
291
292
    /**
293
     * Checks if a request with this name is currently open
294
     *
295
     * @return bool
296
     */
297 1
    private function nameRequestExists()
298
    {
299 1
        $query = "SELECT COUNT(id) FROM request WHERE status != 'Closed' AND name = :name;";
300 1
        $statement = $this->database->prepare($query);
301 1
        $statement->execute(array(':name' => $this->request->getName()));
302
303 1
        if (!$statement) {
304
            return false;
305
        }
306
307 1
        return $statement->fetchColumn() > 0;
308
    }
309
}
310