Completed
Branch newinternal (cd27a7)
by Simon
03:47
created

RequestValidationHelper::userSulExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2.0078

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
ccs 7
cts 8
cp 0.875
rs 9.4285
cc 2
eloc 8
nc 2
nop 0
crap 2.0078
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\Interfaces\IBanHelper;
14
use Waca\PdoDatabase;
15
use Waca\Providers\Interfaces\IAntiSpoofProvider;
16
use Waca\Providers\Interfaces\IXffTrustProvider;
17
18
/**
19
 * Performs the validation of an incoming request.
20
 */
21
class RequestValidationHelper
22
{
23
	/** @var IBanHelper */
24
	private $banHelper;
25
	/** @var Request */
26
	private $request;
27
	private $emailConfirmation;
28
	/** @var PdoDatabase */
29
	private $database;
30
	/** @var IAntiSpoofProvider */
31
	private $antiSpoofProvider;
32
	/** @var IXffTrustProvider */
33
	private $xffTrustProvider;
34
35
	/**
36
	 * Summary of __construct
37
	 *
38
	 * @param IBanHelper         $banHelper
39
	 * @param Request            $request
40
	 * @param string             $emailConfirmation
41
	 * @param PdoDatabase        $database
42
	 * @param IAntiSpoofProvider $antiSpoofProvider
43
	 * @param IXffTrustProvider  $xffTrustProvider
44
	 */
45 1
	public function __construct(
46
		IBanHelper $banHelper,
47
		Request $request,
48
		$emailConfirmation,
49
		PdoDatabase $database,
50
		IAntiSpoofProvider $antiSpoofProvider,
51
		IXffTrustProvider $xffTrustProvider
52
	) {
53 1
		$this->banHelper = $banHelper;
54 1
		$this->request = $request;
55 1
		$this->emailConfirmation = $emailConfirmation;
56 1
		$this->database = $database;
57 1
		$this->antiSpoofProvider = $antiSpoofProvider;
58 1
		$this->xffTrustProvider = $xffTrustProvider;
59 1
	}
60
61
	/**
62
	 * Summary of validateName
63
	 * @return ValidationError[]
64
	 */
65 1
	public function validateName()
66
	{
67 1
		$errorList = array();
68
69
		// ERRORS
70
		// name is empty
71 1
		if (trim($this->request->getName()) == "") {
72
			$errorList[ValidationError::NAME_EMPTY] = new ValidationError(ValidationError::NAME_EMPTY);
73
		}
74
75
		// name is banned
76 1
		$ban = $this->banHelper->nameIsBanned($this->request->getName());
77 1
		if ($ban != false) {
78
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
79
		}
80
81
		// username already exists
82
		// 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...
83 1
		if ($this->userExists()) {
84
			$errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS);
85
		}
86
87
		// username part of SUL account
88
		// 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...
89 1
		if ($this->userSulExists()) {
90
			// using same error slot as name exists - it's the same sort of error, and we probably only want to show one.
91
			$errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS_SUL);
92
		}
93
94
		// username is numbers
95 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...
96
			$errorList[ValidationError::NAME_NUMONLY] = new ValidationError(ValidationError::NAME_NUMONLY);
97
		}
98
99
		// username can't contain #@/<>[]|{}
100 1
		if (preg_match("/[" . preg_quote("#@/<>[]|{}", "/") . "]/", $this->request->getName()) === 1) {
101
			$errorList[ValidationError::NAME_INVALIDCHAR] = new ValidationError(ValidationError::NAME_INVALIDCHAR);
102
		}
103
104
		// existing non-closed request for this name
105
		// 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...
106 1
		if ($this->nameRequestExists()) {
107
			$errorList[ValidationError::OPEN_REQUEST_NAME] = new ValidationError(ValidationError::OPEN_REQUEST_NAME);
108
		}
109
110
		// WARNINGS
111
		// name has to be sanitised
112
		// 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...
113 1
		if (false) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
114
			$errorList[ValidationError::NAME_SANITISED] = new ValidationError(ValidationError::NAME_SANITISED, false);
115
		}
116
117 1
		return $errorList;
118
	}
119
120
	/**
121
	 * Summary of validateEmail
122
	 * @return ValidationError[]
123
	 */
124
	public function validateEmail()
