OAuthRequest::to_url()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace Integrations\Connectors\Twitter;
4
5
// vim: foldmethod=marker
6
7
/* Generic exception class
8
 */
9
if (!class_exists('OAuthException')) {
10
    class OAuthException extends Exception
11
    {
12
        // pass
13
    }
14
}
15
16
class OAuthConsumer
17
{
18
    public $key;
19
    public $secret;
20
21
    function __construct($key, $secret, $callback_url=null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
22
    {
23
        $this->key = $key;
24
        $this->secret = $secret;
25
        $this->callback_url = $callback_url;
0 ignored issues
show
Bug introduced by
The property callback_url does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
26
    }
27
28
    function __toString()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
29
    {
30
        return "OAuthConsumer[key=$this->key,secret=$this->secret]";
31
    }
32
}
33
34
class OAuthToken
35
{
36
    // access tokens and request tokens
37
    public $key;
38
    public $secret;
39
40
    /**
41
     * key = the token
42
     * secret = the token secret
43
     */
44
    function __construct($key, $secret)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
45
    {
46
        $this->key = $key;
47
        $this->secret = $secret;
48
    }
49
50
    /**
51
     * generates the basic string serialization of a token that a server
52
     * would respond to request_token and access_token calls with
53
     */
54
    function to_string()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
55
    {
56
        return "oauth_token=" .
57
           OAuthUtil::urlencode_rfc3986($this->key) .
58
           "&oauth_token_secret=" .
59
           OAuthUtil::urlencode_rfc3986($this->secret);
60
    }
61
62
    function __toString()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
63
    {
64
        return $this->to_string();
65
    }
66
}
67
68
/**
69
 * A class for implementing a Signature Method
70
 * See section 9 ("Signing Requests") in the spec
71
 */
72
abstract class OAuthSignatureMethod
73
{
74
    /**
75
     * Needs to return the name of the Signature Method (ie HMAC-SHA1)
76
     *
77
     * @return string
78
     */
79
    abstract public function get_name();
80
81
    /**
82
     * Build up the signature
83
     * NOTE: The output of this function MUST NOT be urlencoded.
84
     * the encoding is handled in OAuthRequest when the final
85
     * request is serialized
86
     *
87
     * @param  OAuthRequest  $request
88
     * @param  OAuthConsumer $consumer
89
     * @param  OAuthToken    $token
90
     * @return string
91
     */
92
    abstract public function build_signature($request, $consumer, $token);
93
94
    /**
95
     * Verifies that a given signature is correct
96
     *
97
     * @param  OAuthRequest  $request
98
     * @param  OAuthConsumer $consumer
99
     * @param  OAuthToken    $token
100
     * @param  string        $signature
101
     * @return bool
102
     */
103
    public function check_signature($request, $consumer, $token, $signature)
104
    {
105
        $built = $this->build_signature($request, $consumer, $token);
106
        return $built == $signature;
107
    }
108
}
109
110
/**
111
 * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] 
112
 * where the Signature Base String is the text and the key is the concatenated values (each first 
113
 * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' 
114
 * character (ASCII code 38) even if empty.
115
 *   - Chapter 9.2 ("HMAC-SHA1")
116
 */
117
class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod
118
{
119
    function get_name()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
120
    {
121
        return "HMAC-SHA1";
122
    }
123
124
    public function build_signature($request, $consumer, $token)
125
    {
126
        $base_string = $request->get_signature_base_string();
127
        $request->base_string = $base_string;
128
129
        $key_parts = array(
130
        $consumer->secret,
131
        ($token) ? $token->secret : ""
132
        );
133
134
        $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
135
        $key = implode('&', $key_parts);
136
137
        return base64_encode(hash_hmac('sha1', $base_string, $key, true));
138
    }
139
}
140
141
/**
142
 * The PLAINTEXT method does not provide any security protection and SHOULD only be used 
143
 * over a secure channel such as HTTPS. It does not use the Signature Base String.
144
 *   - Chapter 9.4 ("PLAINTEXT")
145
 */
146
class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod
147
{
148
    public function get_name()
149
    {
150
        return "PLAINTEXT";
151
    }
152
153
    /**
154
     * oauth_signature is set to the concatenated encoded values of the Consumer Secret and 
155
     * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is 
156
     * empty. The result MUST be encoded again.
157
     *   - Chapter 9.4.1 ("Generating Signatures")
158
     *
159
     * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
160
     * OAuthRequest handles this!
161
     */
162
    public function build_signature($request, $consumer, $token)
163
    {
164
        $key_parts = array(
165
        $consumer->secret,
166
        ($token) ? $token->secret : ""
167
        );
168
169
        $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
170
        $key = implode('&', $key_parts);
171
        $request->base_string = $key;
172
173
        return $key;
174
    }
175
}
176
177
/**
178
 * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in 
179
 * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for 
180
 * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a 
181
 * verified way to the Service Provider, in a manner which is beyond the scope of this 
182
 * specification.
183
 *   - Chapter 9.3 ("RSA-SHA1")
184
 */
185
abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod
186
{
187
    public function get_name()
188
    {
189
        return "RSA-SHA1";
190
    }
191
192
    // Up to the SP to implement this lookup of keys. Possible ideas are:
193
    // (1) do a lookup in a table of trusted certs keyed off of consumer
194
    // (2) fetch via http using a url provided by the requester
195
    // (3) some sort of specific discovery code based on request
196
    //
197
    // Either way should return a string representation of the certificate
198
    protected abstract function fetch_public_cert(&$request);
199
200
    // Up to the SP to implement this lookup of keys. Possible ideas are:
201
    // (1) do a lookup in a table of trusted certs keyed off of consumer
202
    //
203
    // Either way should return a string representation of the certificate
204
    protected abstract function fetch_private_cert(&$request);
205
206
    public function build_signature($request, $consumer, $token)
207
    {
208
        $base_string = $request->get_signature_base_string();
209
        $request->base_string = $base_string;
210
211
        // Fetch the private key cert based on the request
212
        $cert = $this->fetch_private_cert($request);
213
214
        // Pull the private key ID from the certificate
215
        $privatekeyid = openssl_get_privatekey($cert);
216
217
        // Sign using the key
218
        $ok = openssl_sign($base_string, $signature, $privatekeyid);
0 ignored issues
show
Unused Code introduced by
$ok is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
219
220
        // Release the key resource
221
        openssl_free_key($privatekeyid);
222
223
        return base64_encode($signature);
224
    }
225
226
    public function check_signature($request, $consumer, $token, $signature)
227
    {
228
        $decoded_sig = base64_decode($signature);
229
230
        $base_string = $request->get_signature_base_string();
231
232
        // Fetch the public key cert based on the request
233
        $cert = $this->fetch_public_cert($request);
234
235
        // Pull the public key ID from the certificate
236
        $publickeyid = openssl_get_publickey($cert);
237
238
        // Check the computed signature against the one passed in the query
239
        $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
240
241
        // Release the key resource
242
        openssl_free_key($publickeyid);
243
244
        return $ok == 1;
245
    }
246
}
247
248
class OAuthRequest
249
{
250
    private $parameters;
251
    private $http_method;
252
    private $http_url;
253
    // for debug purposes
254
    public $base_string;
255
    public static $version = '1.0';
256
    public static $POST_INPUT = 'php://input';
257
258
    function __construct($http_method, $http_url, $parameters=null)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
259
    {
260
        @$parameters or $parameters = array();
261
        $parameters = array_merge(OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
262
        $this->parameters = $parameters;
263
        $this->http_method = $http_method;
264
        $this->http_url = $http_url;
265
    }
266
267
268
    /**
269
     * attempt to build up a request from what was passed to the server
270
     */
271
    public static function from_request($http_method=null, $http_url=null, $parameters=null)
272
    {
273
        $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
274
              ? 'http'
275
              : 'https';
276
        @$http_url or $http_url = $scheme .
277
                              '://' . $_SERVER['HTTP_HOST'] .
278
                              ':' .
279
                              $_SERVER['SERVER_PORT'] .
280
                              $_SERVER['REQUEST_URI'];
281
        @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
282
283
        // We weren't handed any parameters, so let's find the ones relevant to
284
        // this request.
285
        // If you run XML-RPC or similar you should use this to provide your own
286
        // parsed parameter-list
287
        if (!$parameters) {
288
            // Find request headers
289
            $request_headers = OAuthUtil::get_headers();
290
291
            // Parse the query-string to find GET parameters
292
            $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
293
294
            // It's a POST request of the proper content-type, so parse POST
295
            // parameters and add those overriding any duplicates from GET
296
            if ($http_method == "POST"
297
                && @strstr(
298
                    $request_headers["Content-Type"],
299
                    "application/x-www-form-urlencoded"
300
                )
301
            ) {
302
                $post_data = OAuthUtil::parse_parameters(
303
                    file_get_contents(self::$POST_INPUT)
304
                );
305
                $parameters = array_merge($parameters, $post_data);
306
            }
307
308
            // We have a Authorization-header with OAuth data. Parse the header
309
            // and add those overriding any duplicates from GET or POST
310
            if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
311
                $header_parameters = OAuthUtil::split_header(
312
                    $request_headers['Authorization']
313
                );
314
                $parameters = array_merge($parameters, $header_parameters);
315
            }
316
317
        }
318
319
        return new OAuthRequest($http_method, $http_url, $parameters);
320
    }
321
322
    /**
323
     * pretty much a helper function to set up the request
324
     */
325
    public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=null)
326
    {
327
        @$parameters or $parameters = array();
328
        $defaults = array("oauth_version" => OAuthRequest::$version,
329
                      "oauth_nonce" => OAuthRequest::generate_nonce(),
330
                      "oauth_timestamp" => OAuthRequest::generate_timestamp(),
331
                      "oauth_consumer_key" => $consumer->key);
332
        if ($token) {
333
            $defaults['oauth_token'] = $token->key;
334
        }
335
336
        $parameters = array_merge($defaults, $parameters);
337
338
        return new OAuthRequest($http_method, $http_url, $parameters);
339
    }
340
341
    public function set_parameter($name, $value, $allow_duplicates = true)
342
    {
343
        if ($allow_duplicates && isset($this->parameters[$name])) {
344
            // We have already added parameter(s) with this name, so add to the list
345
            if (is_scalar($this->parameters[$name])) {
346
                // This is the first duplicate, so transform scalar (string)
347
                // into an array so we can add the duplicates
348
                $this->parameters[$name] = array($this->parameters[$name]);
349
            }
350
351
            $this->parameters[$name][] = $value;
352
        } else {
353
            $this->parameters[$name] = $value;
354
        }
355
    }
356
357
    public function get_parameter($name)
358
    {
359
        return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
360
    }
361
362
    public function get_parameters()
363
    {
364
        return $this->parameters;
365
    }
366
367
    public function unset_parameter($name)
368
    {
369
        unset($this->parameters[$name]);
370
    }
371
372
    /**
373
     * The request parameters, sorted and concatenated into a normalized string.
374
     *
375
     * @return string
376
     */
377
    public function get_signable_parameters()
378
    {
379
        // Grab all parameters
380
        $params = $this->parameters;
381
382
        // Remove oauth_signature if present
383
        // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
384
        if (isset($params['oauth_signature'])) {
385
            unset($params['oauth_signature']);
386
        }
387
388
        return OAuthUtil::build_http_query($params);
389
    }
390
391
    /**
392
     * Returns the base string of this request
393
     *
394
     * The base string defined as the method, the url
395
     * and the parameters (normalized), each urlencoded
396
     * and the concated with &.
397
     */
398
    public function get_signature_base_string()
399
    {
400
        $parts = array(
401
        $this->get_normalized_http_method(),
402
        $this->get_normalized_http_url(),
403
        $this->get_signable_parameters()
404
        );
405
406
        $parts = OAuthUtil::urlencode_rfc3986($parts);
407
408
        return implode('&', $parts);
409
    }
410
411
    /**
412
     * just uppercases the http method
413
     */
414
    public function get_normalized_http_method()
415
    {
416
        return strtoupper($this->http_method);
417
    }
418
419
    /**
420
     * parses the url and rebuilds it to be
421
     * scheme://host/path
422
     */
423
    public function get_normalized_http_url()
424
    {
425
        $parts = parse_url($this->http_url);
426
427
        $port = @$parts['port'];
428
        $scheme = $parts['scheme'];
429
        $host = $parts['host'];
430
        $path = @$parts['path'];
431
432
        $port or $port = ($scheme == 'https') ? '443' : '80';
433
434
        if (($scheme == 'https' && $port != '443')
435
            || ($scheme == 'http' && $port != '80')
436
        ) {
437
            $host = "$host:$port";
438
        }
439
        return "$scheme://$host$path";
440
    }
441
442
    /**
443
     * builds a url usable for a GET request
444
     */
445
    public function to_url()
446
    {
447
        $post_data = $this->to_postdata();
448
        $out = $this->get_normalized_http_url();
449
        if ($post_data) {
450
            $out .= '?'.$post_data;
451
        }
452
        return $out;
453
    }
454
455
    /**
456
     * builds the data one would send in a POST request
457
     */
458
    public function to_postdata()
459
    {
460
        return OAuthUtil::build_http_query($this->parameters);
461
    }
462
463
    /**
464
     * builds the Authorization: header
465
     */
466
    public function to_header($realm=null)
467
    {
468
        $first = true;
469
        if($realm) {
470
                $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
471
                $first = false;
472
        } else {
473
            $out = 'Authorization: OAuth';
474
        }
475
476
        $total = array();
0 ignored issues
show
Unused Code introduced by
$total is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
477
        foreach ($this->parameters as $k => $v) {
478
            if (substr($k, 0, 5) != "oauth") { continue;
479
            }
480
            if (is_array($v)) {
481
                throw new OAuthException('Arrays not supported in headers');
482
            }
483
            $out .= ($first) ? ' ' : ',';
484
            $out .= OAuthUtil::urlencode_rfc3986($k) .
485
              '="' .
486
              OAuthUtil::urlencode_rfc3986($v) .
487
              '"';
488
            $first = false;
489
        }
490
        return $out;
491
    }
492
493
    public function __toString()
494
    {
495
        return $this->to_url();
496
    }
497
498
499
    public function sign_request($signature_method, $consumer, $token)
500
    {
501
        $this->set_parameter(
502
            "oauth_signature_method",
503
            $signature_method->get_name(),
504
            false
505
        );
506
        $signature = $this->build_signature($signature_method, $consumer, $token);
507
        $this->set_parameter("oauth_signature", $signature, false);
508
    }
509
510
    public function build_signature($signature_method, $consumer, $token)
511
    {
512
        $signature = $signature_method->build_signature($this, $consumer, $token);
513
        return $signature;
514
    }
515
516
    /**
517
     * util function: current timestamp
518
     */
519
    private static function generate_timestamp()
520
    {
521
        return time();
522
    }
523
524
    /**
525
     * util function: current nonce
526
     */
527
    private static function generate_nonce()
528
    {
529
        $mt = microtime();
530
        $rand = mt_rand();
531
532
        return md5($mt . $rand); // md5s look nicer than numbers
533
    }
534
}
535
536
class OAuthServer
537
{
538
    protected $timestamp_threshold = 300; // in seconds, five minutes
539
    protected $version = '1.0';             // hi blaine
540
    protected $signature_methods = array();
541
542
    protected $data_store;
543
544
    function __construct($data_store)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
545
    {
546
        $this->data_store = $data_store;
547
    }
548
549
    public function add_signature_method($signature_method)
550
    {
551
        $this->signature_methods[$signature_method->get_name()] =
552
        $signature_method;
553
    }
554
555
    // high level functions
556
557
    /**
558
     * process a request_token request
559
     * returns the request token on success
560
     */
561
    public function fetch_request_token(&$request)
562
    {
563
        $this->get_version($request);
564
565
        $consumer = $this->get_consumer($request);
566
567
        // no token required for the initial token request
568
        $token = null;
569
570
        $this->check_signature($request, $consumer, $token);
571
572
        // Rev A change
573
        $callback = $request->get_parameter('oauth_callback');
574
        $new_token = $this->data_store->new_request_token($consumer, $callback);
575
576
        return $new_token;
577
    }
578
579
    /**
580
     * process an access_token request
581
     * returns the access token on success
582
     */
583
    public function fetch_access_token(&$request)
584
    {
585
        $this->get_version($request);
586
587
        $consumer = $this->get_consumer($request);
588
589
        // requires authorized request token
590
        $token = $this->get_token($request, $consumer, "request");
591
592
        $this->check_signature($request, $consumer, $token);
593
594
        // Rev A change
595
        $verifier = $request->get_parameter('oauth_verifier');
596
        $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
597
598
        return $new_token;
599
    }
600
601
    /**
602
     * verify an api call, checks all the parameters
603
     */
604
    public function verify_request(&$request)
605
    {
606
        $this->get_version($request);
607
        $consumer = $this->get_consumer($request);
608
        $token = $this->get_token($request, $consumer, "access");
609
        $this->check_signature($request, $consumer, $token);
610
        return array($consumer, $token);
611
    }
612
613
    // Internals from here
614
    /**
615
     * version 1
616
     */
617
    private function get_version(&$request)
618
    {
619
        $version = $request->get_parameter("oauth_version");
620
        if (!$version) {
621
            // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. 
622
            // Chapter 7.0 ("Accessing Protected Ressources")
623
            $version = '1.0';
624
        }
625
        if ($version !== $this->version) {
626
            throw new OAuthException("OAuth version '$version' not supported");
627
        }
628
        return $version;
629
    }
630
631
    /**
632
     * figure out the signature with some defaults
633
     */
634
    private function get_signature_method(&$request)
635
    {
636
        $signature_method =
637
        @$request->get_parameter("oauth_signature_method");
638
639
        if (!$signature_method) {
640
            // According to chapter 7 ("Accessing Protected Ressources") the signature-method
641
            // parameter is required, and we can't just fallback to PLAINTEXT
642
            throw new OAuthException('No signature method parameter. This parameter is required');
643
        }
644
645
        if (!in_array(
646
            $signature_method,
647
            array_keys($this->signature_methods)
648
        )
649
        ) {
650
            throw new OAuthException(
651
                "Signature method '$signature_method' not supported " .
652
                "try one of the following: " .
653
                implode(", ", array_keys($this->signature_methods))
654
            );
655
        }
656
        return $this->signature_methods[$signature_method];
657
    }
658
659
    /**
660
     * try to find the consumer for the provided request's consumer key
661
     */
662
    private function get_consumer(&$request)
663
    {
664
        $consumer_key = @$request->get_parameter("oauth_consumer_key");
665
        if (!$consumer_key) {
666
            throw new OAuthException("Invalid consumer key");
667
        }
668
669
        $consumer = $this->data_store->lookup_consumer($consumer_key);
670
        if (!$consumer) {
671
            throw new OAuthException("Invalid consumer");
672
        }
673
674
        return $consumer;
675
    }
676
677
    /**
678
     * try to find the token for the provided request's token key
679
     */
680
    private function get_token(&$request, $consumer, $token_type="access")
681
    {
682
        $token_field = @$request->get_parameter('oauth_token');
683
        $token = $this->data_store->lookup_token(
684
            $consumer, $token_type, $token_field
685
        );
686
        if (!$token) {
687
            throw new OAuthException("Invalid $token_type token: $token_field");
688
        }
689
        return $token;
690
    }
691
692
    /**
693
     * all-in-one function to check the signature on a request
694
     * should guess the signature method appropriately
695
     */
696
    private function check_signature(&$request, $consumer, $token)
697
    {
698
        // this should probably be in a different method
699
        $timestamp = @$request->get_parameter('oauth_timestamp');
700
        $nonce = @$request->get_parameter('oauth_nonce');
701
702
        $this->check_timestamp($timestamp);
703
        $this->check_nonce($consumer, $token, $nonce, $timestamp);
704
705
        $signature_method = $this->get_signature_method($request);
706
707
        $signature = $request->get_parameter('oauth_signature');
708
        $valid_sig = $signature_method->check_signature(
709
            $request,
710
            $consumer,
711
            $token,
712
            $signature
713
        );
714
715
        if (!$valid_sig) {
716
            throw new OAuthException("Invalid signature");
717
        }
718
    }
719
720
    /**
721
     * check that the timestamp is new enough
722
     */
723
    private function check_timestamp($timestamp)
724
    {
725
        if(! $timestamp ) {
726
            throw new OAuthException(
727
                'Missing timestamp parameter. The parameter is required'
728
            );
729
        }
730
    
731
        // verify that timestamp is recentish
732
        $now = time();
733
        if (abs($now - $timestamp) > $this->timestamp_threshold) {
734
            throw new OAuthException(
735
                "Expired timestamp, yours $timestamp, ours $now"
736
            );
737
        }
738
    }
739
740
    /**
741
     * check that the nonce is not repeated
742
     */
743
    private function check_nonce($consumer, $token, $nonce, $timestamp)
744
    {
745
        if(! $nonce ) {
746
            throw new OAuthException(
747
                'Missing nonce parameter. The parameter is required'
748
            );
749
        }
750
751
        // verify that the nonce is uniqueish
752
        $found = $this->data_store->lookup_nonce(
753
            $consumer,
754
            $token,
755
            $nonce,
756
            $timestamp
757
        );
758
        if ($found) {
759
            throw new OAuthException("Nonce already used: $nonce");
760
        }
761
    }
762
763
}
764
765
class OAuthDataStore
766
{
767
    function lookup_consumer($consumer_key)
0 ignored issues
show
Unused Code introduced by
The parameter $consumer_key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
768
    {
769
        // implement me
770
    }
771
772
    function lookup_token($consumer, $token_type, $token)
0 ignored issues
show
Unused Code introduced by
The parameter $consumer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $token_type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $token is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
773
    {
774
        // implement me
775
    }
776
777
    function lookup_nonce($consumer, $token, $nonce, $timestamp)
0 ignored issues
show
Unused Code introduced by
The parameter $consumer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $token is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $nonce is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $timestamp is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
778
    {
779
        // implement me
780
    }
781
782
    function new_request_token($consumer, $callback = null)
0 ignored issues
show
Unused Code introduced by
The parameter $consumer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $callback is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
783
    {
784
        // return a new token attached to this consumer
785
    }
786
787
    function new_access_token($token, $consumer, $verifier = null)
0 ignored issues
show
Unused Code introduced by
The parameter $token is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $consumer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $verifier is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
788
    {
789
        // return a new access token attached to this consumer
790
        // for the user associated with this token if the request token
791
        // is authorized
792
        // should also invalidate the request token
793
    }
794
795
}
796
797
class OAuthUtil
798
{
799
    public static function urlencode_rfc3986($input)
800
    {
801
        if (is_array($input)) {
802
            return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
803
        } else if (is_scalar($input)) {
804
            return str_replace(
805
                '+',
806
                ' ',
807
                str_replace('%7E', '~', rawurlencode($input))
808
            );
809
        } else {
810
            return '';
811
        }
812
    }
813
814
815
    // This decode function isn't taking into consideration the above
816
    // modifications to the encoding process. However, this method doesn't
817
    // seem to be used anywhere so leaving it as is.
818
    public static function urldecode_rfc3986($string)
819
    {
820
        return urldecode($string);
821
    }
822
823
    // Utility function for turning the Authorization: header into
824
    // parameters, has to do some unescaping
825
    // Can filter out any non-oauth parameters if needed (default behaviour)
826
    public static function split_header($header, $only_allow_oauth_parameters = true)
827
    {
828
        $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
829
        $offset = 0;
830
        $params = array();
831
        while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
832
            $match = $matches[0];
833
            $header_name = $matches[2][0];
834
            $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
835
            if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
836
                $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
837
            }
838
            $offset = $match[1] + strlen($match[0]);
839
        }
