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

includes/Validation/RequestValidationHelper.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
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) {
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
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) {
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()) == "") {
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()) != "") {
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) {
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?
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'])) {
275
			return true;
276
		}
277
278 1
		return false;
279
	}
280
281 1
	private function userSulExists()
282
	{
283
		// @todo is this really necessary?!
284
		// $reqname = str_replace("_", " ", $this->request->getName());
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'])) {
299
			return true;
300
		}
301
302 1
		return false;
303
	}
304
305 1 View Code Duplication
	private function nameRequestExists()
0 ignored issues
show
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