Failed Conditions
Branch newinternal (3df7fe)
by Simon
04:13
created

RequestValidationHelper   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 295
Duplicated Lines 8.14 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 40.77%

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 33
c 6
b 1
f 1
lcom 1
cbo 8
dl 24
loc 295
ccs 53
cts 130
cp 0.4077
rs 9.3999

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 23 1
C validateName() 3 54 9
C validateEmail() 9 38 7
B validateOther() 0 30 3
A checkAntiSpoof() 0 13 3
A checkTitleBlacklist() 0 23 3
A userExists() 0 19 3
A userSulExists() 0 23 2
A nameRequestExists() 12 12 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
		// TODO: implement
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
108 1
		if ($this->userExists()) {
109
			$errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS);
110
		}
111
112
		// username part of SUL account
113
		// TODO: implement
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
114 1
		if ($this->userSulExists()) {
115
			// using same error slot as name exists - it's the same sort of error, and we probably only want to show one.
116
			$errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS_SUL);
117
		}
118
119
		// username is numbers
120 1 View Code Duplication
		if (preg_match("/^[0-9]+$/", $this->request->getName()) === 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
121
			$errorList[ValidationError::NAME_NUMONLY] = new ValidationError(ValidationError::NAME_NUMONLY);
122
		}
123
124
		// username can't contain #@/<>[]|{}
125 1
		if (preg_match("/[" . preg_quote("#@/<>[]|{}", "/") . "]/", $this->request->getName()) === 1) {
126
			$errorList[ValidationError::NAME_INVALIDCHAR] = new ValidationError(ValidationError::NAME_INVALIDCHAR);
127
		}
128
129
		// existing non-closed request for this name
130
		// TODO: implement
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
131 1
		if ($this->nameRequestExists()) {
132
			$errorList[ValidationError::OPEN_REQUEST_NAME] = new ValidationError(ValidationError::OPEN_REQUEST_NAME);
133
		}
134
135
		// WARNINGS
136
		// name has to be sanitised
137
		// TODO: implement
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
138 1
		if (false) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
139
			$errorList[ValidationError::NAME_SANITISED] = new ValidationError(ValidationError::NAME_SANITISED, false);
140
		}
141
142 1
		return $errorList;
143
	}
144
145
	/**
146
	 * Summary of validateEmail
147
	 * @return ValidationError[]
148
	 */
149
	public function validateEmail()
150
	{
151
		$errorList = array();
152
153
		// ERRORS
154
155
		// Email is banned
156
		$ban = $this->banHelper->emailIsBanned($this->request->getEmail());
157
		if ($ban != false) {
158
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
159
		}
160
161
		// email addresses must match
162
		if ($this->request->getEmail() != $this->emailConfirmation) {
163
			$errorList[ValidationError::EMAIL_MISMATCH] = new ValidationError(ValidationError::EMAIL_MISMATCH);
164
		}
165
166
		// email address must be validly formed
167 View Code Duplication
		if (trim($this->request->getEmail()) == "") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
168
			$errorList[ValidationError::EMAIL_EMPTY] = new ValidationError(ValidationError::EMAIL_EMPTY);
169
		}
170
171
		// email address must be validly formed
172
		if (!filter_var($this->request->getEmail(), FILTER_VALIDATE_EMAIL)) {
173 View Code Duplication
			if (trim($this->request->getEmail()) != "") {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
174
				$errorList[ValidationError::EMAIL_INVALID] = new ValidationError(ValidationError::EMAIL_INVALID);
175
			}
176
		}
177
178
		// email address can't be wikimedia/wikipedia .com/org
179 View Code Duplication
		if (preg_match('/.*@.*wiki(m.dia|p.dia)\.(org|com)/i', $this->request->getEmail()) === 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
180
			$errorList[ValidationError::EMAIL_WIKIMEDIA] = new ValidationError(ValidationError::EMAIL_WIKIMEDIA);
181
		}
182
183
		// WARNINGS
184
185
		return $errorList;
186
	}
