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

IdentificationVerifier   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 165
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 33.33%

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 8
lcom 1
cbo 4
dl 0
loc 165
ccs 14
cts 42
cp 0.3333
rs 10
c 4
b 0
f 1

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A isUserIdentified() 0 16 3
A checkIdentificationCache() 0 21 1
A clearExpiredCacheEntries() 0 10 1
A cacheIdentificationStatus() 0 15 1
A isIdentifiedOnWiki() 0 16 1
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;
10
11
use PDO;
12
13
use Waca\Helpers\HttpHelper;
14
15
/**
16
 * Class IdentificationVerifier
17
 *
18
 * Handles automatically verifying if users are identified with the Wikimedia Foundation or not.  Intended to be used
19
 * as necessary by the User class when a user's "forceidentified" attribute is NULL.
20
 *
21
 * @package  Waca
22
 * @author   Andrew "FastLizard4" Adams
23
 * @category Security-Critical
24
 */
25
class IdentificationVerifier
26
{
27
	/**
28
	 * This field is an array of parameters, in key => value format, that should be appended to the Meta Wikimedia
29
	 * Web Service Endpoint URL to query if a user is listed on the Identification Noticeboard.  Note that URL encoding
30
	 * of these values is *not* necessary; this is done automatically.
31
	 *
32
	 * @var string[]
33
	 * @category Security-Critical
34
	 */
35
	private static $apiQueryParameters = array(
36
		'action'   => 'query',
37
		'format'   => 'json',
38
		'prop'     => 'links',
39
		'titles'   => 'Access to nonpublic information policy/Noticeboard',
40
		// Username of the user to be checked, with User: prefix, goes here!  Set in isIdentifiedOnWiki()
41
		'pltitles' => '',
42
	);
43
	/** @var HttpHelper */
44
	private $httpHelper;
45
	/** @var SiteConfiguration */
46
	private $siteConfiguration;
47
	/** @var PdoDatabase */
48
	private $dbObject;
49
50
	/**
51
	 * IdentificationVerifier constructor.
52
	 *
53
	 * @param HttpHelper        $httpHelper
54
	 * @param SiteConfiguration $siteConfiguration
55
	 * @param PdoDatabase       $dbObject
56
	 */
57 1
	public function __construct(HttpHelper $httpHelper, SiteConfiguration $siteConfiguration, PdoDatabase $dbObject)
58
	{
59 1
		$this->httpHelper = $httpHelper;
60 1
		$this->siteConfiguration = $siteConfiguration;
61 1
		$this->dbObject = $dbObject;
62 1
	}
63
64
	/**
65
	 * Checks if the given user is identified to the Wikimedia Foundation.
66
	 *
67
	 * @param string $onWikiName The Wikipedia username of the user
68
	 *
69
	 * @return bool
70
	 * @category Security-Critical
71
	 */
72
	public function isUserIdentified($onWikiName)
73
	{
74
		if ($this->checkIdentificationCache($onWikiName)) {
75
			return true;
76
		}
77
		else {
78
			if ($this->isIdentifiedOnWiki($onWikiName)) {
79
				$this->cacheIdentificationStatus($onWikiName);
80
81
				return true;
82
			}
83
			else {
84
				return false;
85
			}
86
		}
87
	}
88
89
	/**
90
	 * Checks if the given user has a valid entry in the idcache table.
91
	 *
92
	 * @param string $onWikiName The Wikipedia username of the user
93
	 *
94
	 * @return bool
95
	 * @category Security-Critical
96
	 */
97
	private function checkIdentificationCache($onWikiName)
98
	{
99
		$interval = $this->siteConfiguration->getIdentificationCacheExpiry();
100
101
		$query = <<<SQL
102
			SELECT COUNT(`id`)
103
			FROM `idcache`
104
			WHERE `onwikiusername` = :onwikiname
105
				AND DATE_ADD(`checktime`, INTERVAL {$interval}) >= NOW();
106
SQL;
107
		$stmt = $this->dbObject->prepare($query);
108
		$stmt->bindValue(':onwikiname', $onWikiName, PDO::PARAM_STR);
109
		$stmt->execute();
110
111
		// Guaranteed by the query to only return a single row with a single column
112
		$results = $stmt->fetch(PDO::FETCH_NUM);
113
114
		// I don't expect this to ever be a value other than 0 or 1 since the `onwikiusername` column is declared as a
115
		// unique key - but meh.
116
		return $results[0] != 0;
117
	}
118
119
	/**
120
	 * Does pretty much exactly what it says on the label - this method will clear all expired idcache entries from the
121
	 * idcache table.  Meant to be called periodically by a maintenance script.
122
	 *
123
	 * @param SiteConfiguration $siteConfiguration
124
	 * @param PdoDatabase       $dbObject
125
	 *
126
	 * @return void
127
	 */
128
	public static function clearExpiredCacheEntries(SiteConfiguration $siteConfiguration, PdoDatabase $dbObject)
129
	{
130
		$interval = $siteConfiguration->getIdentificationCacheExpiry();
131
132
		$query = <<<SQL
133
			DELETE FROM `idcache`
134
			WHERE DATE_ADD(`checktime`, INTERVAL {$interval}) < NOW();
135
SQL;
136
		$dbObject->prepare($query)->execute();
137
	}
138
139
	/**
140
	 * This method will add an entry to the idcache that the given Wikipedia user has been verified as identified.  This
141
	 * is so we don't have to hit the API every single time we check.  The cache entry is valid for as long as specified
142
	 * in the ACC configuration (validity enforced by checkIdentificationCache() and clearExpiredCacheEntries()).
143
	 *
144
	 * @param string $onWikiName The Wikipedia username of the user
145
	 *
146
	 * @return void
147
	 * @category Security-Critical
148
	 */
149
	private function cacheIdentificationStatus($onWikiName)
150
	{
151
		$query = <<<SQL
152
			INSERT INTO `idcache`
153
				(`onwikiusername`)
154
			VALUES
155
				(:onwikiname)
156
			ON DUPLICATE KEY UPDATE
157
				`onwikiusername` = VALUES(`onwikiusername`),
158
				`checktime` = CURRENT_TIMESTAMP;
159
SQL;
160
		$stmt = $this->dbObject->prepare($query);
161
		$stmt->bindValue(':onwikiname', $onWikiName, PDO::PARAM_STR);
162
		$stmt->execute();
163
	}
164
165
	/**
166
	 * Queries the Wikimedia API to determine if the specified user is listed on the identification noticeboard.
167
	 *
168
	 * @param string $onWikiName The Wikipedia username of the user
169
	 *
170
	 * @return bool
171
	 * @category Security-Critical
172
	 */
173 1
	private function isIdentifiedOnWiki($onWikiName)
174
	{
175 1
		$strings = new StringFunctions();
176
177
		// First character of Wikipedia usernames is always capitalized.
178 1
		$onWikiName = $strings->ucfirst($onWikiName);
179
180 1
		$parameters = self::$apiQueryParameters;
181 1
		$parameters['pltitles'] = "User:" . $onWikiName;
182 1
		$response = $this->httpHelper->get($this->siteConfiguration->getMetaWikimediaWebServiceEndpoint(), $parameters);
183 1
		$response = json_decode($response, true);
184
185 1
		$page = @array_pop($response['query']['pages']);
186
187 1
		return @$page['links'][0]['title'] === "User:" . $onWikiName;
188
	}
189
}
190