Passed
Push — master ( 3e5dae...8d4e4c )
by Simon
12:37
created
includes/Helpers/HttpHelper.php 1 patch
Indentation   +108 added lines, -108 removed lines patch added patch discarded remove patch
@@ -13,112 +13,112 @@
 block discarded – undo
13 13
 
14 14
 class HttpHelper
15 15
 {
16
-    private $curlHandle;
17
-
18
-    /**
19
-     * HttpHelper constructor.
20
-     *
21
-     * @param SiteConfiguration $siteConfiguration
22
-     * @param string            $cookieJar
23
-     */
24
-    public function __construct($siteConfiguration, $cookieJar = null)
25
-    {
26
-        $this->curlHandle = curl_init();
27
-
28
-        curl_setopt($this->curlHandle, CURLOPT_RETURNTRANSFER, true);
29
-        curl_setopt($this->curlHandle, CURLOPT_USERAGENT, $siteConfiguration->getUserAgent());
30
-        curl_setopt($this->curlHandle, CURLOPT_FAILONERROR, true);
31
-
32
-        if ($siteConfiguration->getCurlDisableVerifyPeer()) {
33
-            curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYPEER, false);
34
-        }
35
-
36
-        if ($cookieJar !== null) {
37
-            curl_setopt($this->curlHandle, CURLOPT_COOKIEFILE, $cookieJar);
38
-            curl_setopt($this->curlHandle, CURLOPT_COOKIEJAR, $cookieJar);
39
-        }
40
-    }
41
-
42
-    public function __destruct()
43
-    {
44
-        curl_close($this->curlHandle);
45
-    }
46
-
47
-    /**
48
-     * Fetches the content of a URL, with an optional parameter set.
49
-     *
50
-     * @param string     $url        The URL to fetch.
51
-     * @param null|array $parameters Key/value pair of GET parameters to add to the request.
52
-     *                               Null lets you handle it yourself.
53
-     *
54
-     * @param array      $headers
55
-     * @param int        $timeout Timeout in ms
56
-     *
57
-     * @return string
58
-     * @throws CurlException
59
-     */
60
-    public function get($url, $parameters = null, $headers = array(), $timeout = 300000)
61
-    {
62
-        if ($parameters !== null && is_array($parameters)) {
63
-            $getString = '?' . http_build_query($parameters);
64
-            $url .= $getString;
65
-        }
66
-
67
-        curl_setopt($this->curlHandle, CURLOPT_URL, $url);
68
-
69
-        // Make sure we're doing a GET
70
-        curl_setopt($this->curlHandle, CURLOPT_POST, false);
71
-
72
-        curl_setopt($this->curlHandle, CURLOPT_HTTPHEADER, $headers);
73
-
74
-        curl_setopt($this->curlHandle, CURLOPT_CONNECTTIMEOUT_MS, $timeout);
75
-        curl_setopt($this->curlHandle, CURLOPT_TIMEOUT_MS, $timeout);
76
-
77
-        $result = curl_exec($this->curlHandle);
78
-
79
-        if ($result === false) {
80
-            $error = curl_error($this->curlHandle);
81
-            throw new CurlException('Remote request failed with error ' . $error);
82
-        }
83
-
84
-        return $result;
85
-    }
86
-
87
-    /**
88
-     * Posts data to a URL
89
-     *
90
-     * @param string $url        The URL to fetch.
91
-     * @param array  $parameters Key/value pair of POST parameters to add to the request.
92
-     * @param array  $headers
93
-     *
94
-     * @return string
95
-     * @throws CurlException
96
-     */
97
-    public function post($url, $parameters, $headers = array())
98
-    {
99
-        curl_setopt($this->curlHandle, CURLOPT_URL, $url);
100
-
101
-        // Make sure we're doing a POST
102
-        curl_setopt($this->curlHandle, CURLOPT_POST, true);
103
-        curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, http_build_query($parameters));
104
-
105
-        curl_setopt($this->curlHandle, CURLOPT_HTTPHEADER, $headers);
106
-
107
-        $result = curl_exec($this->curlHandle);
108
-
109
-        if ($result === false) {
110
-            $error = curl_error($this->curlHandle);
111
-            throw new CurlException('Remote request failed with error ' . $error);
112
-        }
113
-
114
-        return $result;
115
-    }
116
-
117
-    /**
118
-     * @return string
119
-     */
120
-    public function getError()
121
-    {
122
-        return curl_error($this->curlHandle);
123
-    }
16
+	private $curlHandle;
17
+
18
+	/**
19
+	 * HttpHelper constructor.
20
+	 *
21
+	 * @param SiteConfiguration $siteConfiguration
22
+	 * @param string            $cookieJar
23
+	 */
24
+	public function __construct($siteConfiguration, $cookieJar = null)
25
+	{
26
+		$this->curlHandle = curl_init();
27
+
28
+		curl_setopt($this->curlHandle, CURLOPT_RETURNTRANSFER, true);
29
+		curl_setopt($this->curlHandle, CURLOPT_USERAGENT, $siteConfiguration->getUserAgent());
30
+		curl_setopt($this->curlHandle, CURLOPT_FAILONERROR, true);
31
+
32
+		if ($siteConfiguration->getCurlDisableVerifyPeer()) {
33
+			curl_setopt($this->curlHandle, CURLOPT_SSL_VERIFYPEER, false);
34
+		}
35
+
36
+		if ($cookieJar !== null) {
37
+			curl_setopt($this->curlHandle, CURLOPT_COOKIEFILE, $cookieJar);
38
+			curl_setopt($this->curlHandle, CURLOPT_COOKIEJAR, $cookieJar);
39
+		}
40
+	}
41
+
42
+	public function __destruct()
43
+	{
44
+		curl_close($this->curlHandle);
45
+	}
46
+
47
+	/**
48
+	 * Fetches the content of a URL, with an optional parameter set.
49
+	 *
50
+	 * @param string     $url        The URL to fetch.
51
+	 * @param null|array $parameters Key/value pair of GET parameters to add to the request.
52
+	 *                               Null lets you handle it yourself.
53
+	 *
54
+	 * @param array      $headers
55
+	 * @param int        $timeout Timeout in ms
56
+	 *
57
+	 * @return string
58
+	 * @throws CurlException
59
+	 */
60
+	public function get($url, $parameters = null, $headers = array(), $timeout = 300000)
61
+	{
62
+		if ($parameters !== null && is_array($parameters)) {
63
+			$getString = '?' . http_build_query($parameters);
64
+			$url .= $getString;
65
+		}
66
+
67
+		curl_setopt($this->curlHandle, CURLOPT_URL, $url);
68
+
69
+		// Make sure we're doing a GET
70
+		curl_setopt($this->curlHandle, CURLOPT_POST, false);
71
+
72
+		curl_setopt($this->curlHandle, CURLOPT_HTTPHEADER, $headers);
73
+
74
+		curl_setopt($this->curlHandle, CURLOPT_CONNECTTIMEOUT_MS, $timeout);
75
+		curl_setopt($this->curlHandle, CURLOPT_TIMEOUT_MS, $timeout);
76
+
77
+		$result = curl_exec($this->curlHandle);
78
+
79
+		if ($result === false) {
80
+			$error = curl_error($this->curlHandle);
81
+			throw new CurlException('Remote request failed with error ' . $error);
82
+		}
83
+
84
+		return $result;
85
+	}
86
+
87
+	/**
88
+	 * Posts data to a URL
89
+	 *
90
+	 * @param string $url        The URL to fetch.
91
+	 * @param array  $parameters Key/value pair of POST parameters to add to the request.
92
+	 * @param array  $headers
93
+	 *
94
+	 * @return string
95
+	 * @throws CurlException
96
+	 */
97
+	public function post($url, $parameters, $headers = array())
98
+	{
99
+		curl_setopt($this->curlHandle, CURLOPT_URL, $url);
100
+
101
+		// Make sure we're doing a POST
102
+		curl_setopt($this->curlHandle, CURLOPT_POST, true);
103
+		curl_setopt($this->curlHandle, CURLOPT_POSTFIELDS, http_build_query($parameters));
104
+
105
+		curl_setopt($this->curlHandle, CURLOPT_HTTPHEADER, $headers);
106
+
107
+		$result = curl_exec($this->curlHandle);
108
+
109
+		if ($result === false) {
110
+			$error = curl_error($this->curlHandle);
111
+			throw new CurlException('Remote request failed with error ' . $error);
112
+		}
113
+
114
+		return $result;
115
+	}
116
+
117
+	/**
118
+	 * @return string
119
+	 */
120
+	public function getError()
121
+	{
122
+		return curl_error($this->curlHandle);
123
+	}
124 124
 }