125
	{
126
		$errorList = array();
127
128
		// ERRORS
129
130
		// Email is banned
131
		$ban = $this->banHelper->emailIsBanned($this->request->getEmail());
132
		if ($ban != false) {
133
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
134
		}
135
136
		// email addresses must match
137
		if ($this->request->getEmail() != $this->emailConfirmation) {
138
			$errorList[ValidationError::EMAIL_MISMATCH] = new ValidationError(ValidationError::EMAIL_MISMATCH);
139
		}
140
141
		// email address must be validly formed
142 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...
143
			$errorList[ValidationError::EMAIL_EMPTY] = new ValidationError(ValidationError::EMAIL_EMPTY);
144
		}
145
146
		// email address must be validly formed
147
		if (!filter_var($this->request->getEmail(), FILTER_VALIDATE_EMAIL)) {
148 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...
149
				$errorList[ValidationError::EMAIL_INVALID] = new ValidationError(ValidationError::EMAIL_INVALID);
150
			}
151
		}
152
153
		// email address can't be wikimedia/wikipedia .com/org
154 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...
155
			$errorList[ValidationError::EMAIL_WIKIMEDIA] = new ValidationError(ValidationError::EMAIL_WIKIMEDIA);
156
		}
157
158
		// WARNINGS
159
160
		return $errorList;
161
	}
162
163
	/**
164
	 * Summary of validateOther
165
	 * @return ValidationError[]
166
	 */
167
	public function validateOther()
168
	{
169
		$errorList = array();
170
171
		// ERRORS
172
173
		// TOR nodes
174
		// 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...
175
		if (false) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
176
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED_TOR);
177
		}
178
179
		// IP banned
180
		$trustedIp = $this->xffTrustProvider->getTrustedClientIp($this->request->getIp(), $this->request->getForwardedIp());
181
		$ban = $this->banHelper->ipIsBanned($trustedIp);
182
		if ($ban != false) {
183
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
184
		}
185
186
		// WARNINGS
187
188
		// Antispoof check
189
		$this->checkAntiSpoof();
190
191
		// Blacklist check
192
		$this->checkTitleBlacklist();
193
194
		return $errorList;
195
	}
196
197
	private function checkAntiSpoof()
198
	{
199
		try {
200
			if (count($this->antiSpoofProvider->getSpoofs($this->request->getName())) > 0) {
201
				// If there were spoofs an Admin should handle the request.
202
				$this->request->setStatus("Flagged users");
203
			}
204
		}
205
		catch (Exception $ex) {
206
			// hrm.
207
			// 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...
208
		}
209
	}
210
211
	private function checkTitleBlacklist()
212
	{
213
		global $enableTitleblacklist;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
214
		if ($enableTitleblacklist == 1) {
215
			$apiResult = file_get_contents("https://en.wikipedia.org/w/api.php?action=titleblacklist&tbtitle=" . urlencode($this->request->getName()) . "&tbaction=new-account&tbnooverride&format=php");
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 192 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...
216
217
			$data = unserialize($apiResult);
218
219
			$requestIsOk = $data['titleblacklist']['result'] == "ok";
220
221
			if (!$requestIsOk) {
222
				$this->request->setStatus("Flagged users");
223
			}
224
		}
225
	}
226
227 1
	private function userExists()
228
	{
229 1
		global $mediawikiWebServiceEndpoint;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
230
231 1
		$userexist = file_get_contents($mediawikiWebServiceEndpoint . "?action=query&list=users&ususers=" . urlencode($this->request->getName()) . "&format=php");
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 156 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...
232 1
		$ue = unserialize($userexist);
233 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...
234
			return true;
235
		}
236
237 1
		return false;
238
	}
239
240 1
	private function userSulExists()
241
	{
242 1
		global $mediawikiWebServiceEndpoint;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
243
244 1
		$reqname = str_replace("_", " ", $this->request->getName());
245 1
		$userexist = file_get_contents($mediawikiWebServiceEndpoint . "?action=query&meta=globaluserinfo&guiuser=" . urlencode($reqname) . "&format=php");
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 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...
246 1
		$ue = unserialize($userexist);
247 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...
248
			return true;
249
		}
250
251 1
		return false;
252
	}
253
254 1
	private function nameRequestExists()
255
	{
256 1
		$query = "SELECT COUNT(id) FROM request WHERE status != 'Closed' AND name = :name;";
257 1
		$statement = $this->database->prepare($query);
258 1
		$statement->execute(array(':name' => $this->request->getName()));
259
260 1
		if (!$statement) {
261
			return false;
262
		}
263
264 1
		return $statement->fetchColumn() > 0;
265
	}
266
}
267