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

RequestValidationHelper::userExists()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 11
c 2
b 1
f 0
dl 0
loc 18
ccs 0
cts 16
cp 0
rs 9.9
cc 3
nc 2
nop 0
crap 12
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
    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
        $this->banHelper = $banHelper;
75
        $this->request = $request;
76
        $this->emailConfirmation = $emailConfirmation;
77
        $this->database = $database;
78
        $this->antiSpoofProvider = $antiSpoofProvider;
79
        $this->xffTrustProvider = $xffTrustProvider;
80
        $this->httpHelper = $httpHelper;
81
        $this->mediawikiApiEndpoint = $mediawikiApiEndpoint;
82
        $this->titleBlacklistEnabled = $titleBlacklistEnabled;
83
        $this->torExitProvider = $torExitProvider;
84
    }
85
86
    /**
87
     * Summary of validateName
88
     * @return ValidationError[]
89
     */
90
    public function validateName()
91
    {
92
        $errorList = array();
93
94
        // ERRORS
95
        // name is empty
96
        if (trim($this->request->getName()) == "") {
97
            $errorList[ValidationError::NAME_EMPTY] = new ValidationError(ValidationError::NAME_EMPTY);
98
        }
99
100
        // username already exists
101
        if ($this->userExists()) {
102
            $errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS);
103
        }
104
105
        // username part of SUL account
106
        if ($this->userSulExists()) {
107
            // using same error slot as name exists - it's the same sort of error, and we probably only want to show one.
108
            $errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS_SUL);
109
        }
110
111
        // username is numbers
112
        if (preg_match("/^[0-9]+$/", $this->request->getName()) === 1) {
113
            $errorList[ValidationError::NAME_NUMONLY] = new ValidationError(ValidationError::NAME_NUMONLY);
114
        }
115
116
        // username can't contain #@/<>[]|{}
117
        if (preg_match("/[" . preg_quote("#@/<>[]|{}", "/") . "]/", $this->request->getName()) === 1) {
118
            $errorList[ValidationError::NAME_INVALIDCHAR] = new ValidationError(ValidationError::NAME_INVALIDCHAR);
119
        }
120
121
        // existing non-closed request for this name
122
        if ($this->nameRequestExists()) {
123
            $errorList[ValidationError::OPEN_REQUEST_NAME] = new ValidationError(ValidationError::OPEN_REQUEST_NAME);
124
        }
125
126
        return $errorList;
127
    }
128
129
    /**
130
     * Summary of validateEmail
131
     * @return ValidationError[]
132
     */
133
    public function validateEmail()
134
    {
135
        $errorList = array();
136
137
        // ERRORS
138
139
        // email addresses must match
140
        if ($this->request->getEmail() != $this->emailConfirmation) {
141
            $errorList[ValidationError::EMAIL_MISMATCH] = new ValidationError(ValidationError::EMAIL_MISMATCH);
142
        }
143
144
        // email address must be validly formed
145
        if (trim($this->request->getEmail()) == "") {
146
            $errorList[ValidationError::EMAIL_EMPTY] = new ValidationError(ValidationError::EMAIL_EMPTY);
147
        }
148
149
        // email address must be validly formed
150
        if (!filter_var($this->request->getEmail(), FILTER_VALIDATE_EMAIL)) {
151
            if (trim($this->request->getEmail()) != "") {
152
                $errorList[ValidationError::EMAIL_INVALID] = new ValidationError(ValidationError::EMAIL_INVALID);
153
            }
154
        }
155
156
        // email address can't be wikimedia/wikipedia .com/org
157
        if (preg_match('/.*@.*wiki(m.dia|p.dia)\.(org|com)/i', $this->request->getEmail()) === 1) {
158
            $errorList[ValidationError::EMAIL_WIKIMEDIA] = new ValidationError(ValidationError::EMAIL_WIKIMEDIA);
159
        }
160
161
        // WARNINGS
162
163
        return $errorList;
164
    }
165
166
    /**
167
     * Summary of validateOther
168
     * @return ValidationError[]
169
     */
170
    public function validateOther()
171
    {
172
        $errorList = array();
173
174
        $trustedIp = $this->xffTrustProvider->getTrustedClientIp($this->request->getIp(),
175
            $this->request->getForwardedIp());
176
177
        // ERRORS
178
179
        // TOR nodes
180
        if ($this->torExitProvider->isTorExit($trustedIp)) {
181
            $errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED_TOR);
182
        }
183
184
        // Bans
185
        if ($this->banHelper->isBanned($this->request)) {
186
            $errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
187
        }
188
189
        // WARNINGS
190
191
        // Antispoof check
192
        $this->checkAntiSpoof();
193
194
        // Blacklist check
195
        $this->checkTitleBlacklist();
196
197
        return $errorList;
198
    }
199
200
    private function checkAntiSpoof()
201
    {
202
        try {
203
            if (count($this->antiSpoofProvider->getSpoofs($this->request->getName())) > 0) {
204
                // If there were spoofs an Admin should handle the request.
205
                $this->request->setStatus("Flagged users");
206
            }
207
        }
208
        catch (Exception $ex) {
209
            // logme
210
        }
211
    }
212
213
    private function checkTitleBlacklist()
214
    {
215
        if ($this->titleBlacklistEnabled == 1) {
216
            $apiResult = $this->httpHelper->get(
217
                $this->mediawikiApiEndpoint,
218
                array(
219
                    'action'       => 'titleblacklist',
220
                    'tbtitle'      => $this->request->getName(),
221
                    'tbaction'     => 'new-account',
222
                    'tbnooverride' => true,
223
                    'format'       => 'php',
224
                )
225
            );
226
227
            $data = unserialize($apiResult);
228
229
            $requestIsOk = $data['titleblacklist']['result'] == "ok";
230
231
            if (!$requestIsOk) {
232
                $this->request->setStatus("Flagged users");
233
            }
234
        }
235
    }
236
237
    private function userExists()
238
    {
239
        $userExists = $this->httpHelper->get(
240
            $this->mediawikiApiEndpoint,
241
            array(
242
                'action'  => 'query',
243
                'list'    => 'users',
244
                'ususers' => $this->request->getName(),
245
                'format'  => 'php',
246
            )
247
        );
248
249
        $ue = unserialize($userExists);
250
        if (!isset ($ue['query']['users']['0']['missing']) && isset ($ue['query']['users']['0']['userid'])) {
251
            return true;
252
        }
253
254
        return false;
255
    }
256
257
    private function userSulExists()
258
    {
259
        $requestName = $this->request->getName();
260
261
        $userExists = $this->httpHelper->get(
262
            $this->mediawikiApiEndpoint,
263
            array(
264
                'action'  => 'query',
265
                'meta'    => 'globaluserinfo',
266
                'guiuser' => $requestName,
267
                'format'  => 'php',
268
            )
269
        );
270
271
        $ue = unserialize($userExists);
272
        if (isset ($ue['query']['globaluserinfo']['id'])) {
273
            return true;
274
        }
275
276
        return false;
277
    }
278
279
    /**
280
     * Checks if a request with this name is currently open
281
     *
282
     * @return bool
283
     */
284
    private function nameRequestExists()
285
    {
286
        $query = "SELECT COUNT(id) FROM request WHERE status != 'Closed' AND name = :name;";
287
        $statement = $this->database->prepare($query);
288
        $statement->execute(array(':name' => $this->request->getName()));
289
290
        if (!$statement) {
291
            return false;
292
        }
293
294
        return $statement->fetchColumn() > 0;
295
    }
296
}
297