Please login to merge, or discard this patch.
includes/Helpers/BanHelper.php 1 patch
Indentation   +147 added lines, -147 removed lines patch added patch discarded remove patch
@@ -19,60 +19,60 @@  discard block
 block discarded – undo
19 19
 
20 20
 class BanHelper implements IBanHelper
21 21
 {
22
-    /** @var PdoDatabase */
23
-    private $database;
24
-    /** @var IXffTrustProvider */
25
-    private $xffTrustProvider;
26
-    /** @var Ban[][] */
27
-    private $banCache = [];
28
-    /**
29
-     * @var null|SecurityManager
30
-     */
31
-    private $securityManager;
32
-
33
-    public function __construct(
34
-        PdoDatabase $database,
35
-        IXffTrustProvider $xffTrustProvider,
36
-        ?SecurityManager $securityManager
37
-    ) {
38
-        $this->database = $database;
39
-        $this->xffTrustProvider = $xffTrustProvider;
40
-        $this->securityManager = $securityManager;
41
-    }
42
-
43
-    public function isBlockBanned(Request $request): bool
44
-    {
45
-        if (!isset($this->banCache[$request->getId()])) {
46
-            $this->banCache[$request->getId()] = $this->getBansForRequestFromDatabase($request);
47
-        }
48
-
49
-        foreach ($this->banCache[$request->getId()] as $ban) {
50
-            if ($ban->getAction() === Ban::ACTION_BLOCK) {
51
-                return true;
52
-            }
53
-        }
54
-
55
-        return false;
56
-    }
57
-
58
-    /**
59
-     * @param Request $request
60
-     *
61
-     * @return Ban[]
62
-     */
63
-    public function getBans(Request $request): array
64
-    {
65
-        if (!isset($this->banCache[$request->getId()])) {
66
-            $this->banCache[$request->getId()] = $this->getBansForRequestFromDatabase($request);
67
-        }
68
-
69
-        return $this->banCache[$request->getId()];
70
-    }
71
-
72
-    public function getBansByTarget(?string $name, ?string $email, ?string $ip, ?int $mask, ?string $useragent)
73
-    {
74
-        /** @noinspection SqlConstantCondition */
75
-        $query = <<<SQL
22
+	/** @var PdoDatabase */
23
+	private $database;
24
+	/** @var IXffTrustProvider */
25
+	private $xffTrustProvider;
26
+	/** @var Ban[][] */
27
+	private $banCache = [];
28
+	/**
29
+	 * @var null|SecurityManager
30
+	 */
31
+	private $securityManager;
32
+
33
+	public function __construct(
34
+		PdoDatabase $database,
35
+		IXffTrustProvider $xffTrustProvider,
36
+		?SecurityManager $securityManager
37
+	) {
38
+		$this->database = $database;
39
+		$this->xffTrustProvider = $xffTrustProvider;
40
+		$this->securityManager = $securityManager;
41
+	}
42
+
43
+	public function isBlockBanned(Request $request): bool
44
+	{
45
+		if (!isset($this->banCache[$request->getId()])) {
46
+			$this->banCache[$request->getId()] = $this->getBansForRequestFromDatabase($request);
47
+		}
48
+
49
+		foreach ($this->banCache[$request->getId()] as $ban) {
50
+			if ($ban->getAction() === Ban::ACTION_BLOCK) {
51
+				return true;
52
+			}
53
+		}
54
+
55
+		return false;
56
+	}
57
+
58
+	/**
59
+	 * @param Request $request
60
+	 *
61
+	 * @return Ban[]
62
+	 */
63
+	public function getBans(Request $request): array
64
+	{
65
+		if (!isset($this->banCache[$request->getId()])) {
66
+			$this->banCache[$request->getId()] = $this->getBansForRequestFromDatabase($request);
67
+		}
68
+
69
+		return $this->banCache[$request->getId()];
70
+	}
71
+
72
+	public function getBansByTarget(?string $name, ?string $email, ?string $ip, ?int $mask, ?string $useragent)
73
+	{
74
+		/** @noinspection SqlConstantCondition */
75
+		$query = <<<SQL
76 76
 SELECT * FROM ban 
77 77
 WHERE 1 = 1
78 78
   AND ((name is null and :nname is null) OR name = :name)
@@ -84,76 +84,76 @@  discard block
 block discarded – undo
84 84
   AND active = 1;
85 85
 SQL;
86 86
 
87
-        $statement = $this->database->prepare($query);
88
-        $statement->execute([
89
-            ':name'       => $name,
90
-            ':nname'      => $name,
91
-            ':email'      => $email,
92
-            ':nemail'     => $email,
93
-            ':ip'         => $ip,
94
-            ':nip'        => $ip,
95
-            ':ipmask'     => $mask,
96
-            ':nipmask'    => $mask,
97
-            ':useragent'  => $useragent,
98
-            ':nuseragent' => $useragent,
99
-        ]);
100
-
101
-        $result = array();
102
-
103
-        /** @var Ban $v */
104
-        foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
105
-            $v->setDatabase($this->database);
106
-            $result[] = $v;
107
-        }
108
-
109
-        return $result;
110
-    }
111
-
112
-    public function isActive(Ban $ban): bool
113
-    {
114
-        if (!$ban->isActive()) {
115
-            return false;
116
-        }
117
-
118
-        if ($ban->getDuration() !== null && $ban->getDuration() < time()) {
119
-            return false;
120
-        }
121
-
122
-        return true;
123
-    }
124
-
125
-    public function canUnban(Ban $ban): bool
126
-    {
127
-        if ($this->securityManager === null) {
128
-            return false;
129
-        }
130
-
131
-        if (!$this->isActive($ban)) {
132
-            return false;
133
-        }
134
-
135
-        $user = User::getCurrent($this->database);
136
-
137
-        $allowed = true;
138
-        $allowed = $allowed && ($ban->getName() === null || $this->securityManager->allows('BanType', 'name', $user) === SecurityManager::ALLOWED);
139
-        $allowed = $allowed && ($ban->getEmail() === null || $this->securityManager->allows('BanType', 'email', $user) === SecurityManager::ALLOWED);
140
-        $allowed = $allowed && ($ban->getIp() === null || $this->securityManager->allows('BanType', 'ip', $user) === SecurityManager::ALLOWED);
141
-        $allowed = $allowed && ($ban->getUseragent() === null || $this->securityManager->allows('BanType', 'useragent', $user) === SecurityManager::ALLOWED);
142
-
143
-        $allowed = $allowed && $this->securityManager->allows('BanVisibility', $ban->getVisibility(), $user) === SecurityManager::ALLOWED;
144
-
145
-        return $allowed;
146
-    }
147
-
148
-    /**
149
-     * @param Request $request
150
-     *
151
-     * @return Ban[]
152
-     */
153
-    private function getBansForRequestFromDatabase(Request $request): array
154
-    {
155
-        /** @noinspection SqlConstantCondition - included for clarity of code */
156
-        $query = <<<SQL
87
+		$statement = $this->database->prepare($query);
88
+		$statement->execute([
89
+			':name'       => $name,
90
+			':nname'      => $name,
91
+			':email'      => $email,
92
+			':nemail'     => $email,
93
+			':ip'         => $ip,
94
+			':nip'        => $ip,
95
+			':ipmask'     => $mask,
96
+			':nipmask'    => $mask,
97
+			':useragent'  => $useragent,
98
+			':nuseragent' => $useragent,
99
+		]);
100
+
101
+		$result = array();
102
+
103
+		/** @var Ban $v */
104
+		foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
105
+			$v->setDatabase($this->database);
106
+			$result[] = $v;
107
+		}
108
+
109
+		return $result;
110
+	}
111
+
112
+	public function isActive(Ban $ban): bool
113
+	{
114
+		if (!$ban->isActive()) {
115
+			return false;
116
+		}
117
+
118
+		if ($ban->getDuration() !== null && $ban->getDuration() < time()) {
119
+			return false;
120
+		}
121
+
122
+		return true;
123
+	}
124
+
125
+	public function canUnban(Ban $ban): bool
126
+	{
127
+		if ($this->securityManager === null) {
128
+			return false;
129
+		}
130
+
131
+		if (!$this->isActive($ban)) {
132
+			return false;
133
+		}
134
+
135
+		$user = User::getCurrent($this->database);
136
+
137
+		$allowed = true;
138
+		$allowed = $allowed && ($ban->getName() === null || $this->securityManager->allows('BanType', 'name', $user) === SecurityManager::ALLOWED);
139
+		$allowed = $allowed && ($ban->getEmail() === null || $this->securityManager->allows('BanType', 'email', $user) === SecurityManager::ALLOWED);
140
+		$allowed = $allowed && ($ban->getIp() === null || $this->securityManager->allows('BanType', 'ip', $user) === SecurityManager::ALLOWED);
141
+		$allowed = $allowed && ($ban->getUseragent() === null || $this->securityManager->allows('BanType', 'useragent', $user) === SecurityManager::ALLOWED);
142
+
143
+		$allowed = $allowed && $this->securityManager->allows('BanVisibility', $ban->getVisibility(), $user) === SecurityManager::ALLOWED;
144
+
145
+		return $allowed;
146
+	}
147
+
148
+	/**
149
+	 * @param Request $request
150
+	 *
151
+	 * @return Ban[]
152
+	 */
153
+	private function getBansForRequestFromDatabase(Request $request): array
154
+	{
155
+		/** @noinspection SqlConstantCondition - included for clarity of code */
156
+		$query = <<<SQL
157 157
 select b.* from ban b
158 158
 left join netmask n on 1 = 1
159 159
     and n.cidr = b.ipmask
@@ -174,27 +174,27 @@  discard block
 block discarded – undo
174 174
     and (duration > UNIX_TIMESTAMP() or duration is null)
175 175
 SQL;
176 176
 
177
-        $statement = $this->database->prepare($query);
178
-        $trustedIp = $this->xffTrustProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
179
-
180
-        $statement->execute([
181
-            ':name'      => $request->getName(),
182
-            ':email'     => $request->getEmail(),
183
-            ':useragent' => $request->getUserAgent(),
184
-            ':ip4'       => $trustedIp,
185
-            ':ip6h'      => $trustedIp,
186
-            ':ip6l'      => $trustedIp,
187
-        ]);
188
-
189
-        /** @var Ban[] $result */
190
-        $result = [];
191
-
192
-        /** @var Ban $v */
193
-        foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
194
-            $v->setDatabase($this->database);
195
-            $result[] = $v;
196
-        }
197
-
198
-        return $result;
199
-    }
177
+		$statement = $this->database->prepare($query);
178
+		$trustedIp = $this->xffTrustProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp());
179
+
180
+		$statement->execute([
181
+			':name'      => $request->getName(),
182
+			':email'     => $request->getEmail(),
183
+			':useragent' => $request->getUserAgent(),
184
+			':ip4'       => $trustedIp,
185
+			':ip6h'      => $trustedIp,
186
+			':ip6l'      => $trustedIp,
187
+		]);
188
+
189
+		/** @var Ban[] $result */
190
+		$result = [];
191
+
192
+		/** @var Ban $v */
193
+		foreach ($statement->fetchAll(PDO::FETCH_CLASS, Ban::class) as $v) {
194
+			$v->setDatabase($this->database);
195
+			$result[] = $v;
196
+		}
197
+
198
+		return $result;
199
+	}
200 200
 }
