Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
110 / 110 |
|
100.00% |
17 / 17 |
CRAP | |
100.00% |
1 / 1 |
Request | |
100.00% |
110 / 110 |
|
100.00% |
17 / 17 |
50 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
fromRequest | |
100.00% |
25 / 25 |
|
100.00% |
1 / 1 |
11 | |||
fromConsumerAndToken | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
setParameter | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
4 | |||
getParameter | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
getParameters | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
unsetParameter | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSignableParameters | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getSignatureBaseString | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
getNormalizedMethod | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getNormalizedUrl | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
10 | |||
toUrl | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
toPostData | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
toHeader | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
6 | |||
__toString | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
signRequest | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
buildSignature | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * @section LICENSE |
4 | * Copyright (c) 2007 Andy Smith |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining |
7 | * a copy of this software and associated documentation files (the |
8 | * "Software"), to deal in the Software without restriction, including without |
9 | * limitation the rights to use, copy, modify, merge, publish, distribute, |
10 | * sublicense, and/or sell copies of the Software, and to permit persons to |
11 | * whom the Software is furnished to do so, subject to the following |
12 | * conditions: |
13 | * |
14 | * The above copyright notice and this permission notice shall be included in |
15 | * all copies or substantial portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
23 | * DEALINGS IN THE SOFTWARE. |
24 | * |
25 | * @file |
26 | */ |
27 | |
28 | namespace MediaWiki\OAuthClient; |
29 | |
30 | /** |
31 | * An OAuth request |
32 | */ |
33 | class Request { |
34 | /** |
35 | * @var array |
36 | */ |
37 | protected $parameters; |
38 | |
39 | /** |
40 | * @var string |
41 | */ |
42 | protected $method; |
43 | |
44 | /** |
45 | * @var string |
46 | */ |
47 | protected $url; |
48 | |
49 | /** |
50 | * @var string |
51 | */ |
52 | public static $version = '1.0'; |
53 | |
54 | /** |
55 | * Used for tests. |
56 | * @var string |
57 | */ |
58 | public static $POST_INPUT = 'php://input'; |
59 | |
60 | /** |
61 | * @param string $method |
62 | * @param string $url |
63 | * @param array|null $parameters |
64 | */ |
65 | public function __construct( $method, $url, $parameters = null ) { |
66 | $parameters = $parameters ?: []; |
67 | $parameters = array_merge( |
68 | Util::parseParameters( parse_url( $url, PHP_URL_QUERY ) ), |
69 | $parameters |
70 | ); |
71 | $this->parameters = $parameters; |
72 | $this->method = $method; |
73 | $this->url = $url; |
74 | } |
75 | |
76 | /** |
77 | * Attempt to build up a request from what was passed to the server |
78 | * |
79 | * @param string|null $method |
80 | * @param string|null $url |
81 | * @param array|null $params |
82 | * @return Request |
83 | */ |
84 | public static function fromRequest( |
85 | $method = null, |
86 | $url = null, |
87 | ?array $params = null |
88 | ) { |
89 | $scheme = ( !isset( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS'] != 'on' ) ? |
90 | 'http' : 'https'; |
91 | $url = ( $url ?: $scheme ) . |
92 | '://' . $_SERVER['SERVER_NAME'] . |
93 | ':' . |
94 | $_SERVER['SERVER_PORT'] . |
95 | $_SERVER['REQUEST_URI']; |
96 | $method = $method ?: $_SERVER['REQUEST_METHOD']; |
97 | |
98 | // We weren't handed any params, so let's find the ones relevant |
99 | // to this request. If you run XML-RPC or similar you should use this |
100 | // to provide your own parsed parameter-list |
101 | if ( !$params ) { |
102 | // Find request headers |
103 | $headers = Util::getHeaders(); |
104 | |
105 | // Parse the query-string to find GET params |
106 | $params = Util::parseParameters( $_SERVER['QUERY_STRING'] ); |
107 | |
108 | // It's a POST request of the proper content-type, so parse POST |
109 | // params and add those overriding any duplicates from GET |
110 | if ( $method === 'POST' && |
111 | isset( $headers['Content-Type'] ) && |
112 | strstr( $headers['Content-Type'], |
113 | 'application/x-www-form-urlencoded' |
114 | ) |
115 | ) { |
116 | $post_data = Util::parseParameters( |
117 | file_get_contents( self::$POST_INPUT ) |
118 | ); |
119 | $params = array_merge( $params, $post_data ); |
120 | } |
121 | |
122 | // We have a Authorization-header with OAuth data. Parse the header |
123 | // and add those overriding any duplicates from GET or POST |
124 | if ( isset( $headers['Authorization'] ) && |
125 | substr( $headers['Authorization'], 0, 6 ) === 'OAuth ' |
126 | ) { |
127 | $header_params = Util::splitHeader( $headers['Authorization'] ); |
128 | $params = array_merge( $params, $header_params ); |
129 | } |
130 | } |
131 | |
132 | return new Request( $method, $url, $params ); |
133 | } |
134 | |
135 | /** |
136 | * @param Consumer $consumer |
137 | * @param Token|null $token |
138 | * @param string $method |
139 | * @param string $url |
140 | * @param array|null $parameters |
141 | * @return Request |
142 | */ |
143 | public static function fromConsumerAndToken( |
144 | Consumer $consumer, |
145 | ?Token $token, |
146 | $method, |
147 | $url, |
148 | ?array $parameters = null |
149 | ) { |
150 | $parameters = $parameters ?: []; |
151 | $defaults = [ |
152 | 'oauth_version' => static::$version, |
153 | 'oauth_nonce' => md5( microtime() . mt_rand() ), |
154 | 'oauth_timestamp' => time(), |
155 | 'oauth_consumer_key' => $consumer->key, |
156 | ]; |
157 | if ( $token ) { |
158 | $defaults['oauth_token'] = $token->key; |
159 | } |
160 | $parameters = array_merge( $defaults, $parameters ); |
161 | |
162 | return new self( $method, $url, $parameters ); |
163 | } |
164 | |
165 | /** |
166 | * @param string $name |
167 | * @param string $value |
168 | * @param bool $allow_duplicates |
169 | */ |
170 | public function setParameter( $name, $value, $allow_duplicates = true ) { |
171 | if ( $allow_duplicates && isset( $this->parameters[$name] ) ) { |
172 | // We have already added parameter(s) with this name, so add to |
173 | // the list |
174 | if ( is_scalar( $this->parameters[$name] ) ) { |
175 | // This is the first duplicate, so transform scalar (string) |
176 | // into an array so we can add the duplicates |
177 | $this->parameters[$name] = [ $this->parameters[$name] ]; |
178 | } |
179 | |
180 | $this->parameters[$name][] = $value; |
181 | } else { |
182 | $this->parameters[$name] = $value; |
183 | } |
184 | } |
185 | |
186 | /** |
187 | * @param string $name |
188 | * @return mixed |
189 | */ |
190 | public function getParameter( $name ) { |
191 | return isset( $this->parameters[$name] ) ? |
192 | $this->parameters[$name] : null; |
193 | } |
194 | |
195 | /** |
196 | * @return array |
197 | */ |
198 | public function getParameters() { |
199 | return $this->parameters; |
200 | } |
201 | |
202 | /** |
203 | * @param string $name |
204 | */ |
205 | public function unsetParameter( $name ) { |
206 | unset( $this->parameters[$name] ); |
207 | } |
208 | |
209 | /** |
210 | * The request parameters, sorted and concatenated into a normalized string. |
211 | * @return string |
212 | */ |
213 | public function getSignableParameters() { |
214 | // Grab all parameters |
215 | $params = $this->parameters; |
216 | |
217 | // Remove oauth_signature if present |
218 | // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") |
219 | if ( isset( $params['oauth_signature'] ) ) { |
220 | unset( $params['oauth_signature'] ); |
221 | } |
222 | |
223 | return Util::buildHttpQuery( $params ); |
224 | } |
225 | |
226 | /** |
227 | * Returns the base string of this request |
228 | * |
229 | * The base string defined as the method, the url |
230 | * and the parameters (normalized), each urlencoded |
231 | * and the concated with &. |
232 | * @return string |
233 | */ |
234 | public function getSignatureBaseString() { |
235 | $parts = [ |
236 | $this->getNormalizedMethod(), |
237 | $this->getNormalizedUrl(), |
238 | $this->getSignableParameters() |
239 | ]; |
240 | |
241 | $parts = Util::urlencode( $parts ); |
242 | |
243 | return implode( '&', $parts ); |
244 | } |
245 | |
246 | /** |
247 | * @return string |
248 | */ |
249 | public function getNormalizedMethod() { |
250 | return strtoupper( $this->method ); |
251 | } |
252 | |
253 | /** |
254 | * Parses the url and rebuilds it to be scheme://host/path |
255 | * @return string |
256 | */ |
257 | public function getNormalizedUrl() { |
258 | $parts = parse_url( $this->url ); |
259 | |
260 | $scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'http'; |
261 | $port = isset( $parts['port'] ) ? |
262 | $parts['port'] : ( $scheme === 'https' ? '443' : '80' ); |
263 | $host = isset( $parts['host'] ) ? strtolower( $parts['host'] ) : ''; |
264 | $path = isset( $parts['path'] ) ? $parts['path'] : ''; |
265 | |
266 | if ( ( $scheme === 'https' && $port != '443' ) || |
267 | ( $scheme === 'http' && $port != '80' ) |
268 | ) { |
269 | $host = "{$host}:{$port}"; |
270 | } |
271 | return "{$scheme}://{$host}{$path}"; |
272 | } |
273 | |
274 | /** |
275 | * Builds a url usable for a GET request |
276 | * @return string |
277 | */ |
278 | public function toUrl() { |
279 | $post_data = $this->toPostData(); |
280 | $out = $this->getNormalizedUrl(); |
281 | if ( $post_data ) { |
282 | $out .= '?' . $post_data; |
283 | } |
284 | return $out; |
285 | } |
286 | |
287 | /** |
288 | * Builds the data one would send in a POST request |
289 | * @return string |
290 | */ |
291 | public function toPostData() { |
292 | return Util::buildHttpQuery( $this->parameters ); |
293 | } |
294 | |
295 | /** |
296 | * Builds the Authorization: header |
297 | * @param string|null $realm |
298 | * @return string |
299 | */ |
300 | public function toHeader( $realm = null ) { |
301 | $first = true; |
302 | if ( $realm ) { |
303 | $out = 'Authorization: OAuth realm="' . |
304 | Util::urlencode( $realm ) . '"'; |
305 | $first = false; |
306 | } else { |
307 | $out = 'Authorization: OAuth'; |
308 | } |
309 | |
310 | foreach ( $this->parameters as $k => $v ) { |
311 | if ( substr( $k, 0, 5 ) !== 'oauth' ) { |
312 | continue; |
313 | } |
314 | if ( is_array( $v ) ) { |
315 | throw new Exception( 'Arrays not supported in headers' ); |
316 | } |
317 | $out .= ( $first ) ? ' ' : ','; |
318 | $out .= Util::urlencode( $k ) . '="' . Util::urlencode( $v ) . '"'; |
319 | $first = false; |
320 | } |
321 | return $out; |
322 | } |
323 | |
324 | public function __toString() { |
325 | return $this->toUrl(); |
326 | } |
327 | |
328 | /** |
329 | * @param SignatureMethod $signature_method |
330 | * @param Consumer $consumer |
331 | * @param Token|null $token |
332 | */ |
333 | public function signRequest( |
334 | SignatureMethod $signature_method, |
335 | Consumer $consumer, |
336 | ?Token $token = null |
337 | ) { |
338 | $this->setParameter( |
339 | 'oauth_signature_method', |
340 | $signature_method->getName(), |
341 | false |
342 | ); |
343 | $signature = $this->buildSignature( |
344 | $signature_method, $consumer, $token |
345 | ); |
346 | $this->setParameter( 'oauth_signature', $signature, false ); |
347 | } |
348 | |
349 | /** |
350 | * @param SignatureMethod $signature_method |
351 | * @param Consumer $consumer |
352 | * @param Token|null $token |
353 | * @return mixed |
354 | */ |
355 | public function buildSignature( |
356 | SignatureMethod $signature_method, |
357 | Consumer $consumer, |
358 | ?Token $token = null |
359 | ) { |
360 | $signature = $signature_method->buildSignature( |
361 | $this, $consumer, $token |
362 | ); |
363 | return $signature; |
364 | } |
365 | } |