187
188
	/**
189
	 * Summary of validateOther
190
	 * @return ValidationError[]
191
	 */
192
	public function validateOther()
193
	{
194
		$errorList = array();
195
196
		$trustedIp = $this->xffTrustProvider->getTrustedClientIp($this->request->getIp(),
197
			$this->request->getForwardedIp());
198
199
		// ERRORS
200
201
		// TOR nodes
202
		if ($this->torExitProvider->isTorExit($trustedIp)) {
203
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED_TOR);
204
		}
205
206
		// IP banned
207
		$ban = $this->banHelper->ipIsBanned($trustedIp);
208
		if ($ban != false) {
209
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
210
		}
211
212
		// WARNINGS
213
214
		// Antispoof check
215
		$this->checkAntiSpoof();
216
217
		// Blacklist check
218
		$this->checkTitleBlacklist();
219
220
		return $errorList;
221
	}
222
223
	private function checkAntiSpoof()
224
	{
225
		try {
226
			if (count($this->antiSpoofProvider->getSpoofs($this->request->getName())) > 0) {
227
				// If there were spoofs an Admin should handle the request.
228
				$this->request->setStatus("Flagged users");
229
			}
230
		}
231
		catch (Exception $ex) {
232
			// hrm.
233
			// TODO: log this?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
234
		}
235
	}
236
237
	private function checkTitleBlacklist()
238
	{
239
		if ($this->titleBlacklistEnabled == 1) {
240
			$apiResult = $this->httpHelper->get(
241
				$this->mediawikiApiEndpoint,
242
				array(
243
					'action'       => 'titleblacklist',
244
					'tbtitle'      => $this->request->getName(),
245
					'tbaction'     => 'new-account',
246
					'tbnooverride' => true,
247
					'format'       => 'php',
248
				)
249
			);
250
251
			$data = unserialize($apiResult);
252
253
			$requestIsOk = $data['titleblacklist']['result'] == "ok";
254
255
			if (!$requestIsOk) {
256
				$this->request->setStatus("Flagged users");
257
			}
258
		}
259
	}
260
261 1
	private function userExists()
262
	{
263 1
		$userexist = $this->httpHelper->get(
264 1
			$this->mediawikiApiEndpoint,
265
			array(
266 1
				'action'  => 'query',
267 1
				'list'    => 'users',
268 1
				'ususers' => $this->request->getName(),
269 1
				'format'  => 'php',
270
			)
271 1
		);
272
273 1
		$ue = unserialize($userexist);
274 1
		if (!isset ($ue['query']['users']['0']['missing']) && isset ($ue['query']['users']['0']['userid'])) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !isset($ue['query...sers']['0']['userid']);.
Loading history...
275
			return true;
276
		}
277
278 1
		return false;
279
	}
280
281 1
	private function userSulExists()
282
	{
283
		// @todo is this really necessary?!
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
284
		// $reqname = str_replace("_", " ", $this->request->getName());
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
285 1
		$reqname = $this->request->getName();
286
287 1
		$userexist = $this->httpHelper->get(
288 1
			$this->mediawikiApiEndpoint,
289
			array(
290 1
				'action'  => 'query',
291 1
				'meta'    => 'globaluserinfo',
292 1
				'guiuser' => $reqname,
293 1
				'format'  => 'php',
294
			)
295 1
		);
296
297 1
		$ue = unserialize($userexist);
298 1
		if (isset ($ue['query']['globaluserinfo']['id'])) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return isset($ue['query'...lobaluserinfo']['id']);.
Loading history...
299
			return true;
300
		}
301
302 1
		return false;
303
	}
304
305 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...
306
	{
307 1
		$query = "SELECT COUNT(id) FROM request WHERE status != 'Closed' AND name = :name;";
308 1
		$statement = $this->database->prepare($query);
309 1
		$statement->execute(array(':name' => $this->request->getName()));
310
311 1
		if (!$statement) {
312
			return false;
313
		}
314
315 1
		return $statement->fetchColumn() > 0;
316
	}
317
}
318