Completed
Branch newinternal (ffe884)
by Simon
04:07
created

RequestValidationHelper::userExists()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261
Metric Value
dl 0
loc 12
ccs 6
cts 7
cp 0.8571
rs 9.4285
cc 3
eloc 7
nc 2
nop 0
crap 3.0261
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
33
	/**
34
	 * Summary of __construct
35
	 *
36
	 * @param IBanHelper         $banHelper
37
	 * @param Request            $request
38
	 * @param string             $emailConfirmation
39
	 * @param PdoDatabase        $database
40
	 * @param IAntiSpoofProvider $antiSpoofProvider
41
	 */
42 1
	public function __construct(
43
		IBanHelper $banHelper,
44
		Request $request,
45
		$emailConfirmation,
46
		PdoDatabase $database,
47
		IAntiSpoofProvider $antiSpoofProvider
48
	) {
49 1
		$this->banHelper = $banHelper;
50 1
		$this->request = $request;
51 1
		$this->emailConfirmation = $emailConfirmation;
52 1
		$this->database = $database;
53 1
		$this->antiSpoofProvider = $antiSpoofProvider;
54 1
	}
55
56
	/**
57
	 * Summary of validateName
58
	 * @return ValidationError[]
59
	 */
60 1
	public function validateName()
61
	{
62 1
		$errorList = array();
63
64
		// ERRORS
65
		// name is empty
66 1
		if (trim($this->request->getName()) == "") {
67
			$errorList[ValidationError::NAME_EMPTY] = new ValidationError(ValidationError::NAME_EMPTY);
68
		}
69
70
		// name is banned
71 1
		$ban = $this->banHelper->nameIsBanned($this->request->getName());
72 1
		if ($ban != false) {
73
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
74
		}
75
76
		// username already exists
77
		// 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...
78 1
		if ($this->userExists()) {
79
			$errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS);
80
		}
81
82
		// username part of SUL account
83
		// 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...
84 1
		if ($this->userSulExists()) {
85
			// using same error slot as name exists - it's the same sort of error, and we probably only want to show one.
86
			$errorList[ValidationError::NAME_EXISTS] = new ValidationError(ValidationError::NAME_EXISTS_SUL);
87
		}
88
89
		// username is numbers
90 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...
91
			$errorList[ValidationError::NAME_NUMONLY] = new ValidationError(ValidationError::NAME_NUMONLY);
92
		}
93
94
		// username can't contain #@/<>[]|{}
95 1
		if (preg_match("/[" . preg_quote("#@/<>[]|{}", "/") . "]/", $this->request->getName()) === 1) {
96
			$errorList[ValidationError::NAME_INVALIDCHAR] = new ValidationError(ValidationError::NAME_INVALIDCHAR);
97
		}
98
99
		// existing non-closed request for this name
100
		// 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...
101 1
		if ($this->nameRequestExists()) {
102
			$errorList[ValidationError::OPEN_REQUEST_NAME] = new ValidationError(ValidationError::OPEN_REQUEST_NAME);
103
		}
104
105
		// WARNINGS
106
		// name has to be sanitised
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 (false) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
109
			$errorList[ValidationError::NAME_SANITISED] = new ValidationError(ValidationError::NAME_SANITISED, false);
110
		}
111
112 1
		return $errorList;
113
	}
114
115
	/**
116
	 * Summary of validateEmail
117
	 * @return ValidationError[]
118
	 */
119
	public function validateEmail()
120
	{
121
		$errorList = array();
122
123
		// ERRORS
124
125
		// Email is banned
126
		$ban = $this->banHelper->emailIsBanned($this->request->getEmail());
127
		if ($ban != false) {
128
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
129
		}
130
131
		// email addresses must match
132
		if ($this->request->getEmail() != $this->emailConfirmation) {
133
			$errorList[ValidationError::EMAIL_MISMATCH] = new ValidationError(ValidationError::EMAIL_MISMATCH);
134
		}
135
136
		// email address must be validly formed
137 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...
138
			$errorList[ValidationError::EMAIL_EMPTY] = new ValidationError(ValidationError::EMAIL_EMPTY);
139
		}
140
141
		// email address must be validly formed
142
		if (!filter_var($this->request->getEmail(), FILTER_VALIDATE_EMAIL)) {
143 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...
144
				$errorList[ValidationError::EMAIL_INVALID] = new ValidationError(ValidationError::EMAIL_INVALID);
145
			}
146
		}
147
148
		// email address can't be wikimedia/wikipedia .com/org
149 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...
150
			$errorList[ValidationError::EMAIL_WIKIMEDIA] = new ValidationError(ValidationError::EMAIL_WIKIMEDIA);
151
		}
152
153
		// WARNINGS
154
155
		return $errorList;
156
	}
157
158
	/**
159
	 * Summary of validateOther
160
	 * @return ValidationError[]
161
	 */
162
	public function validateOther()
163
	{
164
		// @todo remove me!
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...
165
		/** @var $xffTrustProvider IXffTrustProvider */
166
		global $xffTrustProvider;
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...
167
168
		$errorList = array();
169
170
		// ERRORS
171
172
		// TOR nodes
173
		// 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...
174
		if (false) {
0 ignored issues
show
Bug introduced by
Avoid IF statements that are always true or false
Loading history...
175
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED_TOR);
176
		}
177
178
		// IP banned
179
		$trustedIp = $xffTrustProvider->getTrustedClientIp($this->request->getIp(), $this->request->getForwardedIp());
180
		$ban = $this->banHelper->ipIsBanned($trustedIp);
181
		if ($ban != false) {
182
			$errorList[ValidationError::BANNED] = new ValidationError(ValidationError::BANNED);
183
		}
184
185
		// WARNINGS
186
187
		// Antispoof check
188
		$this->checkAntiSpoof();
189
190
		// Blacklist check
191
		$this->checkTitleBlacklist();
192
193
		return $errorList;
194
	}
195
196
	private function checkAntiSpoof()
197
	{
198
		try {
199
			if (count($this->antiSpoofProvider->getSpoofs($this->request->getName())) > 0) {
200
				// If there were spoofs an Admin should handle the request.
201
				$this->request->setStatus("Flagged users");
202
			}
203
		}
204
		catch (Exception $ex) {
205
			// hrm.
206
			// 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...
207
		}
208
	}
209
210
	private function checkTitleBlacklist()
211
	{
212
		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...
213
		if ($enableTitleblacklist == 1) {
214
			$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...
215
216
			$data = unserialize($apiResult);
217
218
			$requestIsOk = $data['titleblacklist']['result'] == "ok";
219
220
			if (!$requestIsOk) {
221
				$this->request->setStatus("Flagged users");
222
			}
223
		}
224
	}
225
226 1
	private function userExists()
227
	{
228 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...
229
230 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...
231 1
		$ue = unserialize($userexist);
232 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...
233
			return true;
234
		}
235
236 1
		return false;
237
	}
238
239 1
	private function userSulExists()
240
	{
241 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...
242
243 1
		$reqname = str_replace("_", " ", $this->request->getName());
244 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...
245 1
		$ue = unserialize($userexist);
246 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...
247
			return true;
248
		}
249
250 1
		return false;
251
	}
252
253 1
	private function nameRequestExists()
254
	{
255 1
		$query = "SELECT COUNT(id) FROM request WHERE status != 'Closed' AND name = :name;";
256 1
		$statement = $this->database->prepare($query);
257 1
		$statement->execute(array(':name' => $this->request->getName()));
258
259 1
		if (!$statement) {
260
			return false;
261
		}
262
263 1
		return $statement->fetchColumn() > 0;
264
	}
265
}
266