Please login to merge, or discard this patch.
includes/Helpers/TypeAheadHelper.php 1 patch
Indentation   +31 added lines, -31 removed lines patch added patch discarded remove patch
@@ -12,21 +12,21 @@  discard block
 block discarded – undo
12 12
 
13 13
 class TypeAheadHelper implements ITypeAheadHelper
14 14
 {
15
-    private $definedClasses = array();
15
+	private $definedClasses = array();
16 16
 
17
-    /**
18
-     * @param string   $class     CSS class to apply this typeahead to.
19
-     * @param callable $generator Generator function taking no arguments to return an array of strings.
20
-     */
21
-    public function defineTypeAheadSource($class, callable $generator)
22
-    {
23
-        $dataList = '';
24
-        foreach ($generator() as $dataItem) {
25
-            $dataList .= '"' . htmlentities($dataItem) . '", ';
26
-        }
27
-        $dataList = "[" . rtrim($dataList, ", ") . "]";
17
+	/**
18
+	 * @param string   $class     CSS class to apply this typeahead to.
19
+	 * @param callable $generator Generator function taking no arguments to return an array of strings.
20
+	 */
21
+	public function defineTypeAheadSource($class, callable $generator)
22
+	{
23
+		$dataList = '';
24
+		foreach ($generator() as $dataItem) {
25
+			$dataList .= '"' . htmlentities($dataItem) . '", ';
26
+		}
27
+		$dataList = "[" . rtrim($dataList, ", ") . "]";
28 28
 
29
-        $script = <<<JS
29
+		$script = <<<JS
30 30
 
31 31
 $('.{$class}').typeahead({
32 32
         hint: true,
@@ -39,32 +39,32 @@  discard block
 block discarded – undo
39 39
 })
40 40
 ;
41 41
 JS;
42
-        $this->definedClasses[$class] = $script;
43
-    }
42
+		$this->definedClasses[$class] = $script;
43
+	}
44 44
 
45
-    /**
46
-     * @return string HTML fragment containing a JS block for typeaheads.
47
-     */
48
-    public function getTypeAheadScriptBlock()
49
-    {
50
-        $jsBlocks = '';
45
+	/**
46
+	 * @return string HTML fragment containing a JS block for typeaheads.
47
+	 */
48
+	public function getTypeAheadScriptBlock()
49
+	{
50
+		$jsBlocks = '';
51 51
 
52
-        if (count($this->definedClasses) === 0) {
53
-            return '';
54
-        }
52
+		if (count($this->definedClasses) === 0) {
53
+			return '';
54
+		}
55 55
 
56
-        foreach ($this->definedClasses as $class => $js) {
57
-            $jsBlocks = $js . "\r\n\r\n";
58
-        }
56
+		foreach ($this->definedClasses as $class => $js) {
57
+			$jsBlocks = $js . "\r\n\r\n";
58
+		}
59 59
 
60
-        $data = <<<HTML
60
+		$data = <<<HTML
61 61
 <script type="text/javascript">
62 62
 	{$jsBlocks}
63 63
 </script>
64 64
 HTML;
65 65
 
66
-        $this->definedClasses = array();
66
+		$this->definedClasses = array();
67 67
 
68
-        return $data;
69
-    }
68
+		return $data;
69
+	}
70 70
 }
Please login to merge, or discard this patch.
includes/Helpers/Interfaces/IBlacklistHelper.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -10,12 +10,12 @@
 block discarded – undo
10 10
 
11 11
 interface IBlacklistHelper
