Failed Conditions
Branch newinternal (104de7)
by Simon
09:33
created

RequestValidationHelper::validateOther()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 33
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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