840
841
        if (isset($params['realm'])) {
842
            unset($params['realm']);
843
        }
844
845
        return $params;
846
    }
847
848
    // helper to try to sort out headers for people who aren't running apache
849
    public static function get_headers()
850
    {
851
        if (function_exists('apache_request_headers')) {
852
            // we need this to get the actual Authorization: header
853
            // because apache tends to tell us it doesn't exist
854
            $headers = apache_request_headers();
855
856
            // sanitize the output of apache_request_headers because
857
            // we always want the keys to be Cased-Like-This and arh()
858
            // returns the headers in the same case as they are in the
859
            // request
860
            $out = array();
861
            foreach( $headers AS $key => $value ) {
862
                $key = str_replace(
863
                    " ",
864
                    "-",
865
                    ucwords(strtolower(str_replace("-", " ", $key)))
866
                );
867
                $out[$key] = $value;
868
            }
869
        } else {
870
            // otherwise we don't have apache and are just going to have to hope
871
            // that $_SERVER actually contains what we need
872
            $out = array();
873
            if(isset($_SERVER['CONTENT_TYPE']) ) {
874
                $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
875
            }
876
            if(isset($_ENV['CONTENT_TYPE']) ) {
877
                $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
878
            }
879
880
            foreach ($_SERVER as $key => $value) {
881
                if (substr($key, 0, 5) == "HTTP_") {
882
                    // this is chaos, basically it is just there to capitalize the first
883
                    // letter of every word that is not an initial HTTP and strip HTTP
884
                    // code from przemek
885
                    $key = str_replace(
886
                        " ",
887
                        "-",
888
                        ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
889
                    );
890
                    $out[$key] = $value;
891
                }
892
            }
893
        }
894
        return $out;
895
    }