12 12
 {
13
-    /**
14
-     * Returns a value indicating whether the provided username is blacklisted by the on-wiki title blacklist
15
-     *
16
-     * @param string $username
17
-     *
18
-     * @return bool
19
-     */
20
-    public function isBlacklisted($username);
13
+	/**
14
+	 * Returns a value indicating whether the provided username is blacklisted by the on-wiki title blacklist
15
+	 *
16
+	 * @param string $username
17
+	 *
18
+	 * @return bool
19
+	 */
20
+	public function isBlacklisted($username);
21 21
 }
22 22
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Helpers/Interfaces/IOAuthProtocolHelper.php 1 patch
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -15,53 +15,53 @@
 block discarded – undo
15 15
 
16 16
 interface IOAuthProtocolHelper
17 17
 {
18
-    /**
19
-     * @return stdClass
20
-     *
21
-     * @throws Exception
22
-     * @throws CurlException
23
-     */
24
-    public function getRequestToken();
18
+	/**
19
+	 * @return stdClass
20
+	 *
21
+	 * @throws Exception
22
+	 * @throws CurlException
23
+	 */
24
+	public function getRequestToken();
25 25
 
26
-    /**
27
-     * @param string $requestToken
28
-     *
29
-     * @return string
30
-     */
31
-    public function getAuthoriseUrl($requestToken);
26
+	/**
27
+	 * @param string $requestToken
28
+	 *
29
+	 * @return string
30
+	 */
31
+	public function getAuthoriseUrl($requestToken);
32 32
 
33
-    /**
34
-     * @param string $oauthRequestToken
35
-     * @param string $oauthRequestSecret
36
-     * @param string $oauthVerifier
37
-     *
38
-     * @return stdClass
39
-     * @throws CurlException
40
-     * @throws Exception
41
-     */
42
-    public function callbackCompleted($oauthRequestToken, $oauthRequestSecret, $oauthVerifier);
33
+	/**
34
+	 * @param string $oauthRequestToken
35
+	 * @param string $oauthRequestSecret
36
+	 * @param string $oauthVerifier
37
+	 *
38
+	 * @return stdClass
39
+	 * @throws CurlException
40
+	 * @throws Exception
41
+	 */
42
+	public function callbackCompleted($oauthRequestToken, $oauthRequestSecret, $oauthVerifier);
43 43
 
44
-    /**
45
-     * @param string $oauthAccessToken
46
-     * @param string $oauthAccessSecret
47
-     *
48
-     * @return stdClass
49
-     * @throws CurlException
50
-     * @throws Exception
51
-     * @throws \MediaWiki\OAuthClient\Exception
52
-     */
53
-    public function getIdentityTicket($oauthAccessToken, $oauthAccessSecret);
44
+	/**
45
+	 * @param string $oauthAccessToken
46
+	 * @param string $oauthAccessSecret
47
+	 *
48
+	 * @return stdClass
49
+	 * @throws CurlException
50
+	 * @throws Exception
51
+	 * @throws \MediaWiki\OAuthClient\Exception
52
+	 */
53
+	public function getIdentityTicket($oauthAccessToken, $oauthAccessSecret);
54 54
 
55
-    /**
56
-     * @param array  $apiParams    array of parameters to send to the API
57
-     * @param string $accessToken  user's access token
58
-     * @param string $accessSecret user's secret
59
-     * @param string $method       HTTP method
60
-     *
61
-     * @return stdClass
62
-     * @throws ApplicationLogicException
63
-     * @throws CurlException
64
-     * @throws Exception
65
-     */
66
-    public function apiCall($apiParams, $accessToken, $accessSecret, $method = 'GET');
55
+	/**
56
+	 * @param array  $apiParams    array of parameters to send to the API
57
+	 * @param string $accessToken  user's access token
58
+	 * @param string $accessSecret user's secret
59
+	 * @param string $method       HTTP method
60
+	 *
61
+	 * @return stdClass
62
+	 * @throws ApplicationLogicException
63
+	 * @throws CurlException
64
+	 * @throws Exception
65
+	 */
66
+	public function apiCall($apiParams, $accessToken, $accessSecret, $method = 'GET');
67 67
 }
68 68
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Helpers/Interfaces/IMediaWikiClient.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -10,5 +10,5 @@
 block discarded – undo
10 10
 
11 11
 interface IMediaWikiClient
12 12
 {
13
-    function doApiCall($params, $method);
13
+	function doApiCall($params, $method);
14 14
 }
15 15
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Helpers/Interfaces/ITypeAheadHelper.php 1 patch
Indentation   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -10,16 +10,16 @@
 block discarded – undo
10 10
 
11 11
 interface ITypeAheadHelper
12 12
 {
13
-    /**
14
-     * @param string   $class     CSS class to apply this typeahead to.
15
-     * @param callable $generator Generator function taking no arguments to return an array of strings.
16
-     *
17
-     * @return void
18
-     */
19
-    public function defineTypeAheadSource($class, callable $generator);
13
+	/**
14
+	 * @param string   $class     CSS class to apply this typeahead to.
15
+	 * @param callable $generator Generator function taking no arguments to return an array of strings.
16
+	 *
17
+	 * @return void
18
+	 */
19
+	public function defineTypeAheadSource($class, callable $generator);
20 20
 
21
-    /**
22
-     * @return string HTML fragment containing a JS block for typeaheads.
23
-     */
24
-    public function getTypeAheadScriptBlock();
21
+	/**
22
+	 * @return string HTML fragment containing a JS block for typeaheads.
23
+	 */
24
+	public function getTypeAheadScriptBlock();
25 25
 }
26 26
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Helpers/Interfaces/IBanHelper.php 1 patch
Indentation   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -13,21 +13,21 @@
 block discarded – undo
13 13
 
14 14
 interface IBanHelper
15 15
 {
16
-    /**
17
-     * @param Request $request
18
-     *
19
-     * @return bool
20
-     */
21
-    public function isBlockBanned(Request $request): bool;
16
+	/**
17
+	 * @param Request $request
18
+	 *
19
+	 * @return bool
20
+	 */
21
+	public function isBlockBanned(Request $request): bool;
22 22
 
23
-    /**
24
-     * @param Request $request
25
-     *
26
-     * @return Ban[]
27
-     */
28
-    public function getBans(Request $request): array;
23
+	/**
24
+	 * @param Request $request
25
+	 *
26
+	 * @return Ban[]
27
+	 */
28
+	public function getBans(Request $request): array;
29 29
 
30
-    public function canUnban(Ban $ban): bool;
30
+	public function canUnban(Ban $ban): bool;
31 31
 
32
-    public function isActive(Ban $ban): bool;
32
+	public function isActive(Ban $ban): bool;
33 33
 }
Please login to merge, or discard this patch.
includes/Helpers/OAuthUserHelper.php 1 patch
Indentation   +452 added lines, -452 removed lines patch added patch discarded remove patch
@@ -25,460 +25,460 @@
 block discarded – undo
25 25
 
26 26
 class OAuthUserHelper implements IMediaWikiClient
