Completed
Pull Request — newinternal (#285)
by Simon
06:42 queued 03:28
created

IdentificationVerifier::isIdentifiedOnWiki()   B

Complexity

Conditions 2
Paths 4

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2.0145

Importance

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

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
196
        }
197
198 1
        $page = @array_pop($response['query']['pages']);
199
200 1
        return @$page['links'][0]['title'] === "User:" . $onWikiName;
201
    }
202
}
203