896
897
    // This function takes a input like a=b&a=c&d=e and returns the parsed
898
    // parameters like this
899
    // array('a' => array('b','c'), 'd' => 'e')
900
    public static function parse_parameters( $input )
901
    {
902
        if (!isset($input) || !$input) { return array();
903
        }
904
905
        $pairs = explode('&', $input);
906
907
        $parsed_parameters = array();
908
        foreach ($pairs as $pair) {
909
            $split = explode('=', $pair, 2);
910
            $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
911
            $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
912
913
            if (isset($parsed_parameters[$parameter])) {
914
                // We have already recieved parameter(s) with this name, so add to the list
915
                // of parameters with this name
916
917
                if (is_scalar($parsed_parameters[$parameter])) {
918
                    // This is the first duplicate, so transform scalar (string) into an array
919
                    // so we can add the duplicates
920
                    $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
921
                }
922
923
                $parsed_parameters[$parameter][] = $value;
924
            } else {
925
                $parsed_parameters[$parameter] = $value;
926
            }
927
        }
928
        return $parsed_parameters;
929
    }
930
931
    public static function build_http_query($params)
932
    {
933
        if (!$params) { return '';
934
        }
935
936
        // Urlencode both keys and values
937
        $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
938
        $values = OAuthUtil::urlencode_rfc3986(array_values($params));
939
        $params = array_combine($keys, $values);
940
941
        // Parameters are sorted by name, using lexicographical byte value ordering.
942
        // Ref: Spec: 9.1.1 (1)
943
        uksort($params, 'strcmp');
944
945
        $pairs = array();
946
        foreach ($params as $parameter => $value) {
947
            if (is_array($value)) {
948
                // If two or more parameters share the same name, they are sorted by their value
949
                // Ref: Spec: 9.1.1 (1)
950
                natsort($value);
951
                foreach ($value as $duplicate_value) {
952
                    $pairs[] = $parameter . '=' . $duplicate_value;
953
                }
954
            } else {
955
                $pairs[] = $parameter . '=' . $value;
956
            }
957
        }
958
        // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
959
        // Each name-value pair is separated by an '&' character (ASCII code 38)
960
        return implode('&', $pairs);
961
    }
962
}
963