27 27
 {
28
-    const TOKEN_REQUEST = 'request';
29
-    const TOKEN_ACCESS = 'access';
30
-    /** @var PDOStatement */
31
-    private static $tokenCountStatement = null;
32
-    /** @var PDOStatement */
33
-    private $getTokenStatement;
34
-    /**
35
-     * @var User
36
-     */
37
-    private $user;
38
-    /**
39
-     * @var PdoDatabase
40
-     */
41
-    private $database;
42
-    /**
43
-     * @var IOAuthProtocolHelper
44
-     */
45
-    private $oauthProtocolHelper;
46
-    /**
47
-     * @var bool|null Is the user linked to OAuth
48
-     */
49
-    private $linked;
50
-    private $partiallyLinked;
51
-    /** @var OAuthToken */
52
-    private $accessToken;
53
-    /** @var bool */
54
-    private $accessTokenLoaded = false;
55
-    /**
56
-     * @var OAuthIdentity
57
-     */
58
-    private $identity = null;
59
-    /**
60
-     * @var bool
61
-     */
62
-    private $identityLoaded = false;
63
-    /**
64
-     * @var SiteConfiguration
65
-     */
66
-    private $siteConfiguration;
67
-
68
-    private $legacyTokens;
69
-
70
-    #region Static methods
71
-
72
-    public static function findUserByRequestToken($requestToken, PdoDatabase $database)
73
-    {
74
-        $statement = $database->prepare(<<<'SQL'
28
+	const TOKEN_REQUEST = 'request';
29
+	const TOKEN_ACCESS = 'access';
30
+	/** @var PDOStatement */
31
+	private static $tokenCountStatement = null;
32
+	/** @var PDOStatement */
33
+	private $getTokenStatement;
34
+	/**
35
+	 * @var User
36
+	 */
37
+	private $user;
38
+	/**
39
+	 * @var PdoDatabase
40
+	 */
41
+	private $database;
42
+	/**
43
+	 * @var IOAuthProtocolHelper
44
+	 */
45
+	private $oauthProtocolHelper;
46
+	/**
47
+	 * @var bool|null Is the user linked to OAuth
48
+	 */
49
+	private $linked;
50
+	private $partiallyLinked;
51
+	/** @var OAuthToken */
52
+	private $accessToken;
53
+	/** @var bool */
54
+	private $accessTokenLoaded = false;
55
+	/**
56
+	 * @var OAuthIdentity
57
+	 */
58
+	private $identity = null;
59
+	/**
60
+	 * @var bool
61
+	 */
62
+	private $identityLoaded = false;
63
+	/**
64
+	 * @var SiteConfiguration
65
+	 */
66
+	private $siteConfiguration;
67
+
68
+	private $legacyTokens;
69
+
70
+	#region Static methods
71
+
72
+	public static function findUserByRequestToken($requestToken, PdoDatabase $database)
73
+	{
74
+		$statement = $database->prepare(<<<'SQL'
75 75
             SELECT u.* FROM user u 
76 76
             INNER JOIN oauthtoken t ON t.user = u.id 
77 77
             WHERE t.type = :type AND t.token = :token
78 78
 SQL
79
-        );
80
-        $statement->execute(array(':type' => self::TOKEN_REQUEST, ':token' => $requestToken));
81
-
82
-        /** @var User $user */
83
-        $user = $statement->fetchObject(User::class);
84
-        $statement->closeCursor();
85
-
86
-        if ($user === false) {
87
-            throw new ApplicationLogicException('Token not found in store, please try again');
88
-        }
89
-
90
-        $user->setDatabase($database);
91
-
92
-        return $user;
93
-    }
94
-
95
-    public static function userIsFullyLinked(User $user, PdoDatabase $database = null)
96
-    {
97
-        if (self::$tokenCountStatement === null && $database === null) {
98
-            throw new ApplicationLogicException('Static link request without initialised statement');
99
-        }
100
-
101
-        return self::runTokenCount($user->getId(), $database, self::TOKEN_ACCESS);
102
-    }
103
-
104
-    public static function userIsPartiallyLinked(User $user, PdoDatabase $database = null)
105
-    {
106
-        if (self::$tokenCountStatement === null && $database === null) {
107
-            throw new ApplicationLogicException('Static link request without initialised statement');
108
-        }
109
-
110
-        if (self::userIsFullyLinked($user, $database)) {
111
-            return false;
112
-        }
113
-
114
-        return self::runTokenCount($user->getId(), $database, self::TOKEN_REQUEST)
115
-            || $user->getOnWikiName() == null;
116
-    }
117
-
118
-    /**
119
-     * @param PdoDatabase $database
120
-     */
121
-    public static function prepareTokenCountStatement(PdoDatabase $database)
122
-    {
123
-        if (self::$tokenCountStatement === null) {
124
-            self::$tokenCountStatement = $database->prepare('SELECT COUNT(*) FROM oauthtoken WHERE user = :user AND type = :type');
125
-        }
126
-    }
127
-
128
-    private static function runTokenCount($userId, $database, $tokenType)
129
-    {
130
-        if (self::$tokenCountStatement === null) {
131
-            self::prepareTokenCountStatement($database);
132
-        }
133
-
134
-        self::$tokenCountStatement->execute(array(
135
-            ':user' => $userId,
136
-            ':type' => $tokenType,
137
-        ));
138
-
139
-        $tokenCount = self::$tokenCountStatement->fetchColumn();
140
-        $linked = $tokenCount > 0;
141
-        self::$tokenCountStatement->closeCursor();
142
-
143
-        return $linked;
144
-    }
145
-
146
-    #endregion Static methods
147
-
148
-    /**
149
-     * OAuthUserHelper constructor.
150
-     *
151
-     * @param User                 $user
152
-     * @param PdoDatabase          $database
153
-     * @param IOAuthProtocolHelper $oauthProtocolHelper
154
-     * @param SiteConfiguration    $siteConfiguration
155
-     */
156
-    public function __construct(
157
-        User $user,
158
-        PdoDatabase $database,
159
-        IOAuthProtocolHelper $oauthProtocolHelper,
160
-        SiteConfiguration $siteConfiguration
161
-    ) {
162
-        $this->user = $user;
163
-        $this->database = $database;
164
-        $this->oauthProtocolHelper = $oauthProtocolHelper;
165
-
166
-        $this->linked = null;
167
-        $this->partiallyLinked = null;
168
-        $this->siteConfiguration = $siteConfiguration;
169
-
170
-        self::prepareTokenCountStatement($database);
171
-        $this->getTokenStatement = $this->database->prepare('SELECT * FROM oauthtoken WHERE user = :user AND type = :type');
172
-
173
-        $this->legacyTokens = $this->siteConfiguration->getOauthLegacyConsumerTokens();
174
-    }
175
-
176
-    /**
177
-     * Determines if the user is fully connected to OAuth.
178
-     *
179
-     * @return bool
180
-     */
181
-    public function isFullyLinked()
182
-    {
183
-        if ($this->linked === null) {
184
-            $this->linked = self::userIsFullyLinked($this->user, $this->database);
185
-        }
186
-
187
-        return $this->linked;
188
-    }
189
-
190
-    /**
191
-     * Attempts to figure out if a user is partially linked to OAuth, and therefore needs to complete the OAuth
192
-     * procedure before configuring.
193
-     * @return bool
194
-     */
195
-    public function isPartiallyLinked()
196
-    {
197
-        if ($this->partiallyLinked === null) {
198
-            $this->partiallyLinked = self::userIsPartiallyLinked($this->user, $this->database);
199
-        }
200
-
201
-        return $this->partiallyLinked;
202
-    }
203
-
204
-    public function canCreateAccount()
205
-    {
206
-        return $this->isFullyLinked()
207
-            && $this->getIdentity(true)->getGrantBasic()
208
-            && $this->getIdentity(true)->getGrantHighVolume()
209
-            && $this->getIdentity(true)->getGrantCreateAccount();
210
-    }
211
-
212
-    public function canWelcome()
213
-    {
214
-        return $this->isFullyLinked()
215
-            && $this->getIdentity(true)->getGrantBasic()
216
-            && $this->getIdentity(true)->getGrantHighVolume()
217
-            && $this->getIdentity(true)->getGrantCreateEditMovePage();
218
-    }
219
-
220
-    /**
221
-     * @throws OAuthException
222
-     * @throws CurlException
223
-     * @throws OptimisticLockFailedException
224
-     * @throws Exception
225
-     */
226
-    public function refreshIdentity()
227
-    {
228
-        $this->loadIdentity();
229
-
230
-        if ($this->identity === null) {
231
-            $this->identity = new OAuthIdentity();
232
-            $this->identity->setUserId($this->user->getId());
233
-            $this->identity->setDatabase($this->database);
234
-        }
235
-
236
-        $token = $this->loadAccessToken();
237
-
238
-        try {
239
-            $rawTicket = $this->oauthProtocolHelper->getIdentityTicket($token->getToken(), $token->getSecret());
240
-        }
241
-        catch (Exception $ex) {
242
-            if (strpos($ex->getMessage(), "mwoauthdatastore-access-token-not-found") !== false) {
243
-                throw new OAuthException('No approved grants for this access token.', -1, $ex);
244
-            }
245
-
246
-            throw $ex;
247
-        }
248
-
249
-        $this->identity->populate($rawTicket);
250
-
251
-        if (!$this->identityIsValid()) {
252
-            throw new OAuthException('Identity ticket is not valid!');
253
-        }
254
-
255
-        $this->identity->save();
256
-
257
-        $this->user->setOnWikiName($this->identity->getUsername());
258
-        $this->user->save();
259
-    }
260
-
261
-    /**
262
-     * @return string
263
-     * @throws CurlException
264
-     */
265
-    public function getRequestToken()
266
-    {
267
-        $token = $this->oauthProtocolHelper->getRequestToken();
268
-
269
-        $this->partiallyLinked = true;
270
-        $this->linked = false;
271
-
272
-        $this->database
273
-            ->prepare('DELETE FROM oauthtoken WHERE user = :user AND type = :type')
274
-            ->execute(array(':user' => $this->user->getId(), ':type' => self::TOKEN_REQUEST));
275
-
276
-        $this->database
277
-            ->prepare('INSERT INTO oauthtoken (user, type, token, secret, expiry) VALUES (:user, :type, :token, :secret, DATE_ADD(NOW(), INTERVAL 1 DAY))')
278
-            ->execute(array(
279
-                ':user'   => $this->user->getId(),
280
-                ':type'   => self::TOKEN_REQUEST,
281
-                ':token'  => $token->key,
282
-                ':secret' => $token->secret,
283
-            ));
284
-
285
-        return $this->oauthProtocolHelper->getAuthoriseUrl($token->key);
286
-    }
287
-
288
-    /**
289
-     * @param $verificationToken
290
-     *
291
-     * @throws ApplicationLogicException
292
-     * @throws CurlException
293
-     * @throws OAuthException
294
-     * @throws OptimisticLockFailedException
295
-     */
296
-    public function completeHandshake($verificationToken)
297
-    {
298
-        $this->getTokenStatement->execute(array(':user' => $this->user->getId(), ':type' => self::TOKEN_REQUEST));
299
-
300
-        /** @var OAuthToken $token */
301
-        $token = $this->getTokenStatement->fetchObject(OAuthToken::class);
302
-        $this->getTokenStatement->closeCursor();
303
-
304
-        if ($token === false) {
305
-            throw new ApplicationLogicException('Cannot find request token');
306
-        }
307
-
308
-        $token->setDatabase($this->database);
309
-
310
-        $accessToken = $this->oauthProtocolHelper->callbackCompleted($token->getToken(), $token->getSecret(),
311
-            $verificationToken);
312
-
313
-        $clearStatement = $this->database->prepare('DELETE FROM oauthtoken WHERE user = :u AND type = :t');
314
-        $clearStatement->execute(array(':u' => $this->user->getId(), ':t' => self::TOKEN_ACCESS));
315
-
316
-        $token->setToken($accessToken->key);
317
-        $token->setSecret($accessToken->secret);
318
-        $token->setType(self::TOKEN_ACCESS);
319
-        $token->setExpiry(null);
320
-        $token->save();
321
-
322
-        $this->partiallyLinked = false;
323
-        $this->linked = true;
324
-
325
-        $this->refreshIdentity();
326
-    }
327
-
328
-    public function detach()
329
-    {
330
-        $this->loadIdentity();
331
-
332
-        $this->identity->delete();
333
-        $statement = $this->database->prepare('DELETE FROM oauthtoken WHERE user = :user');
334
-        $statement->execute(array(':user' => $this->user->getId()));
335
-
336
-        $this->identity = null;
337
-        $this->linked = false;
338
-        $this->partiallyLinked = false;
339
-    }
340
-
341
-    /**
342
-     * @param bool $expiredOk
343
-     *
344
-     * @return OAuthIdentity
345
-     * @throws OAuthException
346
-     */
347
-    public function getIdentity($expiredOk = false)
348
-    {
349
-        $this->loadIdentity();
350
-
351
-        if (!$this->identityIsValid($expiredOk)) {
352
-            throw new OAuthException('Stored identity is not valid.');
353
-        }
354
-
355
-        return $this->identity;
356
-    }
357
-
358
-    public function doApiCall($params, $method)
359
-    {
360
-        // Ensure we're logged in
361
-        $params['assert'] = 'user';
362
-
363
-        $token = $this->loadAccessToken();
364
-        return $this->oauthProtocolHelper->apiCall($params, $token->getToken(), $token->getSecret(), $method);
365
-    }
366
-
367
-    /**
368
-     * @param bool $expiredOk
369
-     *
370
-     * @return bool
371
-     */
372
-    private function identityIsValid($expiredOk = false)
373
-    {
374
-        $this->loadIdentity();
375
-
376
-        if ($this->identity === null) {
377
-            return false;
378
-        }
379
-
380
-        if ($this->identity->getIssuedAtTime() === false
381
-            || $this->identity->getExpirationTime() === false
382
-            || $this->identity->getAudience() === false
383
-            || $this->identity->getIssuer() === false
384
-        ) {
385
-            // this isn't populated properly.
386
-            return false;
387
-        }
388
-
389
-        $issue = DateTimeImmutable::createFromFormat("U", $this->identity->getIssuedAtTime());
390
-        $now = new DateTimeImmutable();
391
-
392
-        if ($issue > $now) {
393
-            // wat.
394
-            return false;
395
-        }
396
-
397
-        if ($this->identityExpired() && !$expiredOk) {
398
-            // soz.
399
-            return false;
400
-        }
401
-
402
-        if ($this->identity->getAudience() !== $this->siteConfiguration->getOAuthConsumerToken()) {
403
-            // token not issued for us
404
-
405
-            // we allow cases where the cache is expired and the cache is for a legacy token
406
-            if (!($expiredOk && in_array($this->identity->getAudience(), $this->legacyTokens))) {
407
-                return false;
408
-            }
409
-        }
410
-
411
-        if ($this->identity->getIssuer() !== $this->siteConfiguration->getOauthMediaWikiCanonicalServer()) {
412
-            // token not issued by the right person
413
-            return false;
414
-        }
415
-
416
-        // can't find a reason to not trust it
417
-        return true;
418
-    }
419
-
420
-    /**
421
-     * @return bool
422
-     */
423
-    public function identityExpired()
424
-    {
425
-        // allowed max age
426
-        $gracePeriod = $this->siteConfiguration->getOauthIdentityGraceTime();
427
-
428
-        $expiry = DateTimeImmutable::createFromFormat("U", $this->identity->getExpirationTime());
429
-        $graceExpiry = $expiry->modify($gracePeriod);
430
-        $now = new DateTimeImmutable();
431
-
432
-        return $graceExpiry < $now;
433
-    }
434
-
435
-    /**
436
-     * Loads the OAuth identity from the database for the current user.
437
-     */
438
-    private function loadIdentity()
439
-    {
440
-        if ($this->identityLoaded) {
441
-            return;
442
-        }
443
-
444
-        $statement = $this->database->prepare('SELECT * FROM oauthidentity WHERE user = :user');
445
-        $statement->execute(array(':user' => $this->user->getId()));
446
-        /** @var OAuthIdentity $obj */
447
-        $obj = $statement->fetchObject(OAuthIdentity::class);
448
-
449
-        if ($obj === false) {
450
-            // failed to load identity.
451
-            $this->identityLoaded = true;
452
-            $this->identity = null;
453
-
454
-            return;
455
-        }
456
-
457
-        $obj->setDatabase($this->database);
458
-        $this->identityLoaded = true;
459
-        $this->identity = $obj;
460
-    }
461
-
462
-    /**
463
-     * @return OAuthToken
464
-     * @throws OAuthException
465
-     */
466
-    private function loadAccessToken()
467
-    {
468
-        if (!$this->accessTokenLoaded) {
469
-            $this->getTokenStatement->execute(array(':user' => $this->user->getId(), ':type' => self::TOKEN_ACCESS));
470
-            /** @var OAuthToken $token */
471
-            $token = $this->getTokenStatement->fetchObject(OAuthToken::class);
472
-            $this->getTokenStatement->closeCursor();
473
-
474
-            if ($token === false) {
475
-                throw new OAuthException('Access token not found!');
476
-            }
477
-
478
-            $this->accessToken = $token;
479
-            $this->accessTokenLoaded = true;
480
-        }
481
-
482
-        return $this->accessToken;
483
-    }
79
+		);
80
+		$statement->execute(array(':type' => self::TOKEN_REQUEST, ':token' => $requestToken));
81
+
82
+		/** @var User $user */
83
+		$user = $statement->fetchObject(User::class);
84
+		$statement->closeCursor();
85
+
86
+		if ($user === false) {
87
+			throw new ApplicationLogicException('Token not found in store, please try again');
88
+		}
89
+
90
+		$user->setDatabase($database);
91
+
92
+		return $user;
93
+	}
94
+
95
+	public static function userIsFullyLinked(User $user, PdoDatabase $database = null)
96
+	{
97
+		if (self::$tokenCountStatement === null && $database === null) {
98
+			throw new ApplicationLogicException('Static link request without initialised statement');
99
+		}
100
+
101
+		return self::runTokenCount($user->getId(), $database, self::TOKEN_ACCESS);
102
+	}
103
+
104
+	public static function userIsPartiallyLinked(User $user, PdoDatabase $database = null)
105
+	{
106
+		if (self::$tokenCountStatement === null && $database === null) {
107
+			throw new ApplicationLogicException('Static link request without initialised statement');
108
+		}
109
+
110
+		if (self::userIsFullyLinked($user, $database)) {
111
+			return false;
112
+		}
113
+
114
+		return self::runTokenCount($user->getId(), $database, self::TOKEN_REQUEST)
115
+			|| $user->getOnWikiName() == null;
116
+	}
117
+
118
+	/**
119
+	 * @param PdoDatabase $database
120
+	 */
121
+	public static function prepareTokenCountStatement(PdoDatabase $database)
122
+	{
123
+		if (self::$tokenCountStatement === null) {
124
+			self::$tokenCountStatement = $database->prepare('SELECT COUNT(*) FROM oauthtoken WHERE user = :user AND type = :type');
125
+		}
126
+	}
127
+
128
+	private static function runTokenCount($userId, $database, $tokenType)
129
+	{
130
+		if (self::$tokenCountStatement === null) {
131
+			self::prepareTokenCountStatement($database);
132
+		}
133
+
134
+		self::$tokenCountStatement->execute(array(
135
+			':user' => $userId,
136
+			':type' => $tokenType,
137
+		));
138
+
139
+		$tokenCount = self::$tokenCountStatement->fetchColumn();
140
+		$linked = $tokenCount > 0;
141
+		self::$tokenCountStatement->closeCursor();
142
+
143
+		return $linked;
144
+	}
145
+
146
+	#endregion Static methods
147
+
148
+	/**
149
+	 * OAuthUserHelper constructor.
150
+	 *
151
+	 * @param User                 $user
152
+	 * @param PdoDatabase          $database
153
+	 * @param IOAuthProtocolHelper $oauthProtocolHelper
154
+	 * @param SiteConfiguration    $siteConfiguration
155
+	 */
156
+	public function __construct(
157
+		User $user,
158
+		PdoDatabase $database,
159
+		IOAuthProtocolHelper $oauthProtocolHelper,
160
+		SiteConfiguration $siteConfiguration
161
+	) {
162
+		$this->user = $user;
163
+		$this->database = $database;
164
+		$this->oauthProtocolHelper = $oauthProtocolHelper;
165
+
166
+		$this->linked = null;
167
+		$this->partiallyLinked = null;
168
+		$this->siteConfiguration = $siteConfiguration;
169
+
170
+		self::prepareTokenCountStatement($database);
171
+		$this->getTokenStatement = $this->database->prepare('SELECT * FROM oauthtoken WHERE user = :user AND type = :type');
172
+
173
+		$this->legacyTokens = $this->siteConfiguration->getOauthLegacyConsumerTokens();
174
+	}
175
+
176
+	/**
177
+	 * Determines if the user is fully connected to OAuth.
178
+	 *
179
+	 * @return bool
180
+	 */
181
+	public function isFullyLinked()
182
+	{
183
+		if ($this->linked === null) {
184
+			$this->linked = self::userIsFullyLinked($this->user, $this->database);
185
+		}
186
+
187
+		return $this->linked;
188
+	}
189
+
190
+	/**
191
+	 * Attempts to figure out if a user is partially linked to OAuth, and therefore needs to complete the OAuth
192
+	 * procedure before configuring.
193
+	 * @return bool
194
+	 */
195
+	public function isPartiallyLinked()
196
+	{
197
+		if ($this->partiallyLinked === null) {
198
+			$this->partiallyLinked = self::userIsPartiallyLinked($this->user, $this->database);
199
+		}
200
+
201
+		return $this->partiallyLinked;
202
+	}
203
+
204
+	public function canCreateAccount()
205
+	{
206
+		return $this->isFullyLinked()
207
+			&& $this->getIdentity(true)->getGrantBasic()
208
+			&& $this->getIdentity(true)->getGrantHighVolume()
209
+			&& $this->getIdentity(true)->getGrantCreateAccount();
210
+	}
211
+
212
+	public function canWelcome()
213
+	{
214
+		return $this->isFullyLinked()
215
+			&& $this->getIdentity(true)->getGrantBasic()
216
+			&& $this->getIdentity(true)->getGrantHighVolume()
217
+			&& $this->getIdentity(true)->getGrantCreateEditMovePage();
218
+	}
219
+
220
+	/**
221
+	 * @throws OAuthException
222
+	 * @throws CurlException
223
+	 * @throws OptimisticLockFailedException
224
+	 * @throws Exception
225
+	 */
226
+	public function refreshIdentity()
227
+	{
228
+		$this->loadIdentity();
229
+
230
+		if ($this->identity === null) {
231
+			$this->identity = new OAuthIdentity();
232
+			$this->identity->setUserId($this->user->getId());
233
+			$this->identity->setDatabase($this->database);
234
+		}
235
+
236
+		$token = $this->loadAccessToken();
237
+
238
+		try {
239
+			$rawTicket = $this->oauthProtocolHelper->getIdentityTicket($token->getToken(), $token->getSecret());
240
+		}
241
+		catch (Exception $ex) {
242
+			if (strpos($ex->getMessage(), "mwoauthdatastore-access-token-not-found") !== false) {
243
+				throw new OAuthException('No approved grants for this access token.', -1, $ex);
244
+			}
245
+
246
+			throw $ex;
247
+		}
248
+
249
+		$this->identity->populate($rawTicket);
250
+
251
+		if (!$this->identityIsValid()) {
252
+			throw new OAuthException('Identity ticket is not valid!');
253
+		}
254
+
255
+		$this->identity->save();
256
+
257
+		$this->user->setOnWikiName($this->identity->getUsername());
258
+		$this->user->save();
259
+	}
260
+
261
+	/**
262
+	 * @return string
263
+	 * @throws CurlException
264
+	 */
265
+	public function getRequestToken()
266
+	{
267
+		$token = $this->oauthProtocolHelper->getRequestToken();
268
+
269
+		$this->partiallyLinked = true;
270
+		$this->linked = false;
271
+
272
+		$this->database
273
+			->prepare('DELETE FROM oauthtoken WHERE user = :user AND type = :type')
274
+			->execute(array(':user' => $this->user->getId(), ':type' => self::TOKEN_REQUEST));
275
+
276
+		$this->database
277
+			->prepare('INSERT INTO oauthtoken (user, type, token, secret, expiry) VALUES (:user, :type, :token, :secret, DATE_ADD(NOW(), INTERVAL 1 DAY))')
278
+			->execute(array(
279
+				':user'   => $this->user->getId(),
280
+				':type'   => self::TOKEN_REQUEST,
281
+				':token'  => $token->key,
282
+				':secret' => $token->secret,
283
+			));
284
+
285
+		return $this->oauthProtocolHelper->getAuthoriseUrl($token->key);
286
+	}
287
+
288
+	/**
289
+	 * @param $verificationToken
290
+	 *
291
+	 * @throws ApplicationLogicException
292
+	 * @throws CurlException
293
+	 * @throws OAuthException
294
+	 * @throws OptimisticLockFailedException
295
+	 */
296
+	public function completeHandshake($verificationToken)
297
+	{
298
+		$this->getTokenStatement->execute(array(':user' => $this->user->getId(), ':type' => self::TOKEN_REQUEST));
299
+
300
+		/** @var OAuthToken $token */
301
+		$token = $this->getTokenStatement->fetchObject(OAuthToken::class);
302
+		$this->getTokenStatement->closeCursor();
303
+
304
+		if ($token === false) {
305
+			throw new ApplicationLogicException('Cannot find request token');
306
+		}
307
+
308
+		$token->setDatabase($this->database);
309
+
310
+		$accessToken = $this->oauthProtocolHelper->callbackCompleted($token->getToken(), $token->getSecret(),
311
+			$verificationToken);
312
+
313
+		$clearStatement = $this->database->prepare('DELETE FROM oauthtoken WHERE user = :u AND type = :t');
314
+		$clearStatement->execute(array(':u' => $this->user->getId(), ':t' => self::TOKEN_ACCESS));
315
+
316
+		$token->setToken($accessToken->key);
317
+		$token->setSecret($accessToken->secret);
318
+		$token->setType(self::TOKEN_ACCESS);
319
+		$token->setExpiry(null);
320
+		$token->save();
321
+
322
+		$this->partiallyLinked = false;
323
+		$this->linked = true;
324
+
325
+		$this->refreshIdentity();
326
+	}
327
+
328
+	public function detach()
329
+	{
330
+		$this->loadIdentity();
331
+
332
+		$this->identity->delete();
333
+		$statement = $this->database->prepare('DELETE FROM oauthtoken WHERE user = :user');
334
+		$statement->execute(array(':user' => $this->user->getId()));
335
+
336
+		$this->identity = null;
337
+		$this->linked = false;
338
+		$this->partiallyLinked = false;
339
+	}
340
+
341
+	/**
342
+	 * @param bool $expiredOk
343
+	 *
344
+	 * @return OAuthIdentity
345
+	 * @throws OAuthException
346
+	 */
347
+	public function getIdentity($expiredOk = false)
348
+	{
349
+		$this->loadIdentity();
350
+
351
+		if (!$this->identityIsValid($expiredOk)) {
352
+			throw new OAuthException('Stored identity is not valid.');
353
+		}
354
+
355
+		return $this->identity;
356
+	}
357
+
358
+	public function doApiCall($params, $method)
359
+	{
360
+		// Ensure we're logged in
361
+		$params['assert'] = 'user';
362
+
363
+		$token = $this->loadAccessToken();
364
+		return $this->oauthProtocolHelper->apiCall($params, $token->getToken(), $token->getSecret(), $method);
365
+	}
366
+
367
+	/**
368
+	 * @param bool $expiredOk
369
+	 *
370
+	 * @return bool
371
+	 */
372
+	private function identityIsValid($expiredOk = false)
373
+	{
374
+		$this->loadIdentity();
375
+
376
+		if ($this->identity === null) {
377
+			return false;
378
+		}
379
+
380
+		if ($this->identity->getIssuedAtTime() === false
381
+			|| $this->identity->getExpirationTime() === false
382
+			|| $this->identity->getAudience() === false
383
+			|| $this->identity->getIssuer() === false
384
+		) {
385
+			// this isn't populated properly.
386
+			return false;
387
+		}
388
+
389
+		$issue = DateTimeImmutable::createFromFormat("U", $this->identity->getIssuedAtTime());
390
+		$now = new DateTimeImmutable();
391
+
392
+		if ($issue > $now) {
393
+			// wat.
394
+			return false;
395
+		}
396
+
397
+		if ($this->identityExpired() && !$expiredOk) {
398
+			// soz.
399
+			return false;
400
+		}
401
+
402
+		if ($this->identity->getAudience() !== $this->siteConfiguration->getOAuthConsumerToken()) {
403
+			// token not issued for us
404
+
405
+			// we allow cases where the cache is expired and the cache is for a legacy token
406
+			if (!($expiredOk && in_array($this->identity->getAudience(), $this->legacyTokens))) {
407
+				return false;
408
+			}
409
+		}
410
+
411
+		if ($this->identity->getIssuer() !== $this->siteConfiguration->getOauthMediaWikiCanonicalServer()) {
412
+			// token not issued by the right person
413
+			return false;
414
+		}
415
+
416
+		// can't find a reason to not trust it
417
+		return true;
418
+	}
419
+
420
+	/**
421
+	 * @return bool
422
+	 */
423
+	public function identityExpired()
424
+	{
425
+		// allowed max age
426
+		$gracePeriod = $this->siteConfiguration->getOauthIdentityGraceTime();
427
+
428
+		$expiry = DateTimeImmutable::createFromFormat("U", $this->identity->getExpirationTime());
429
+		$graceExpiry = $expiry->modify($gracePeriod);
430
+		$now = new DateTimeImmutable();
431
+
432
+		return $graceExpiry < $now;
433
+	}
434
+
435
+	/**
436
+	 * Loads the OAuth identity from the database for the current user.
437
+	 */
438
+	private function loadIdentity()
439
+	{
440
+		if ($this->identityLoaded) {
441
+			return;
442
+		}
443
+
444
+		$statement = $this->database->prepare('SELECT * FROM oauthidentity WHERE user = :user');
445
+		$statement->execute(array(':user' => $this->user->getId()));
446
+		/** @var OAuthIdentity $obj */
447
+		$obj = $statement->fetchObject(OAuthIdentity::class);
448
+
449
+		if ($obj === false) {
450
+			// failed to load identity.
451
+			$this->identityLoaded = true;
452
+			$this->identity = null;
453
+
454
+			return;
455
+		}
456
+
457
+		$obj->setDatabase($this->database);
458
+		$this->identityLoaded = true;
459
+		$this->identity = $obj;
460
+	}
461
+
462
+	/**
463
+	 * @return OAuthToken
464
+	 * @throws OAuthException
465
+	 */
466
+	private function loadAccessToken()
467
+	{
468
+		if (!$this->accessTokenLoaded) {
469
+			$this->getTokenStatement->execute(array(':user' => $this->user->getId(), ':type' => self::TOKEN_ACCESS));
470
+			/** @var OAuthToken $token */
471
+			$token = $this->getTokenStatement->fetchObject(OAuthToken::class);
472
+			$this->getTokenStatement->closeCursor();
473
+
474
+			if ($token === false) {
475
+				throw new OAuthException('Access token not found!');
476
+			}
477
+
478
+			$this->accessToken = $token;
479
+			$this->accessTokenLoaded = true;
480
+		}
481
+
482
+		return $this->accessToken;
483
+	}
484 484
 }
Please login to merge, or discard this patch.