Completed
Pull Request — newinternal (#285)
by Simon
06:16 queued 03:05
created

RequestValidationHelper   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 287
Duplicated Lines 8.36 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 47.62%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
dl 24
loc 287
ccs 50
cts 105
cp 0.4762
rs 9.6
c 4
b 1
f 0
wmc 32
lcom 1
cbo 8

9 Methods

Rating   Name   Duplication   Size   Complexity  
D validateName() 3 44 8
C validateEmail() 9 38 7
A checkTitleBlacklist() 0 23 3
A userExists() 0 19 3
A nameRequestExists() 12 12 2
A __construct() 0 23 1
B validateOther() 0 30 3
A checkAntiSpoof() 0 12 3
A userSulExists() 0 21 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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) {
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.
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 121 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
114
            $errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS_SUL);
115
        }
116
117
        // username is numbers
118 1 View Code Duplication
        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) {
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 View Code Duplication
        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 View Code Duplication
            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 View Code Duplication
        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) {
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 View Code Duplication
    private function nameRequestExists()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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