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

RequestValidationHelper::userSulExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2.0017

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 23
ccs 12
cts 13
cp 0.9231
rs 9.0856
cc 2
eloc 13
nc 2
nop 0
crap 2.0017
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