Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
105 / 105
100.00% covered (success)
100.00%
19 / 19
CRAP
100.00% covered (success)
100.00%
1 / 1
OAuthRequest
100.00% covered (success)
100.00%
105 / 105
100.00% covered (success)
100.00%
19 / 19
52
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 from_request
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
11
 from_consumer_and_token
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 set_parameter
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 get_parameter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 get_parameters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 unset_parameter
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_signable_parameters
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 get_signature_base_string
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 get_normalized_http_method
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_normalized_http_url
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
10
 to_url
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 to_postdata
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 to_header
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
6
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 sign_request
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 build_signature
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 generate_timestamp
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 generate_nonce
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2// vim: foldmethod=marker
3/**
4 * The MIT License
5 *
6 * Copyright (c) 2007 Andy Smith
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files ( the "Software" ), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27namespace MediaWiki\Extension\OAuth\Lib;
28
29use MediaWiki\Extension\OAuth\Lib\OAuthException;
30use MediaWiki\Extension\OAuth\Lib\OAuthUtil;
31use MediaWiki\Logger\LoggerFactory;
32use Psr\Log\LoggerInterface;
33
34class OAuthRequest {
35    protected $parameters;
36    protected $http_method;
37    protected $http_url;
38    // for debug purposes
39    public $base_string;
40    public static $version = '1.0';
41    public static $POST_INPUT = 'php://input';
42
43    /** @var LoggerInterface */
44    protected $logger;
45
46    function __construct( $http_method, $http_url, $parameters = null ) {
47        $parameters = ( $parameters ) ? $parameters : array();
48        $parameters = array_merge(
49            OAuthUtil::parse_parameters( parse_url( $http_url, PHP_URL_QUERY ) ),
50            $parameters
51        );
52        $this->parameters = $parameters;
53        $this->http_method = $http_method;
54        $this->http_url = $http_url;
55        $this->logger = LoggerFactory::getInstance( 'OAuth' );
56    }
57
58    /**
59     * attempt to build up a request from what was passed to the server
60     */
61    public static function from_request( $http_method = null, $http_url = null, $parameters = null ) {
62        $scheme = ( !isset( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS'] != "on" ) ? 'http' : 'https';
63        $http_url = ( $http_url ) ? $http_url : $scheme . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
64        $http_method = ( $http_method ) ? $http_method : $_SERVER['REQUEST_METHOD'];
65
66        // We weren't handed any parameters, so let's find the ones relevant to
67        // this request.
68        // If you run XML-RPC or similar you should use this to provide your own
69        // parsed parameter-list
70        if ( !$parameters ) {
71            // Find request headers
72            $request_headers = OAuthUtil::get_headers();
73
74            // Parse the query-string to find GET parameters
75            $parameters = OAuthUtil::parse_parameters( $_SERVER['QUERY_STRING'] );
76
77            // It's a POST request of the proper content-type, so parse POST
78            // parameters and add those overriding any duplicates from GET
79            if ( $http_method == "POST" && isset( $request_headers['Content-Type'] ) && strstr(
80                    $request_headers['Content-Type'],
81                    'application/x-www-form-urlencoded'
82                )
83            ) {
84                $post_data = OAuthUtil::parse_parameters( file_get_contents( self::$POST_INPUT ) );
85                $parameters = array_merge( $parameters, $post_data );
86            }
87
88            // We have a Authorization-header with OAuth data. Parse the header
89            // and add those overriding any duplicates from GET or POST
90            if ( isset( $request_headers['Authorization'] ) && substr(
91                    $request_headers['Authorization'],
92                    0,
93                    6
94                ) == 'OAuth '
95            ) {
96                $header_parameters = OAuthUtil::split_header( $request_headers['Authorization'] );
97                $parameters = array_merge( $parameters, $header_parameters );
98            }
99        }
100
101        return new OAuthRequest( $http_method, $http_url, $parameters );
102    }
103
104    /**
105     * pretty much a helper function to set up the request
106     */
107    public static function from_consumer_and_token( $consumer, $token, $http_method, $http_url, $parameters = null ) {
108        $parameters = ( $parameters ) ? $parameters : array();
109        $defaults = array(
110            "oauth_version" => OAuthRequest::$version,
111            "oauth_nonce" => OAuthRequest::generate_nonce(),
112            "oauth_timestamp" => OAuthRequest::generate_timestamp(),
113            "oauth_consumer_key" => $consumer->key
114        );
115        if ( $token ) {
116            $defaults['oauth_token'] = $token->key;
117        }
118
119        $parameters = array_merge( $defaults, $parameters );
120
121        return new OAuthRequest( $http_method, $http_url, $parameters );
122    }
123
124    public function set_parameter( $name, $value, $allow_duplicates = true ) {
125        if ( $allow_duplicates && isset( $this->parameters[$name] ) ) {
126            // We have already added parameter( s ) with this name, so add to the list
127            if ( is_scalar( $this->parameters[$name] ) ) {
128                // This is the first duplicate, so transform scalar ( string )
129                // into an array so we can add the duplicates
130                $this->parameters[$name] = array( $this->parameters[$name] );
131            }
132
133            $this->parameters[$name][] = $value;
134        } else {
135            $this->parameters[$name] = $value;
136        }
137    }
138
139    public function get_parameter( $name ) {
140        return isset( $this->parameters[$name] ) ? $this->parameters[$name] : null;
141    }
142
143    public function get_parameters() {
144        return $this->parameters;
145    }
146
147    public function unset_parameter( $name ) {
148        unset( $this->parameters[$name] );
149    }
150
151    /**
152     * The request parameters, sorted and concatenated into a normalized string.
153     * @return string
154     */
155    public function get_signable_parameters() {
156        // Grab all parameters
157        $params = $this->parameters;
158
159        // Remove oauth_signature if present
160        // Ref: Spec: 9.1.1 ( "The oauth_signature parameter MUST be excluded." )
161        if ( isset( $params['oauth_signature'] ) ) {
162            unset( $params['oauth_signature'] );
163        }
164
165        return OAuthUtil::build_http_query( $params );
166    }
167
168    /**
169     * Returns the base string of this request
170     *
171     * The base string defined as the method, the url
172     * and the parameters ( normalized ), each urlencoded
173     * and the concated with &.
174     */
175    public function get_signature_base_string() {
176        $parts = array(
177            $this->get_normalized_http_method(),
178            $this->get_normalized_http_url(),
179            $this->get_signable_parameters()
180        );
181
182        $parts = OAuthUtil::urlencode_rfc3986( $parts );
183
184        return implode( '&', $parts );
185    }
186
187    /**
188     * just uppercases the http method
189     */
190    public function get_normalized_http_method() {
191        return strtoupper( $this->http_method );
192    }
193
194    /**
195     * parses the url and rebuilds it to be
196     * scheme://host/path
197     */
198    public function get_normalized_http_url() {
199        $parts = parse_url( $this->http_url );
200
201        $scheme = ( isset( $parts['scheme'] ) ) ? $parts['scheme'] : 'http';
202        $port = ( isset( $parts['port'] ) ) ? $parts['port'] : ( ( $scheme == 'https' ) ? '443' : '80' );
203        $host = ( isset( $parts['host'] ) ) ? strtolower( $parts['host'] ) : '';
204        $path = ( isset( $parts['path'] ) ) ? $parts['path'] : '';
205
206        if ( ( $scheme == 'https' && $port != '443' ) || ( $scheme == 'http' && $port != '80' ) ) {
207            $host = "$host:$port";
208        }
209
210        return "$scheme://$host$path";
211    }
212
213    /**
214     * builds a url usable for a GET request
215     */
216    public function to_url() {
217        $post_data = $this->to_postdata();
218        $out = $this->get_normalized_http_url();
219        if ( $post_data ) {
220            $out .= '?' . $post_data;
221        }
222
223        return $out;
224    }
225
226    /**
227     * builds the data one would send in a POST request
228     */
229    public function to_postdata() {
230        return OAuthUtil::build_http_query( $this->parameters );
231    }
232
233    /**
234     * builds the Authorization: header
235     */
236    public function to_header( $realm = null ) {
237        $first = true;
238        if ( $realm ) {
239            $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986( $realm ) . '"';
240            $first = false;
241        } else {
242            $out = 'Authorization: OAuth';
243        }
244        $total = array();
245        foreach ( $this->parameters as $k => $v ) {
246            if ( substr( $k, 0, 5 ) != "oauth" ) {
247                continue;
248            }
249            if ( is_array( $v ) ) {
250                throw new OAuthException( 'Arrays not supported in headers' );
251            }
252            $out .= ( $first ) ? ' ' : ',';
253            $out .= OAuthUtil::urlencode_rfc3986( $k ) . '="' . OAuthUtil::urlencode_rfc3986(
254                    $v
255                ) . '"';
256            $first = false;
257        }
258
259        return $out;
260    }
261
262    public function __toString() {
263        return $this->to_url();
264    }
265
266    public function sign_request( $signature_method, $consumer, $token ) {
267        $this->set_parameter(
268            "oauth_signature_method",
269            $signature_method->get_name(),
270            false
271        );
272        $signature = $this->build_signature( $signature_method, $consumer, $token );
273        $this->set_parameter( "oauth_signature", $signature, false );
274    }
275
276    public function build_signature( $signature_method, $consumer, $token ) {
277        $signature = $signature_method->build_signature( $this, $consumer, $token );
278
279        return $signature;
280    }
281
282    /**
283     * util function: current timestamp
284     */
285    private static function generate_timestamp() {
286        return time();
287    }
288
289    /**
290     * util function: current nonce
291     */
292    private static function generate_nonce() {
293        $mt = microtime();
294        $rand = mt_rand();
295
296        return md5( $mt . $rand ); // md5s look nicer than numbers
297    }
298}