Compare commits

...

79 Commits

Author SHA1 Message Date
Jacob Kiers 95477a7710 Merge pull request #9 from jacobkiers/update-travis-configuration
Update travis configuration: test PHP 5.3-5.6; 7 and HHVM.
2015-10-02 15:50:51 +02:00
Jacob Kiers e507a5dc8b Update travis configuration
Test more PHP versions, in order to maintain compatibility. Also updates
the composer install command.

Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2015-10-02 15:46:24 +02:00
Jacob Kiers 11f934ee88 Merge pull request #8 from jacobkiers/fix-7-abstract-class
Fix interface inheritance for PHP 5.3.0 - 5.3.8
2015-10-02 15:43:01 +02:00
Jacob Kiers 3099dcf617 Maintain compatibility for PHP 5.3.0 - 5.3.8
Interface inheritance is not possible before PHP 5.3.9. That is an
artefact of PHP bug 43200 (https://bugs.php.net/43200).

Since the composer.json states that the PHP versions >= 5.3.0 are
supported, we have to maintain compatibility with PHP 5.3.X.

Fixes: #7

Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2015-10-02 15:34:34 +02:00
Jacob Kiers d2b3281815 Release 1.0.11
This release fixes a little bug in the exception that is thrown when an
invalid token is encountered.

Reported and fixed by @elieux.

Signed-off-by: Jacob Kiers <jacob@alphacomm.nl>
2015-07-16 16:06:29 +02:00
Jacob Kiers 999a007b3b Merge pull request #6 from elieux/master-fix1
Fix variable name when creating an "invalid token" exception
2015-07-16 16:04:50 +02:00
David Macek 3ef8e25bdb Fix variable name when creating an "invalid token" exception 2015-07-16 15:53:27 +02:00
Jacob Kiers 4f352302a1 Made version parameter private (by @vimishor).
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2014-02-21 18:39:51 +01:00
Alexandru G cc7992f2d1 oauth_version parameter should be optional 2014-02-17 15:43:13 +02:00
Jacob Kiers 5616fa0756 Updated CONTRIBUTORS.md and started a change log.
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2013-11-19 21:24:12 +01:00
Jacob Kiers f8ffcd6f87 Added tests for new required 'oauth_consumer_key'.
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2013-11-19 21:21:41 +01:00
Jacob Kiers e4e8bc2f90 Merge remote-tracking branch 'vb/master' 2013-11-19 21:03:00 +01:00
victorbjelkholm 4011b3674d Throwing OAuthException without oauth_consumer_key 2013-11-19 20:58:34 +01:00
victorbjelkholm fdb6c2df49 Update travis.yml 2013-11-19 20:57:18 +01:00
victorbjelkholm da8c3c46c5 Remove parameter should be passed reference 2013-11-19 20:54:56 +01:00
victorbjelkholm cec7f31cda Change colors=false to colors=true 2013-11-19 20:54:56 +01:00
Jacob Kiers ca5c3596dc Added .gitattributes
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2013-08-15 19:59:33 +02:00
Jacob Kiers b3d4f9b6bc Added CONTRIBUTORS.md file.
Signed-off-by: Jacob Kiers <jacob@jacobkiers.net>
2013-06-06 05:58:53 +02:00
Alexandru G 087cb1278f Because `$token` refs an object, this statement will always be true. Therefore an empty `oauth_token` param will be added to each request, wich will break Bitbucket authorization process.
To fix this, we need to check if token key is empty and ignore it.
2013-06-06 05:58:53 +02:00
Jacob Kiers 6677c1da7d The output of Server::verifyToken is now a hash. 2013-02-15 12:09:56 +00:00
Jacob Kiers 097b0af7b4 Merge pull request #1 from jacobkiers/feature/use-interfaces
Use Interfaces everywhere.
2013-02-11 06:29:16 -08:00
Jacob Kiers 8bd355f556 Use Interfaces anywhere.
In practice, there were still too many concrete classes, which makes
integration into a framework hard. To overcome this, the codebase has
been refactored to use Interfaces when a resource is needed.

All necessary Interfaces have been created, and the existing concrete
classes now implement these interfaces.
2013-02-11 14:02:14 +00:00
Jacob Kiers 4e6cc6c811 Bugfix in Server and CS Fixes. 2013-02-11 09:30:45 +00:00
Jacob Kiers 73c99e3652 Fix SignatureMethodTest to use RequestInterface. 2013-02-08 15:28:03 +00:00
Jacob Kiers 646e466639 Also use RequestInterface in Signature Methods. 2013-02-08 15:26:42 +00:00
Jacob Kiers e8edc17196 Have the Server use RequestInterface. 2013-02-08 15:14:38 +00:00
Jacob Kiers 74fd426e45 Also build against PHP 5.5.
But a failing build agains PHP 5.5 should not fail the whole build.
2013-02-08 11:09:37 +00:00
Jacob Kiers be09ba0216 Added RequestInterface to make integration easier.
Since now only a RequestInterface is needed instead of a Request object,
it becomes easier to use the Server component with a different Request
object, such as the Symfony or Zend Framework Request objects.

This will now only need a small wrapper, instead of extending and
rewriting the existing Request object.
2013-02-08 10:57:53 +00:00
Jacob Kiers c471cd2b8d Renamed DataStore to DataStoreInterface
Also, the Interface now contains comments on the expected return values.
2013-02-08 10:37:21 +00:00
Jacob Kiers 204ec8b42f Renamed namespace to JacobKiers 2013-02-08 10:32:26 +00:00
Jacob Kiers 81a1af738b Update information so the fork won't create issues
Just some updates to the Composer and Travis configuration and the
LICENSE file.
2013-02-06 22:39:13 +00:00
GaryJones 354f31fd6d Stop emails from Travis notifications. 2012-12-04 02:12:35 +00:00
GaryJones 49ecd40ba5 Undo travis config simplification, as tests have now passed in the meantime. 2012-12-04 02:07:00 +00:00
GaryJones a404154b2c Simplify Travis config file to get it working. 2012-12-04 02:02:40 +00:00
GaryJones 2890e3cd33 Add Travis build status image to readme. 2012-12-04 01:58:08 +00:00
GaryJones d5b23e95bb Update gitignore file 2012-12-04 01:28:58 +00:00
GaryJones 391a3f2a91 Add travis config file 2012-12-04 01:28:30 +00:00
GaryJones 87a4b17394 Refactor common code for getting the signature key. 2012-11-23 12:30:38 +00:00
GaryJones a5ba220ae1 Add tests for concrete method inside abstract SignatureMethod class. 2012-11-23 12:22:04 +00:00
GaryJones 7457f612b7 Add test for NullToken. 2012-11-23 12:20:23 +00:00
GaryJones d94446782a Rename OAuth\Exception back to OAuthException, to follow the standard PHP way of naming predefined / SPL exceptions. 2012-11-22 18:11:12 +00:00
GaryJones f8be34b748 Typo 2012-11-22 17:47:43 +00:00
GaryJones fce57a4e6e Add new NullToken, to satisfy type hint checks when token would otherwise not be set. 2012-11-22 17:37:12 +00:00
GaryJones af1993ac3a Make the Token arg of buildSignature() methods optional.
RSA-SHA1 buildSignature() doesn't use it at all, and the other two allow for there to be no token, as on the initial client request. The token shared-secret as taken to be an empty string.
2012-11-22 16:27:47 +00:00
GaryJones bfabd20ae4 Fix bug where the colon between host and port would appear, even when port was an empty string. 2012-11-22 16:25:34 +00:00
GaryJones 52d9317cfd Add unit tests for signature methods, and a couple for Request. 2012-11-22 16:24:21 +00:00
GaryJones b564cbd103 Change line endings...again :-/ 2012-11-22 13:24:15 +00:00
GaryJones 1f05207957 Add basic client and token unit tests. 2012-11-22 13:18:56 +00:00
GaryJones 7ef4283511 Limit phpunit dev dependency to stable. 2012-11-22 13:18:34 +00:00
GaryJones a58bceb4aa Make callback_url property protected, and add get/set methods. 2012-11-22 13:17:23 +00:00
GaryJones b06d99ccbf Everything documented, including clearer author attribution.
Renamed a few classes to rely more on OAuth namespace, instead of OAuth class prefix.
2012-11-21 11:10:57 +00:00
GaryJones 92cd49d385 Clarify license as MIT.
Include a LICENSE file, giving original work copyright to Andy Smith, and modifications by Gary Jones.
2012-11-19 17:55:49 +00:00
Gary Jones e233a83f43 Add current status note to readme. 2012-11-18 12:29:31 +00:00
Gary Jones e8cd1bdc22 Add phpunit.xml config file. 2012-11-18 12:28:10 +00:00
Gary Jones 68dcff84a4 Add some require-dev dependencies. 2012-11-18 12:27:26 +00:00
Gary Jones 12afd40c6e Change all references to consumer to client, as per RFC 5849. 2012-11-18 02:33:29 +00:00
Gary Jones 4d5cfee737 Add trailing slash to PSR-0 value. 2012-11-18 01:40:40 +00:00
Gary Jones abe9e182cb Fix incorrect namespace. 2012-11-18 01:33:27 +00:00
Gary Jones 36add4e946 Use single quotes for simple strings. 2012-11-18 01:32:38 +00:00
Gary Jones bebf65bfed Fix issue for PHP 5.4.8 where the host part from parse_url seems to also include the port as well. 2012-11-18 01:18:11 +00:00
Gary Jones dc7230cbb4 Fix old array_map() reference. 2012-11-17 23:14:25 +00:00
Gary Jones f896020b35 Change static calls from method_names to methodNames. 2012-11-17 22:33:43 +00:00
Gary Jones 244bd74225 Rename vendor and package in composer to be lowercase. 2012-11-17 22:03:48 +00:00
Gary Jones fbe80f2624 Oops, removing Netbeans project directory. 2012-11-17 21:24:13 +00:00
Gary Jones 63b4a0f58d Made package PSR-2 compatible.
Since this is a fork, also renamed the namespaces and containing folder.
2012-11-17 21:13:44 +00:00
Eher 6a15224e34 Merge branch 'master' of github.com:EHER/OAuth 2012-04-03 11:16:07 -03:00
Eher c434283f5f Psr-0 updated. 2012-04-03 11:13:50 -03:00
Eher 815a86af6e Alteração do psr-0 2012-04-03 11:00:41 -03:00
Eher 26a92abbdc Refactoring 2012-02-13 01:52:03 -02:00
Eher 2feec37a98 Composer.json updated 2012-02-12 15:39:42 -02:00
Eher 17ca454bb8 Composer.json updated 2012-02-12 15:17:48 -02:00
Eher 4344315960 Composer.json updated 2012-02-12 15:10:02 -02:00
Eher 0849d47418 psr-0 namespace configured 2012-02-12 13:39:28 -02:00
Eher b1cea0857a Merge branch 'master' of github.com:EHER/OAuth
Conflicts:
	.gitignore
	composer.json
2012-02-09 13:03:36 -02:00
Eher 9d62ccdb86 Compuser json fixed 2012-02-09 08:33:29 -02:00
Eher 0efb3a5dfa Ignore some compuser and vim files 2012-02-09 08:33:01 -02:00
Eher 9cc377d653 SignatureMethod removed from namespace 2012-02-08 23:35:31 -02:00
Eher 67680e1564 Composer project data fixed 2012-02-08 22:44:20 -02:00
Eher 2187cd8b44 Ignore files 2012-02-08 22:42:21 -02:00
43 changed files with 2383 additions and 924 deletions

14
.gitattributes vendored Normal file
View File

@ -0,0 +1,14 @@
# Text files.
*.ini text
*.json text
*.md text
*.php text diff=php
*.phtml text diff=html
*.xml text
# Binary files.
*.alaw binary
*.gif binary
*.phar binary
*.png binary
*.wav binary

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.swp
composer.phar
composer.lock
vendor
nbproject

15
.travis.yml Normal file
View File

@ -0,0 +1,15 @@
sudo: false
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7
- hhvm
before_script:
- composer selfupdate --quiet
- composer install --prefer-dist
- vendor/phpunit/phpunit/composer/bin/phpunit

10
CHANGELOG.md Normal file
View File

@ -0,0 +1,10 @@
Version 1.0.10 (2014-02-21)
* Made version parameter optional (@vimishor)
Version 1.0.9 (2013-11-19)
* Merged changes by @VictorBjelkholm. This results in a BC break:
Request\Request now requires the 'oauth_consumer_key' as a parameter
in the contructor.

11
CONTRIBUTORS.md Normal file
View File

@ -0,0 +1,11 @@
This project has had multiple authors. In this file, I will try to credit
each of them. If you are not listed for some reason, please ask.
Andy Smith authored the original code (http://oauth.googlecode.com/svn/code/php/).
* Alexandre Eher (@EHER)
* Gary Jones (@GaryJones)
* Jacob Kiers (@jacobkiers)
* Alexandru G. (@vimishor)
* Victor Bjelkholm (@VictorBjelkholm)
* David Macek (@elieux)

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
Original work Copyright (c) 2007 Andy Smith
Modified work Copyright (c) 2012 Gary Jones
Modified work Copyright (c) 2013 Jacob Kiers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,3 +1,9 @@
#OAuth 1 PHP Library
Based on [Andy Smith's](http://term.ie/) [basic php library](http://oauth.googlecode.com/svn/code/php/) for OAuth.
Actually forked from [EHER's version](https://github.com/EHER/OAuth).
[![Build Status](https://travis-ci.org/jacobkiers/OAuth.png)](https://travis-ci.org/jacobkiers/OAuth)
Current status - Request token part seems to be working - nothing else properly tested yet.

View File

@ -1,18 +1,41 @@
{
"name": "Eher/OAuth",
"name": "jacobkiers/oauth",
"description": "Based on Andy Smith's basic PHP library for OAuth 1.0a",
"type": "library",
"description": "OAuth 1 PHP Library",
"keywords": ["oauth"],
"homepage": "https://github.com/EHER/OAuth",
"psr-0": {
"OAuth": "src/"
},
"authors":
{
"name": "Andy Smith",
"homepage": "http://term.ie/"
},
"homepage": "https://github.com/jacobkiers/OAuth",
"license": "MIT",
"authors": [
{
"name": "Jacob Kiers",
"email": "jacob@alphacomm.nl",
"role": "developer"
},
{
"name": "Gary Jones",
"email": "gary@garyjones.co.uk",
"role": "developer"
},
{
"name": "Alexandre Eher",
"role": "Composer packager"
},
{
"name": "Andy Smith",
"role": "original author"
}
],
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"squizlabs/php_codesniffer": "*",
"phpunit/phpunit": "3.7.*@stable",
"mockery/mockery": "*"
},
"autoload": {
"psr-0": {
"JacobKiers\\OAuth": "src/"
}
}
}

18
phpunit.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="OAuth Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -1,19 +0,0 @@
<?php
namespace Eher\OAuth;
class OAuthConsumer {
public $key;
public $secret;
function __construct($key, $secret, $callback_url=NULL) {
$this->key = $key;
$this->secret = $secret;
$this->callback_url = $callback_url;
}
function __toString() {
return "OAuthConsumer[key=$this->key,secret=$this->secret]";
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace Eher\OAuth;
class OAuthDataStore {
function lookup_consumer($consumer_key) {
// implement me
}
function lookup_token($consumer, $token_type, $token) {
// implement me
}
function lookup_nonce($consumer, $token, $nonce, $timestamp) {
// implement me
}
function new_request_token($consumer, $callback = null) {
// return a new token attached to this consumer
}
function new_access_token($token, $consumer, $verifier = null) {
// return a new access token attached to this consumer
// for the user associated with this token if the request token
// is authorized
// should also invalidate the request token
}
}

View File

@ -1,9 +0,0 @@
<?php
namespace Eher\OAuth;
/* Generic exception class
*/
class OAuthException extends \Exception {
// pass
}

View File

@ -1,264 +0,0 @@
<?php
namespace Eher\OAuth;
class OAuthRequest {
protected $parameters;
protected $http_method;
protected $http_url;
// for debug purposes
public $base_string;
public static $version = '1.0';
public static $POST_INPUT = 'php://input';
function __construct($http_method, $http_url, $parameters=NULL) {
$parameters = ($parameters) ? $parameters : array();
$parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
$this->parameters = $parameters;
$this->http_method = $http_method;
$this->http_url = $http_url;
}
/**
* attempt to build up a request from what was passed to the server
*/
public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
$scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
? 'http'
: 'https';
$http_url = ($http_url) ? $http_url : $scheme .
'://' . $_SERVER['HTTP_HOST'] .
':' .
$_SERVER['SERVER_PORT'] .
$_SERVER['REQUEST_URI'];
$http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
// We weren't handed any parameters, so let's find the ones relevant to
// this request.
// If you run XML-RPC or similar you should use this to provide your own
// parsed parameter-list
if (!$parameters) {
// Find request headers
$request_headers = OAuthUtil::get_headers();
// Parse the query-string to find GET parameters
$parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
// It's a POST request of the proper content-type, so parse POST
// parameters and add those overriding any duplicates from GET
if ($http_method == "POST"
&& isset($request_headers['Content-Type'])
&& strstr($request_headers['Content-Type'],
'application/x-www-form-urlencoded')
) {
$post_data = OAuthUtil::parse_parameters(
file_get_contents(self::$POST_INPUT)
);
$parameters = array_merge($parameters, $post_data);
}
// We have a Authorization-header with OAuth data. Parse the header
// and add those overriding any duplicates from GET or POST
if (isset($request_headers['Authorization']) && substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
$header_parameters = OAuthUtil::split_header(
$request_headers['Authorization']
);
$parameters = array_merge($parameters, $header_parameters);
}
}
return new OAuthRequest($http_method, $http_url, $parameters);
}
/**
* pretty much a helper function to set up the request
*/
public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
$parameters = ($parameters) ? $parameters : array();
$defaults = array("oauth_version" => OAuthRequest::$version,
"oauth_nonce" => OAuthRequest::generate_nonce(),
"oauth_timestamp" => OAuthRequest::generate_timestamp(),
"oauth_consumer_key" => $consumer->key);
if ($token)
$defaults['oauth_token'] = $token->key;
$parameters = array_merge($defaults, $parameters);
return new OAuthRequest($http_method, $http_url, $parameters);
}
public function set_parameter($name, $value, $allow_duplicates = true) {
if ($allow_duplicates && isset($this->parameters[$name])) {
// We have already added parameter(s) with this name, so add to the list
if (is_scalar($this->parameters[$name])) {
// This is the first duplicate, so transform scalar (string)
// into an array so we can add the duplicates
$this->parameters[$name] = array($this->parameters[$name]);
}
$this->parameters[$name][] = $value;
} else {
$this->parameters[$name] = $value;
}
}
public function get_parameter($name) {
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
}
public function get_parameters() {
return $this->parameters;
}
public function unset_parameter($name) {
unset($this->parameters[$name]);
}
/**
* The request parameters, sorted and concatenated into a normalized string.
* @return string
*/
public function get_signable_parameters() {
// Grab all parameters
$params = $this->parameters;
// Remove oauth_signature if present
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
if (isset($params['oauth_signature'])) {
unset($params['oauth_signature']);
}
return OAuthUtil::build_http_query($params);
}
/**
* Returns the base string of this request
*
* The base string defined as the method, the url
* and the parameters (normalized), each urlencoded
* and the concated with &.
*/
public function get_signature_base_string() {
$parts = array(
$this->get_normalized_http_method(),
$this->get_normalized_http_url(),
$this->get_signable_parameters()
);
$parts = OAuthUtil::urlencode_rfc3986($parts);
return implode('&', $parts);
}
/**
* just uppercases the http method
*/
public function get_normalized_http_method() {
return strtoupper($this->http_method);
}
/**
* parses the url and rebuilds it to be
* scheme://host/path
*/
public function get_normalized_http_url() {
$parts = parse_url($this->http_url);
$scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
$port = (isset($parts['port'])) ? $parts['port'] : (($scheme == 'https') ? '443' : '80');
$host = (isset($parts['host'])) ? $parts['host'] : '';
$path = (isset($parts['path'])) ? $parts['path'] : '';
if (($scheme == 'https' && $port != '443')
|| ($scheme == 'http' && $port != '80')) {
$host = "$host:$port";
}
return "$scheme://$host$path";
}
/**
* builds a url usable for a GET request
*/
public function to_url() {
$post_data = $this->to_postdata();
$out = $this->get_normalized_http_url();
if ($post_data) {
$out .= '?'.$post_data;
}
return $out;
}
/**
* builds the data one would send in a POST request
*/
public function to_postdata() {
return OAuthUtil::build_http_query($this->parameters);
}
/**
* builds the Authorization: header
*/
public function to_header($realm=null) {
$first = true;
if($realm) {
$out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
$first = false;
} else
$out = 'Authorization: OAuth';
$total = array();
foreach ($this->parameters as $k => $v) {
if (substr($k, 0, 5) != "oauth") continue;
if (is_array($v)) {
throw new OAuthException('Arrays not supported in headers');
}
$out .= ($first) ? ' ' : ',';
$out .= OAuthUtil::urlencode_rfc3986($k) .
'="' .
OAuthUtil::urlencode_rfc3986($v) .
'"';
$first = false;
}
return $out;
}
public function __toString() {
return $this->to_url();
}
public function sign_request($signature_method, $consumer, $token) {
$this->set_parameter(
"oauth_signature_method",
$signature_method->get_name(),
false
);
$signature = $this->build_signature($signature_method, $consumer, $token);
$this->set_parameter("oauth_signature", $signature, false);
}
public function build_signature($signature_method, $consumer, $token) {
$signature = $signature_method->build_signature($this, $consumer, $token);
return $signature;
}
/**
* util function: current timestamp
*/
private static function generate_timestamp() {
return time();
}
/**
* util function: current nonce
*/
private static function generate_nonce() {
$mt = microtime();
$rand = mt_rand();
return md5($mt . $rand); // md5s look nicer than numbers
}
}

View File

@ -1,225 +0,0 @@
<?php
namespace Eher\OAuth;
class OAuthServer {
protected $timestamp_threshold = 300; // in seconds, five minutes
protected $version = '1.0'; // hi blaine
protected $signature_methods = array();
protected $data_store;
function __construct($data_store) {
$this->data_store = $data_store;
}
public function add_signature_method($signature_method) {
$this->signature_methods[$signature_method->get_name()] =
$signature_method;
}
// high level functions
/**
* process a request_token request
* returns the request token on success
*/
public function fetch_request_token(&$request) {
$this->get_version($request);
$consumer = $this->get_consumer($request);
// no token required for the initial token request
$token = NULL;
$this->check_signature($request, $consumer, $token);
// Rev A change
$callback = $request->get_parameter('oauth_callback');
$new_token = $this->data_store->new_request_token($consumer, $callback);
return $new_token;
}
/**
* process an access_token request
* returns the access token on success
*/
public function fetch_access_token(&$request) {
$this->get_version($request);
$consumer = $this->get_consumer($request);
// requires authorized request token
$token = $this->get_token($request, $consumer, "request");
$this->check_signature($request, $consumer, $token);
// Rev A change
$verifier = $request->get_parameter('oauth_verifier');
$new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
return $new_token;
}
/**
* verify an api call, checks all the parameters
*/
public function verify_request(&$request) {
$this->get_version($request);
$consumer = $this->get_consumer($request);
$token = $this->get_token($request, $consumer, "access");
$this->check_signature($request, $consumer, $token);
return array($consumer, $token);
}
// Internals from here
/**
* version 1
*/
private function get_version(&$request) {
$version = $request->get_parameter("oauth_version");
if (!$version) {
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
// Chapter 7.0 ("Accessing Protected Ressources")
$version = '1.0';
}
if ($version !== $this->version) {
throw new OAuthException("OAuth version '$version' not supported");
}
return $version;
}
/**
* figure out the signature with some defaults
*/
private function get_signature_method($request) {
$signature_method = $request instanceof OAuthRequest
? $request->get_parameter("oauth_signature_method")
: NULL;
if (!$signature_method) {
// According to chapter 7 ("Accessing Protected Ressources") the signature-method
// parameter is required, and we can't just fallback to PLAINTEXT
throw new OAuthException('No signature method parameter. This parameter is required');
}
if (!in_array($signature_method,
array_keys($this->signature_methods))) {
throw new OAuthException(
"Signature method '$signature_method' not supported " .
"try one of the following: " .
implode(", ", array_keys($this->signature_methods))
);
}
return $this->signature_methods[$signature_method];
}
/**
* try to find the consumer for the provided request's consumer key
*/
private function get_consumer($request) {
$consumer_key = $request instanceof OAuthRequest
? $request->get_parameter("oauth_consumer_key")
: NULL;
if (!$consumer_key) {
throw new OAuthException("Invalid consumer key");
}
$consumer = $this->data_store->lookup_consumer($consumer_key);
if (!$consumer) {
throw new OAuthException("Invalid consumer");
}
return $consumer;
}
/**
* try to find the token for the provided request's token key
*/
private function get_token($request, $consumer, $token_type="access") {
$token_field = $request instanceof OAuthRequest
? $request->get_parameter('oauth_token')
: NULL;
$token = $this->data_store->lookup_token(
$consumer, $token_type, $token_field
);
if (!$token) {
throw new OAuthException("Invalid $token_type token: $token_field");
}
return $token;
}
/**
* all-in-one function to check the signature on a request
* should guess the signature method appropriately
*/
private function check_signature($request, $consumer, $token) {
// this should probably be in a different method
$timestamp = $request instanceof OAuthRequest
? $request->get_parameter('oauth_timestamp')
: NULL;
$nonce = $request instanceof OAuthRequest
? $request->get_parameter('oauth_nonce')
: NULL;
$this->check_timestamp($timestamp);
$this->check_nonce($consumer, $token, $nonce, $timestamp);
$signature_method = $this->get_signature_method($request);
$signature = $request->get_parameter('oauth_signature');
$valid_sig = $signature_method->check_signature(
$request,
$consumer,
$token,
$signature
);
if (!$valid_sig) {
throw new OAuthException("Invalid signature");
}
}
/**
* check that the timestamp is new enough
*/
private function check_timestamp($timestamp) {
if( ! $timestamp )
throw new OAuthException(
'Missing timestamp parameter. The parameter is required'
);
// verify that timestamp is recentish
$now = time();
if (abs($now - $timestamp) > $this->timestamp_threshold) {
throw new OAuthException(
"Expired timestamp, yours $timestamp, ours $now"
);
}
}
/**
* check that the nonce is not repeated
*/
private function check_nonce($consumer, $token, $nonce, $timestamp) {
if( ! $nonce )
throw new OAuthException(
'Missing nonce parameter. The parameter is required'
);
// verify that the nonce is uniqueish
$found = $this->data_store->lookup_nonce(
$consumer,
$token,
$nonce,
$timestamp
);
if ($found) {
throw new OAuthException("Nonce already used: $nonce");
}
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace OAuth;
class OAuthToken {
// access tokens and request tokens
public $key;
public $secret;
/**
* key = the token
* secret = the token secret
*/
function __construct($key, $secret) {
$this->key = $key;
$this->secret = $secret;
}
/**
* generates the basic string serialization of a token that a server
* would respond to request_token and access_token calls with
*/
function to_string() {
return "oauth_token=" .
OAuthUtil::urlencode_rfc3986($this->key) .
"&oauth_token_secret=" .
OAuthUtil::urlencode_rfc3986($this->secret);
}
function __toString() {
return $this->to_string();
}
}

View File

@ -1,155 +0,0 @@
<?php
namespace OAuth;
class OAuthUtil {
public static function urlencode_rfc3986($input) {
if (is_array($input)) {
return array_map(array('app\models\oauth\OAuthUtil', 'urlencode_rfc3986'), $input);
} else if (is_scalar($input)) {
return str_replace(
'+',
' ',
str_replace('%7E', '~', rawurlencode($input))
);
} else {
return '';
}
}
// This decode function isn't taking into consideration the above
// modifications to the encoding process. However, this method doesn't
// seem to be used anywhere so leaving it as is.
public static function urldecode_rfc3986($string) {
return urldecode($string);
}
// Utility function for turning the Authorization: header into
// parameters, has to do some unescaping
// Can filter out any non-oauth parameters if needed (default behaviour)
// May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
// see http://code.google.com/p/oauth/issues/detail?id=163
public static function split_header($header, $only_allow_oauth_parameters = true) {
$params = array();
if (preg_match_all('/('.($only_allow_oauth_parameters ? 'oauth_' : '').'[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches)) {
foreach ($matches[1] as $i => $h) {
$params[$h] = OAuthUtil::urldecode_rfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
}
if (isset($params['realm'])) {
unset($params['realm']);
}
}
return $params;
}
// helper to try to sort out headers for people who aren't running apache
public static function get_headers() {
if (function_exists('apache_request_headers')) {
// we need this to get the actual Authorization: header
// because apache tends to tell us it doesn't exist
$headers = apache_request_headers();
// sanitize the output of apache_request_headers because
// we always want the keys to be Cased-Like-This and arh()
// returns the headers in the same case as they are in the
// request
$out = array();
foreach ($headers AS $key => $value) {
$key = str_replace(
" ",
"-",
ucwords(strtolower(str_replace("-", " ", $key)))
);
$out[$key] = $value;
}
} else {
// otherwise we don't have apache and are just going to have to hope
// that $_SERVER actually contains what we need
$out = array();
if( isset($_SERVER['CONTENT_TYPE']) )
$out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
if( isset($_ENV['CONTENT_TYPE']) )
$out['Content-Type'] = $_ENV['CONTENT_TYPE'];
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == "HTTP_") {
// this is chaos, basically it is just there to capitalize the first
// letter of every word that is not an initial HTTP and strip HTTP
// code from przemek
$key = str_replace(
" ",
"-",
ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
);
$out[$key] = $value;
}
}
}
return $out;
}
// This function takes a input like a=b&a=c&d=e and returns the parsed
// parameters like this
// array('a' => array('b','c'), 'd' => 'e')
public static function parse_parameters( $input ) {
if (!isset($input) || !$input) return array();
$pairs = explode('&', $input);
$parsed_parameters = array();
foreach ($pairs as $pair) {
$split = explode('=', $pair, 2);
$parameter = OAuthUtil::urldecode_rfc3986($split[0]);
$value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
if (isset($parsed_parameters[$parameter])) {
// We have already recieved parameter(s) with this name, so add to the list
// of parameters with this name
if (is_scalar($parsed_parameters[$parameter])) {
// This is the first duplicate, so transform scalar (string) into an array
// so we can add the duplicates
$parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
}
$parsed_parameters[$parameter][] = $value;
} else {
$parsed_parameters[$parameter] = $value;
}
}
return $parsed_parameters;
}
public static function build_http_query($params) {
if (!$params) return '';
// Urlencode both keys and values
$keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
$values = OAuthUtil::urlencode_rfc3986(array_values($params));
$params = array_combine($keys, $values);
// Parameters are sorted by name, using lexicographical byte value ordering.
// Ref: Spec: 9.1.1 (1)
uksort($params, 'strcmp');
$pairs = array();
foreach ($params as $parameter => $value) {
if (is_array($value)) {
// If two or more parameters share the same name, they are sorted by their value
// Ref: Spec: 9.1.1 (1)
// June 12th, 2010 - changed to sort because of issue 164 by hidetaka
sort($value, SORT_STRING);
foreach ($value as $duplicate_value) {
$pairs[] = $parameter . '=' . $duplicate_value;
}
} else {
$pairs[] = $parameter . '=' . $value;
}
}
// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
// Each name-value pair is separated by an '&' character (ASCII code 38)
return implode('&', $pairs);
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace Eher\OAuth\SignatureMethod;
/**
* The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
* where the Signature Base String is the text and the key is the concatenated values (each first
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
* character (ASCII code 38) even if empty.
* - Chapter 9.2 ("HMAC-SHA1")
*/
class HmacSha1 extends SignatureMethod {
function get_name() {
return "HMAC-SHA1";
}
public function build_signature($request, $consumer, $token) {
$base_string = $request->get_signature_base_string();
$request->base_string = $base_string;
$key_parts = array(
$consumer->secret,
($token) ? $token->secret : ""
);
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key = implode('&', $key_parts);
return base64_encode(hash_hmac('sha1', $base_string, $key, true));
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace Eher\OAuth\SignatureMethod;
/**
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
* over a secure channel such as HTTPS. It does not use the Signature Base String.
* - Chapter 9.4 ("PLAINTEXT")
*/
class PlainText extends SignatureMethod {
public function get_name() {
return "PLAINTEXT";
}
/**
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
* empty. The result MUST be encoded again.
* - Chapter 9.4.1 ("Generating Signatures")
*
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as
* OAuthRequest handles this!
*/
public function build_signature($request, $consumer, $token) {
$key_parts = array(
$consumer->secret,
($token) ? $token->secret : ""
);
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key = implode('&', $key_parts);
$request->base_string = $key;
return $key;
}
}

View File

@ -1,70 +0,0 @@
<?php
namespace Eher\OAuth\SignatureMethod;
/**
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
* verified way to the Service Provider, in a manner which is beyond the scope of this
* specification.
* - Chapter 9.3 ("RSA-SHA1")
*/
abstract class RsaSha1 extends SignatureMethod {
public function get_name() {
return "RSA-SHA1";
}
// Up to the SP to implement this lookup of keys. Possible ideas are:
// (1) do a lookup in a table of trusted certs keyed off of consumer
// (2) fetch via http using a url provided by the requester
// (3) some sort of specific discovery code based on request
//
// Either way should return a string representation of the certificate
protected abstract function fetch_public_cert(&$request);
// Up to the SP to implement this lookup of keys. Possible ideas are:
// (1) do a lookup in a table of trusted certs keyed off of consumer
//
// Either way should return a string representation of the certificate
protected abstract function fetch_private_cert(&$request);
public function build_signature($request, $consumer, $token) {
$base_string = $request->get_signature_base_string();
$request->base_string = $base_string;
// Fetch the private key cert based on the request
$cert = $this->fetch_private_cert($request);
// Pull the private key ID from the certificate
$privatekeyid = openssl_get_privatekey($cert);
// Sign using the key
$ok = openssl_sign($base_string, $signature, $privatekeyid);
// Release the key resource
openssl_free_key($privatekeyid);
return base64_encode($signature);
}
public function check_signature($request, $consumer, $token, $signature) {
$decoded_sig = base64_decode($signature);
$base_string = $request->get_signature_base_string();
// Fetch the public key cert based on the request
$cert = $this->fetch_public_cert($request);
// Pull the public key ID from the certificate
$publickeyid = openssl_get_publickey($cert);
// Check the computed signature against the one passed in the query
$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
// Release the key resource
openssl_free_key($publickeyid);
return $ok == 1;
}
}

View File

@ -1,40 +0,0 @@
<?php
namespace Eher\OAuth\SignatureMethod;
/**
* A class for implementing a Signature Method
* See section 9 ("Signing Requests") in the spec
*/
abstract class SignatureMethod {
/**
* Needs to return the name of the Signature Method (ie HMAC-SHA1)
* @return string
*/
abstract public function get_name();
/**
* Build up the signature
* NOTE: The output of this function MUST NOT be urlencoded.
* the encoding is handled in OAuthRequest when the final
* request is serialized
* @param OAuthRequest $request
* @param OAuthConsumer $consumer
* @param OAuthToken $token
* @return string
*/
abstract public function build_signature($request, $consumer, $token);
/**
* Verifies that a given signature is correct
* @param OAuthRequest $request
* @param OAuthConsumer $consumer
* @param OAuthToken $token
* @param string $signature
* @return bool
*/
public function check_signature($request, $consumer, $token, $signature) {
$built = $this->build_signature($request, $consumer, $token);
return $built == $signature;
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\Consumer;
use \JacobKiers\OAuth\Credential;
/**
* Consumer holds the properties of a single Consumer / consumer.
*
* @package OAuth
* @author Gary Jones <gary@garyjones.co.uk>
*/
class Consumer extends Credential implements ConsumerInterface
{
/**
* URL to which authorized requests will redirect to.
*
* @var string
*/
protected $callback_url;
/**
* Constructs a new Consumer object and populates the required parameters.
*
* @param string $key Consumer key / identifier.
* @param string $secret Consumer shared-secret.
* @param string $callback_url URL to which authorized request will redirect to.
*/
public function __construct($key, $secret, $callback_url = null)
{
$this->setKey($key);
$this->setSecret($secret);
$this->setCallbackUrl($callback_url);
}
/**
* Get the callback URL.
*
* @return string
*/
public function getCallbackUrl()
{
return $this->callback_url;
}
/**
* Set the callbackURL
*
* @param string $callback_url
*/
public function setCallbackUrl($callback_url)
{
$this->callback_url = $callback_url;
}
}

View File

@ -0,0 +1,48 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\Consumer;
/**
* Consumer holds the properties of a single Consumer / consumer.
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
*/
interface ConsumerInterface
{
/**
* Get the callback URL.
*
* @return string
*/
public function getCallbackUrl();
/**
* Set the callbackURL
*
* @param string $callback_url
*/
public function setCallbackUrl($callback_url);
/**
* Return the Consumer key.
*
* @return string Consumer key.
*/
public function getKey();
/**
* Return the Consumer secret
*
* @return string
*/
public function getSecret();
}

View File

@ -0,0 +1,75 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth;
/**
* Credential is the blueprint for all key + secret classes.
*
* @package OAuth
* @author Gary Jones <gary@garyjones.co.uk>
*/
abstract class Credential
{
/**
* The credential key.
*
* @var string
*/
protected $key;
/**
* The secret or shared-secret.
*
* @var string
*/
protected $secret;
/**
* Return the credential key.
*
* @return string Credential key.
*/
public function getKey()
{
return $this->key;
}
/**
* Set the credential key.
*
* @param string $key Credential identifier
*/
public function setKey($key)
{
$this->key = $key;
}
/**
* Return the credential secret
*
* @return string
*/
public function getSecret()
{
return $this->secret;
}
/**
* Set the credential key.
*
* @param string $key Credential identifier
*/
public function setSecret($secret)
{
$this->secret = $secret;
}
}

View File

@ -0,0 +1,81 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\DataStore;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
/**
* The actual implementation of validating and assigning tokens is left up to
* the system using this library.
*
* @package OAuth
* @author Gary Jones <gary@garyjones.co.uk>
*/
interface DataStoreInterface
{
/**
* Validate the consumer.
*
* @param string $consumer_key
*
* @return JacobKiers\OAuth\Consumer\ConsumerInterface
*/
public function lookupConsumer($consumer_key);
/**
* Validate a token.
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param string $token_type Request or access token
* @param string $token_key
*
* @return JacobKiers\OAuth\Token
*/
public function lookupToken(ConsumerInterface $consumer, $token_type, $token_key);
/**
* Validate that a nonce has not been used with the same timestamp before.
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token $token
* @param string $nonce
* @param int $timestamp
*
* @return boolean
*/
public function lookupNonce(ConsumerInterface $consumer, TokenInterface $token, $nonce, $timestamp);
/**
* Return a new token attached to this consumer.
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param string $callback URI to send the post-authorisation callback to.
*
* @return JacobKiers\OAuth\Token
*/
public function newRequestToken(ConsumerInterface $consumer, $callback = null);
/**
* Return a new access token attached to this consumer for the user
* associated with this token if the request token is authorized.
*
* Should also invalidate the request token.
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token $token
* @param string $verifier
*
* @return JacobKiers\OAuth\Token
*/
public function newAccessToken(ConsumerInterface $consumer, TokenInterface $token, $verifier = null);
}

View File

@ -0,0 +1,22 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth;
/**
* Generic exception class.
*
* @package OAuth
* @author Andy Smith
*/
class OAuthException extends \Exception
{
}

View File

@ -0,0 +1,502 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\Request;
use \JacobKiers\OAuth\Util;
use \JacobKiers\OAuth\OAuthException;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
/**
* Handle an OAuth request.
*
* @package OAuth
* @author Andy Smith
*/
class Request implements RequestInterface
{
/**
* HTTP parameters.
*
* @var array
*/
protected $parameters;
/**
* HTTP method - likely GET or POST.
*
* @var string HTTP method.
*/
protected $http_method;
/**
* The URL the request was made to.
*
* @var string Request URL.
*/
protected $http_url;
/**
* OAuth version.
*
* @var string
*/
public static $version = '1.0';
/**
* Stream of POSTed file.
*
* @var string
*/
public static $POST_INPUT = 'php://input';
/**
* Construct a Request object.
*
* @param string $http_method Request HTTP method.
* @param string $http_url Request URL.
* @param array $parameters HTTP parameters.
*/
public function __construct($http_method, $http_url, array $parameters = null)
{
if(!isset($parameters['oauth_consumer_key'])) {
throw new OAuthException('You need a OAuth consumer key to proceed');
}
$parameters = ($parameters) ? $parameters : array();
$this->parameters = array_merge(Util::parseParameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
$this->http_method = $http_method;
$this->http_url = $http_url;
}
/**
* a
*/
/**
* Attempt to build up a request from what was passed to the server.
*
* @param string $http_method Request HTTP method.
* @param string $http_url Request URL.
* @param array $parameters HTTP parameters.
*
* @return JacobKiers\OAuth\Request\RequestInterface
*/
public static function fromRequest($http_method = null, $http_url = null, $parameters = null)
{
$scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on') ? 'http' : 'https';
$http_url = ($http_url) ? $http_url : $scheme .
'://' . $_SERVER['HTTP_HOST'] .
':' .
$_SERVER['SERVER_PORT'] .
$_SERVER['REQUEST_URI'];
$http_method = ($http_method) ? $http_method : $_SERVER['REQUEST_METHOD'];
// We weren't handed any parameters, so let's find the ones relevant to
// this request.
// If you run XML-RPC or similar you should use this to provide your own
// parsed parameter-list
if (!$parameters) {
// Find request headers
$request_headers = Util::getHeaders();
// Parse the query-string to find GET parameters
$parameters = Util::parseParameters($_SERVER['QUERY_STRING']);
// It's a POST request of the proper content-type, so parse POST
// parameters and add those overriding any duplicates from GET
if ('POST' == $http_method
&& isset($request_headers['Content-Type'])
&& strstr($request_headers['Content-Type'], 'application/x-www-form-urlencoded')
) {
$post_data = Util::parseParameters(
file_get_contents(self::$POST_INPUT)
);
$parameters = array_merge($parameters, $post_data);
}
// We have a Authorization-header with OAuth data. Parse the header
// and add those overriding any duplicates from GET or POST
if (isset($request_headers['Authorization']) &&
substr($request_headers['Authorization'], 0, 6) == 'OAuth ') {
$header_parameters = Util::splitHeader(
$request_headers['Authorization']
);
$parameters = array_merge($parameters, $header_parameters);
}
}
return new Request($http_method, $http_url, $parameters);
}
/**
* Helper function to set up the request.
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
* @param string $http_method
* @param string $http_url
* @param array $parameters
*
* @return JacobKiers\OAuth\Request\RequestInterface
*/
public static function fromConsumerAndToken(
ConsumerInterface $consumer,
TokenInterface $token,
$http_method,
$http_url,
array $parameters = null
) {
$parameters = ($parameters) ? $parameters : array();
$defaults = array(
'oauth_nonce' => Request::generateNonce(),
'oauth_timestamp' => Request::generateTimestamp(),
'oauth_consumer_key' => $consumer->getKey());
if ($token->getKey()) {
$defaults['oauth_token'] = $token->getKey();
}
$parameters = array_merge($defaults, $parameters);
return new Request($http_method, $http_url, $parameters);
}
/**
* Add additional parameter to Request.
*
* @param string $name
* @param string $value
* @param bool $allow_duplicates
*/
public function setParameter($name, $value, $allow_duplicates = true)
{
if ($allow_duplicates && isset($this->parameters[$name])) {
// We have already added parameter(s) with this name, so add to the list
if (is_scalar($this->parameters[$name])) {
// This is the first duplicate, so transform scalar (string)
// into an array so we can add the duplicates
$this->parameters[$name] = array($this->parameters[$name]);
}
$this->parameters[$name][] = $value;
} else {
$this->parameters[$name] = $value;
}
}
/**
* Get single request parameter by name.
*
* @param string $name
*
* @return string
*/
public function getParameter($name)
{
return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
}
/**
* Get all request parameters.
*
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Unset single request parameter by name.
*
* @param string $name
*/
public function unsetParameter($name)
{
unset($this->parameters[$name]);
}
/**
* The request parameters, sorted and concatenated into a normalized string.
*
* @return string
*/
public function getSignableParameters()
{
// Grab all parameters
$params = $this->getParameters();
// Remove oauth_signature if present
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
if (isset($params['oauth_signature'])) {
unset($params['oauth_signature']);
}
return Util::buildHttpQuery($params);
}
/**
* Returns the base string of this request
*
* The base string defined as the method, the url
* and the parameters (normalized), each urlencoded
* and the concated with &.
*
* @return string
*/
public function getOAuthSignatureBaseString()
{
$parts = array(
$this->getNormalizedHttpMethod(),
$this->getNormalizedHttpUrl(),
$this->getSignableParameters()
);
$encoded_parts = Util::urlencodeRfc3986($parts);
return implode('&', $encoded_parts);
}
/**
* Uppercases the HTTP method.
*
* @return string
*/
public function getNormalizedHttpMethod()
{
return strtoupper($this->http_method);
}
/**
* Parses the url and rebuilds it to be
* scheme://host/path
*
* @return string URL
*/
public function getNormalizedHttpUrl()
{
$parts = parse_url($this->http_url);
$scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
$port = (isset($parts['port'])) ? $parts['port'] : '';
if (isset($parts['host'])) {
// parse_url in PHP 5.4.8 includes the port in the host, so we need to strip it.
$host_and_maybe_port = explode(':', $parts['host']);
$host = $host_and_maybe_port[0];
} else {
$host = '';
}
// For PHP 5.4+, use:
// $host = (isset($parts['host'])) ? explode(':', $parts['host'])[0] : '';
$path = (isset($parts['path'])) ? $parts['path'] : '';
if ($port) {
if (('https' == $scheme && $port != '443')
|| ('http' == $scheme && $port != '80')) {
$host = "$host:$port";
}
}
return "$scheme://$host$path";
}
/**
* Builds a URL usable for a GET request.
*
* @return string
*/
public function toUrl()
{
$post_data = $this->toPostdata();
$out = $this->getNormalizedHttpUrl();
if ($post_data) {
$out .= '?' . $post_data;
}
return $out;
}
/**
* Builds the data one would send in a POST request.
*
* @return string
*/
public function toPostdata()
{
return Util::buildHttpQuery($this->getParameters());
}
/**
* Builds the Authorization: header.
*
* @param string $realm Authorization realm.
*
* @return string
*
* @throws JacobKiers\OAuth\OAuthException
*/
public function toHeader($realm = null)
{
$first = true;
if ($realm) {
$out = 'Authorization: OAuth realm="' . Util::urlencodeRfc3986($realm) . '"';
$first = false;
} else {
$out = 'Authorization: OAuth';
}
$total = array();
foreach ($this->parameters as $k => $v) {
if (substr($k, 0, 5) != 'oauth') {
continue;
}
if (is_array($v)) {
throw new OAuthException('Arrays not supported in headers');
}
$out .= ($first) ? ' ' : ',';
$out .= Util::urlencodeRfc3986($k) .
'="' .
Util::urlencodeRfc3986($v) .
'"';
$first = false;
}
return $out;
}
/**
* Return request object cast as string.
*
* @return string
*/
public function __toString()
{
return $this->toUrl();
}
/**
* Build signature and add it as parameter.
*
* @param string $signature_method
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*/
public function signRequest($signature_method, ConsumerInterface $consumer, TokenInterface $token)
{
$this->setParameter('oauth_signature_method', $signature_method->getName(), false);
$signature = $this->buildSignature($signature_method, $consumer, $token);
$this->setParameter('oauth_signature', $signature, false);
}
/**
* Build signature.
*
* @param string $signature_method
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @return string
*/
public function buildSignature($signature_method, ConsumerInterface $consumer, TokenInterface $token)
{
return $signature_method->buildSignature($this, $consumer, $token);
}
/**
* {@inheritdoc}
*/
public function getOAuthCallback()
{
return $this->getParameter('oauth_callback');
}
/**
* {@inheritdoc}
*/
public function getOAuthConsumerKey()
{
return $this->getParameter('oauth_consumer_key');
}
/**
* {@inheritdoc}
*/
public function getOAuthNonce()
{
return $this->getParameter('oauth_nonce');
}
/**
* {@inheritdoc}
*/
public function getOAuthSignature()
{
return $this->getParameter('oauth_signature');
}
/**
* {@inheritdoc}
*/
public function getOAuthSignatureMethod()
{
return $this->getParameter('oauth_signature_method');
}
/**
* {@inheritdoc}
*/
public function getOAuthTimestamp()
{
return $this->getParameter('oauth_timestamp');
}
/**
* {@inheritdoc}
*/
public function getOAuthToken()
{
return $this->getParameter('oauth_token');
}
/**
* {@inheritdoc}
*/
public function getOAuthVerifier()
{
return $this->getParameter('oauth_verifier');
}
/**
* {@inheritdoc}
*/
public function getOAuthVersion()
{
return $this->getParameter('oauth_version');
}
/**
* Get current time.
*
* @return int Timestamp.
*/
private static function generateTimestamp()
{
return time();
}
/**
* Generate nonce.
*
* @return string 32-character hexadecimal number.
*/
private static function generateNonce()
{
return md5(microtime() . mt_rand()); // md5s look nicer than numbers
}
}

View File

@ -0,0 +1,97 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\Request;
/**
* Interface providing the necessary methods to handle an OAuth request.
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
*/
interface RequestInterface
{
/**
* Returns the OAuth Callback parameter.
*
* @return string
*/
public function getOAuthCallback();
/**
* Returns the Consumer Key.
*
* @return string
*/
public function getOAuthConsumerKey();
/**
* Returns the Nonce.
*
* In combination with the timestamp and the token, the nonce is
* used to prevent replay and side-channel attacks.
*
* @return string
*/
public function getOAuthNonce();
/**
* Returns the request signature.
*
* @return string
*/
public function getOAuthSignature();
/**
* Returns the base string of this request.
*
* The base string defined as the method, the url
* and the parameters (normalized), each urlencoded
* and the concated with &.
*
* @return string
*/
public function getOAuthSignatureBaseString();
/**
* Returns the signature method with which this signature is signed.
*
* @return string
*/
public function getOAuthSignatureMethod();
/**
* Returns the timestamp of the request.
*
* @return integer
*/
public function getOAuthTimestamp();
/**
* Returns the token.
*
* @return string
*/
public function getOAuthToken();
/**
* Returns the verifier.
*
* @return string
*/
public function getOAuthVerifier();
/**
* Returns the OAuth version used in this request.
*
* @var string
*/
public function getOAuthVersion();
}

View File

@ -0,0 +1,323 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth;
use \JacobKiers\OAuth\Token\Token;
use \JacobKiers\OAuth\Token\NullToken;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\DataStore\DataStoreInterface;
use \JacobKiers\OAuth\Request\RequestInterface;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
use \JacobKiers\OAuth\SignatureMethod\SignatureMethodInterface;
/**
* OAuth server.
*
* @package OAuth
* @author Andy Smith
*/
class Server
{
/**
* Limit to which timestamp is accepted, in seconds.
*
* Requests older than now - this value, are rejected as possible replay attack.
*
* @var int
*/
protected $timestamp_threshold = 300; // 5 minutes
/**
* OAuth version.
*
* @var string
*/
protected $version = '1.0';
/**
* Supported signature methods.
*
* @var array
*/
protected $signature_methods = array();
/**
* Data store object reference.
*
* @var JacobKiers\OAuth\DataStore\DataStoreInterface
*/
protected $data_store;
/**
* Construct OAuth server instance.
*
* @param JacobKiers\OAuth\DataStore\DataStoreInterface $data_store
*/
public function __construct(DataStoreInterface $data_store)
{
$this->data_store = $data_store;
}
/**
* Add a supported signature method.
*
* @param JacobKiers\OAuth\SignatureMethod\SignatureMethodInterface $signature_method
*/
public function addSignatureMethod(SignatureMethodInterface $signature_method)
{
$this->signature_methods[$signature_method->getName()] =
$signature_method;
}
// high level functions
/**
* Process a temporary credential (request_token) request.
*
* Returns the request token on success
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
*
* @return JacobKiers\OAuth\Token\TokenInterface
*/
public function fetchRequestToken(RequestInterface $request)
{
$this->getVersion($request);
$consumer = $this->getConsumer($request);
// no token required for the initial token request
$token = new NullToken;
$this->checkSignature($request, $consumer, $token);
// Rev A change
$callback = $request->getOAuthCallback();
return $this->data_store->newRequestToken($consumer, $callback);
}
/**
* Process a post-authorization token (access_token) request.
*
* Returns the access token on success.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
*
* @return JacobKiers\OAuth\Token\TokenInterface
*/
public function fetchAccessToken(RequestInterface $request)
{
$this->getVersion($request);
$consumer = $this->getConsumer($request);
// requires authorized request token
$token = $this->getToken($request, $consumer, 'request');
$this->checkSignature($request, $consumer, $token);
// Rev A change
$verifier = $request->getOAuthVerifier();
return $this->data_store->newAccessToken($consumer, $token, $verifier);
}
/**
* Verify an api call, checks all the parameters.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
*
* @return array Consumer and Token
*/
public function verifyRequest(RequestInterface $request)
{
$this->getVersion($request);
$consumer = $this->getConsumer($request);
$token = $this->getToken($request, $consumer, 'access');
$this->checkSignature($request, $consumer, $token);
return array('consumer' => $consumer, 'token' => $token);
}
// Internals from here
/**
* Check that version is 1.0.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
*
* @return string
*
* @throws JacobKiers\OAuth\OAuthException
*/
private function getVersion(RequestInterface $request)
{
$version = $request->getOAuthVersion();
if (!$version) {
// Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
// Chapter 7.0 ("Accessing Protected Ressources")
$version = '1.0';
}
if ($version !== $this->version) {
throw new OAuthException("OAuth version '$version' not supported");
}
return $version;
}
/**
* Get the signature method name, and if it is supported.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
*
* @return string Signature method name.
*
* @throws JacobKiers\OAuth\OAuthException
*/
private function getSignatureMethod(RequestInterface $request)
{
$signature_method = $request instanceof RequestInterface ? $request->getOAuthSignatureMethod() : null;
if (!$signature_method) {
// According to chapter 7 ("Accessing Protected Resources") the signature-method
// parameter is required, and we can't just fallback to PLAINTEXT
throw new OAuthException('No signature method parameter. This parameter is required');
}
if (!in_array($signature_method, array_keys($this->signature_methods))) {
throw new OAuthException(
"Signature method '$signature_method' not supported, try one of the following: " .
implode(", ", array_keys($this->signature_methods))
);
}
return $this->signature_methods[$signature_method];
}
/**
* Try to find the consumer for the provided request's consumer key.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
*
* @return JacobKiers\OAuth\Consumer\ConsumerInterface
*
* @throws JacobKiers\OAuth\OAuthException
*/
private function getConsumer(RequestInterface $request)
{
$consumer_key = $request instanceof RequestInterface ? $request->getOAuthConsumerKey() : null;
if (!$consumer_key) {
throw new OAuthException('Invalid consumer key');
}
$consumer = $this->data_store->lookupConsumer($consumer_key);
if (!$consumer) {
throw new OAuthException('Invalid consumer');
}
return $consumer;
}
/**
* Try to find the token for the provided request's token key.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param string $token_type
*
* @return JacobKiers\OAuth\Token\TokenInterface
*
* @throws JacobKiers\OAuth\OAuthException
*/
private function getToken(RequestInterface $request, ConsumerInterface $consumer, $token_type = 'access')
{
$token_key = $request instanceof RequestInterface ? $request->getOAuthToken() : null;
$token = $this->data_store->lookupToken($consumer, $token_type, $token_key);
if (!$token) {
throw new OAuthException("Invalid $token_type token: $token_key");
}
return $token;
}
/**
* All-in-one function to check the signature on a request.
*
* Should determine the signature method appropriately
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @throws JacobKiers\OAuth\OAuthException
*/
private function checkSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token)
{
// this should probably be in a different method
$timestamp = $request instanceof RequestInterface ? $request->getOAuthTimestamp() : null;
$nonce = $request instanceof RequestInterface ? $request->getOAuthNonce() : null;
$this->checkTimestamp($timestamp);
$this->checkNonce($consumer, $token, $nonce, $timestamp);
$signature_method = $this->getSignatureMethod($request);
$signature = $request->getOAuthSignature();
$valid_sig = $signature_method->checkSignature($request, $consumer, $token, $signature);
if (!$valid_sig) {
throw new OAuthException('Invalid signature');
}
}
/**
* Check that the timestamp is new enough
*
* @param int $timestamp
*
* @throws JacobKiers\OAuth\OAuthException
*/
private function checkTimestamp($timestamp)
{
if (!$timestamp) {
throw new OAuthException('Missing timestamp parameter. The parameter is required');
}
// verify that timestamp is recentish
$now = time();
if (abs($now - $timestamp) > $this->timestamp_threshold) {
throw new OAuthException("Expired timestamp, yours $timestamp, ours $now");
}
}
/**
* Check that the nonce is not repeated
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
* @param string $nonce
* @param int $timestamp
*
* @throws JacobKiers\OAuth\OAuthException
*/
private function checkNonce(ConsumerInterface $consumer, TokenInterface $token, $nonce, $timestamp)
{
if (!$nonce) {
throw new OAuthException('Missing nonce parameter. The parameter is required');
}
// verify that the nonce is uniqueish
$found = $this->data_store->lookupNonce($consumer, $token, $nonce, $timestamp);
if ($found) {
throw new OAuthException('Nonce already used: ' . $nonce);
}
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\SignatureMethod;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\Request\RequestInterface;
/**
* The HMAC-SHA1 signature method.
*
* The HMAC-SHA1 signature method. uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
* where the Signature Base String is the text and the key is the concatenated values (each first
* encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
* character (ASCII code 38) even if empty.
* - Chapter 9.2 ("HMAC-SHA1")
*
* @package OAuth
* @author Andy Smith
*/
class HmacSha1 extends SignatureMethod
{
/**
* Return the name of the Signature Method.
*
* @return string
*/
public function getName()
{
return 'HMAC-SHA1';
}
/**
* Build up the signature.
*
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
* empty. The result MUST be encoded again.
* - Chapter 9.4.1 ("Generating Signatures")
*
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as
* OAuthRequest handles this!
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @return string
*/
public function buildSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token = null)
{
$base_string = $request->getOAuthSignatureBaseString();
$key = $this->getSignatureKey($consumer, $token);
return base64_encode(hash_hmac('sha1', $base_string, $key, true));
}
}

View File

@ -0,0 +1,61 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\SignatureMethod;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\Request\RequestInterface;
/**
* PLAINTEXT signature method.
*
* The PLAINTEXT method does not provide any security protection and SHOULD only be used
* over a secure channel such as HTTPS. It does not use the Signature Base String.
* - Chapter 9.4 ("PLAINTEXT")
*
* @package OAuth
* @author Andy Smith
*/
class PlainText extends SignatureMethod
{
/**
* Return the name of the Signature Method.
*
* @return string
*/
public function getName()
{
return 'PLAINTEXT';
}
/**
* Build up the signature.
*
* oauth_signature is set to the concatenated encoded values of the Consumer Secret and
* Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
* empty. The result MUST be encoded again.
* - Chapter 9.4.1 ("Generating Signatures")
*
* Please note that the second encoding MUST NOT happen in the SignatureMethod, as
* OAuthRequest handles this!
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @return string
*/
public function buildSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token = null)
{
return $this->getSignatureKey($consumer, $token);
}
}

View File

@ -0,0 +1,120 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\SignatureMethod;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\Request\RequestInterface;
/**
* The RSA-SHA1 signature method.
*
* The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
* [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
* EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
* verified way to the Service Provider, in a manner which is beyond the scope of this
* specification.
* - Chapter 9.3 ("RSA-SHA1")
*
* @package OAuth
* @author Andy Smith
*/
abstract class RsaSha1 extends SignatureMethod
{
/**
* Return the name of the Signature Method.
*
* @return string
*/
public function getName()
{
return 'RSA-SHA1';
}
/**
* Up to the SP to implement this lookup of keys. Possible ideas are:
* (1) do a lookup in a table of trusted certs keyed off of consumer
* (2) fetch via http using a url provided by the requester
* (3) some sort of specific discovery code based on request
*
* Either way should return a string representation of the certificate
*
*/
abstract protected function fetchPublicCert($request);
/**
* Up to the SP to implement this lookup of keys. Possible ideas are:
* (1) do a lookup in a table of trusted certs keyed off of consumer
*
* Either way should return a string representation of the certificate
*/
abstract protected function fetchPrivateCert($request);
/**
* Build up the signature.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @return string
*/
public function buildSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token = null)
{
$base_string = $request->getOAuthSignatureBaseString();
// Fetch the private key cert based on the request
$cert = $this->fetchPrivateCert($request);
// Pull the private key ID from the certificate
$privatekeyid = openssl_get_privatekey($cert);
// Sign using the key
$ok = openssl_sign($base_string, $signature, $privatekeyid);
// Release the key resource
openssl_free_key($privatekeyid);
return base64_encode($signature);
}
/**
* Verifies that a given signature is correct.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
* @param string $signature
*
* @return bool
*/
public function checkSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token, $signature)
{
$base_string = $request->getOAuthSignatureBaseString();
$decoded_sig = base64_decode($signature);
// Fetch the public key cert based on the request
$cert = $this->fetchPublicCert($request);
// Pull the public key ID from the certificate
$publickeyid = openssl_get_publickey($cert);
// Check the computed signature against the one passed in the query
$ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
// Release the key resource
openssl_free_key($publickeyid);
return $ok == 1;
}
}

View File

@ -0,0 +1,64 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\SignatureMethod;
use \JacobKiers\OAuth\Util;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\Request\RequestInterface;
/**
* A class for implementing a Signature Method.
*
* See section 9 ("Signing Requests") in the spec
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
*/
abstract class SignatureMethod implements SignatureMethodInterface
{
/**
* Get the signature key, made up of consumer and optionally token shared secrets.
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @return string
*/
public function getSignatureKey(ConsumerInterface $consumer, TokenInterface $token = null)
{
$key_parts = array(
$consumer->getSecret(),
($token) ? $token->getSecret() : '',
);
$key_parts = Util::urlencodeRfc3986($key_parts);
return implode('&', $key_parts);
}
/**
* Verifies that a given signature is correct.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
* @param string $signature
*
* @return bool
*/
public function checkSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token, $signature)
{
$built = $this->buildSignature($request, $consumer, $token);
return $built == $signature;
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\SignatureMethod;
use \JacobKiers\OAuth\Consumer\ConsumerInterface;
use \JacobKiers\OAuth\Token\TokenInterface;
use \JacobKiers\OAuth\Request\RequestInterface;
/**
* A class for implementing a Signature Method.
*
* See section 9 ("Signing Requests") in the spec
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
*/
interface SignatureMethodInterface
{
/**
* Return the name of the Signature Method (ie HMAC-SHA1).
*
* @return string
*/
public function getName();
/**
* Build up the signature.
*
* NOTE: The output of this function MUST NOT be urlencoded.
* the encoding is handled in OAuthRequest when the final
* request is serialized.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @return string
*/
public function buildSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token = null);
/**
* Get the signature key, made up of consumer and optionally token shared secrets.
*
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
*
* @return string
*/
public function getSignatureKey(ConsumerInterface $consumer, TokenInterface $token = null);
/**
* Verifies that a given signature is correct.
*
* @param JacobKiers\OAuth\Request\RequestInterface $request
* @param JacobKiers\OAuth\Consumer\ConsumerInterface $consumer
* @param JacobKiers\OAuth\Token\TokenInterface $token
* @param string $signature
*
* @return bool
*/
public function checkSignature(RequestInterface $request, ConsumerInterface $consumer, TokenInterface $token, $signature);
}

View File

@ -0,0 +1,35 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\Token;
/**
* Token holds the properties of a single token.
*
* This class deals with both temporary (request) and token (access) credntials.
*
* @package OAuth
* @author Gary Jones <gary@garyjones.co.uk>
*/
class NullToken extends Token
{
/**
* Constructs a new Token object and populates the required parameters.
*
* @param string $key Token key / identifier.
* @param string $secret Token shared-secret.
*/
public function __construct()
{
$this->setKey('');
$this->setSecret('');
}
}

View File

@ -0,0 +1,50 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\Token;
use \JacobKiers\OAuth\Credential;
use \JacobKiers\OAuth\Util;
/**
* Token holds the properties of a single token.
*
* This class deals with both temporary (request) and token (access) credentials.
*
* @package OAuth
* @author Gary Jones <gary@garyjones.co.uk>
*/
class Token extends Credential implements TokenInterface
{
/**
* Constructs a new Token object and populates the required parameters.
*
* @param string $key Token key / identifier.
* @param string $secret Token shared-secret.
*/
public function __construct($key, $secret)
{
$this->setKey($key);
$this->setSecret($secret);
}
/**
* Generates the basic string serialization of a token that a server
* would respond to request_token and access_token calls with.
*
* @return string
*/
public function toString()
{
return 'oauth_token=' . Util::urlencodeRfc3986($this->key) .
'&oauth_token_secret=' . Util::urlencodeRfc3986($this->secret);
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth\Token;
/**
* Credential is the blueprint for all key + secret classes.
*
* @package OAuth
* @author Jacob Kiers <jacob@alphacomm.nl>
*/
interface TokenInterface
{
/**
* Return the credential key.
*
* @return string Credential key.
*/
public function getKey();
/**
* Return the credential secret
*
* @return string
*/
public function getSecret();
}

View File

@ -0,0 +1,210 @@
<?php
/**
* OAuth
*
* @package OAuth
* @author Andy Smith
* @author Gary Jones <gary@garyjones.co.uk>
* @license https://raw.github.com/jacobkiers/OAuth/master/LICENSE MIT
* @link https://github.com/jacobkiers/OAuth
*/
namespace JacobKiers\OAuth;
/**
* Group of static utility methods.
*
* @package OAuth
* @author Andy Smith
*/
class Util
{
/**
* Encode a string according to RFC 3986.
*
* @param string $input
*
* @return string Encoded string.
*/
public static function urlencodeRfc3986($input)
{
if (is_array($input)) {
return array_map(array('JacobKiers\OAuth\Util', 'urlencodeRfc3986'), $input);
} elseif (is_scalar($input)) {
return str_replace('+', ' ', str_replace('%7E', '~', rawurlencode($input)));
} else {
return '';
}
}
/**
* Decode a string.
*
* This decode function isn't taking into consideration the above modifications to the encoding process.
* However, this method doesn't seem to be used anywhere so leaving it as is.
*
* @param string $string
*
* @return string Decoded string.
*/
public static function urldecodeRfc3986($string)
{
return urldecode($string);
}
/**
* Utility function for turning the Authorization: header into parameters.
*
* Has to do some unescaping. Can filter out any non-oauth parameters if needed (default behaviour).
*
* May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
* see http://code.google.com/p/oauth/issues/detail?id=163
*
* @param string $header
* @param bool $only_allow_oauth_parameters
*
* @return array
*/
public static function splitHeader($header, $only_allow_oauth_parameters = true)
{
$params = array();
$regex = '/(' . ($only_allow_oauth_parameters ? 'oauth_' : '') . '[a-z_-]*)=(:?"([^"]*)"|([^,]*))/';
if (preg_match_all($regex, $header, $matches)) {
foreach ($matches[1] as $i => $h) {
$params[$h] = Util::urldecodeRfc3986(empty($matches[3][$i]) ? $matches[4][$i] : $matches[3][$i]);
}
if (isset($params['realm'])) {
unset($params['realm']);
}
}
return $params;
}
/**
* Helper to try to sort out headers for people who aren't running apache
*
* @return array
*/
public static function getHeaders()
{
$out = array();
if (function_exists('apache_request_headers')) {
// we need this to get the actual Authorization: header
// because apache tends to tell us it doesn't exist
$headers = apache_request_headers();
// sanitize the output of apache_request_headers because
// we always want the keys to be Cased-Like-This and arh()
// returns the headers in the same case as they are in the
// request
foreach ($headers as $key => $value) {
$key = str_replace(' ', '-', ucwords(strtolower(str_replace('-', ' ', $key))));
$out[$key] = $value;
}
} else {
// otherwise we don't have apache and are just going to have to hope
// that $_SERVER actually contains what we need
if (isset($_SERVER['CONTENT_TYPE'])) {
$out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
}
if (isset($_ENV['CONTENT_TYPE'])) {
$out['Content-Type'] = $_ENV['CONTENT_TYPE'];
}
foreach ($_SERVER as $key => $value) {
if (substr($key, 0, 5) == "HTTP_") {
// this is chaos, basically it is just there to capitalize the first
// letter of every word that is not an initial HTTP and strip HTTP
// code from przemek
$key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
$out[$key] = $value;
}
}
}
return $out;
}
/**
* Pull key=value querystring into an array.
*
* This function takes a input like a=b&a=c&d=e and returns the parsed
* parameters like this
* array('a' => array('b','c'), 'd' => 'e')
*
* @param string $input
*
* @return array
*/
public static function parseParameters($input)
{
if (!isset($input) || !$input) {
return array();
}
$pairs = explode('&', $input);
$parsed_parameters = array();
foreach ($pairs as $pair) {
$split = explode('=', $pair, 2);
$parameter = Util::urldecodeRfc3986($split[0]);
$value = isset($split[1]) ? Util::urldecodeRfc3986($split[1]) : '';
if (isset($parsed_parameters[$parameter])) {
// We have already recieved parameter(s) with this name, so add to the list
// of parameters with this name
if (is_scalar($parsed_parameters[$parameter])) {
// This is the first duplicate, so transform scalar (string) into an array
// so we can add the duplicates
$parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
}
$parsed_parameters[$parameter][] = $value;
} else {
$parsed_parameters[$parameter] = $value;
}
}
return $parsed_parameters;
}
/**
* Build query string from parameters, with correct encoding.
*
* @param array $params
*
* @return string
*/
public static function buildHttpQuery(array $params)
{
if (!$params) {
return '';
}
// Urlencode both keys and values
$keys = Util::urlencodeRfc3986(array_keys($params));
$values = Util::urlencodeRfc3986(array_values($params));
$params = array_combine($keys, $values);
// Parameters are sorted by name, using lexicographical byte value ordering.
// Ref: Spec: 9.1.1 (1)
uksort($params, 'strcmp');
$pairs = array();
foreach ($params as $parameter => $value) {
if (is_array($value)) {
// If two or more parameters share the same name, they are sorted by their value
// Ref: Spec: 9.1.1 (1)
// June 12th, 2010 - changed to sort because of issue 164 by hidetaka
sort($value, SORT_STRING);
foreach ($value as $duplicate_value) {
$pairs[] = $parameter . '=' . $duplicate_value;
}
} else {
$pairs[] = $parameter . '=' . $value;
}
}
// For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
// Each name-value pair is separated by an '&' character (ASCII code 38)
return implode('&', $pairs);
}
}

20
tests/ConsumerTest.php Normal file
View File

@ -0,0 +1,20 @@
<?php
use JacobKiers\OAuth\Consumer\Consumer;
class ConsumerTest extends PHPUnit_Framework_TestCase
{
public function testKeyAndSecretAreSet()
{
$consumer = new consumer('foo', 'bar');
$this->assertEquals('foo', $consumer->getKey());
$this->assertEquals('bar', $consumer->getSecret());
}
public function testCallbackUrlIsSet()
{
$consumer = new consumer('foo', 'bar', 'http://example.com/foobar');
$this->assertEquals('http://example.com/foobar', $consumer->getCallbackUrl());
}
}

79
tests/HmacSha1Test.php Normal file
View File

@ -0,0 +1,79 @@
<?php
use Mockery as m;
use JacobKiers\OAuth\SignatureMethod\HmacSha1;
class HmacSha1Test extends PHPUnit_Framework_TestCase
{
public function tearDown()
{
m::close();
}
public function testSignatureName()
{
$hmacsha1 = $this->getSignatureMethod();
$this->assertEquals('HMAC-SHA1', $hmacsha1->getName());
}
public function testBuildSignatureWithoutToken()
{
// Create instance of class to test
$hmacsha1 = $this->getSignatureMethod();
// Get mock objects
$request = $this->getRequest();
$client = $this->getConsumer();
// Run method being tested
$signature = $hmacsha1->buildSignature($request, $client);
// Check results
$this->assertEquals('RaZU4UG/wwJ/E5Df2/pePmwaS1Q=', $signature);
}
public function testBuildSignatureWithToken()
{
// Create instance of class to test
$hmacsha1 = $this->getSignatureMethod();
// Get mock objects
$request = $this->getRequest();
$client = $this->getConsumer();
$token = $this->getToken();
// Run method being tested
$signature = $hmacsha1->buildSignature($request, $client, $token);
// Check results
$this->assertEquals('1P/rfHzjnxBcNzngW9BEuT01goM=', $signature);
}
private function getSignatureMethod()
{
return new HmacSha1;
}
private function getRequest()
{
return m::mock('JacobKiers\OAuth\Request\Request', function ($mock) {
$mock->shouldReceive('getOAuthSignatureBaseString')
->withNoArgs()
->andReturn('POST&http%3A%2F%2Fexample.com%2Ffoobar&oauth_signature_method%3DHMAC-SHA1')->once();
});
}
private function getConsumer()
{
return m::mock('JacobKiers\OAuth\Consumer\Consumer', function ($mock) {
$mock->shouldReceive('getSecret')->withNoArgs()->andReturn('secret')->once();
});
}
private function getToken()
{
return m::mock('JacobKiers\OAuth\Token\Token', function ($mock) {
$mock->shouldReceive('getSecret')->withNoArgs()->andReturn('token_secret');
});
}
}

75
tests/PlainTextTest.php Normal file
View File

@ -0,0 +1,75 @@
<?php
use Mockery as m;
use JacobKiers\OAuth\SignatureMethod\PlainText;
class PlainTextTest extends PHPUnit_Framework_TestCase
{
public function tearDown()
{
m::close();
}
public function testSignatureName()
{
$plaintext = $this->getSignatureMethod();
$this->assertEquals('PLAINTEXT', $plaintext->getName());
}
public function testBuildSignatureWithoutToken()
{
// Create instance of class to test
$plaintext = $this->getSignatureMethod();
// Get mock objects
$request = $this->getRequest();
$client = $this->getConsumer();
// Run method being tested
$signature = $plaintext->buildSignature($request, $client);
// Check results
$this->assertEquals('secret&', $signature);
}
public function testBuildSignatureWithToken()
{
// Create instance of class to test
$plaintext = $this->getSignatureMethod();
// Get mock objects
$request = $this->getRequest();
$client = $this->getConsumer();
$token = $this->getToken();
// Run method being tested
$signature = $plaintext->buildSignature($request, $client, $token);
// Check results
$this->assertEquals('secret&token_secret', $signature);
}
private function getSignatureMethod()
{
return new PlainText;
}
private function getRequest()
{
return m::mock('JacobKiers\OAuth\Request\Request');
}
private function getConsumer()
{
return m::mock('JacobKiers\OAuth\Consumer\Consumer', function ($mock) {
$mock->shouldReceive('getSecret')->withNoArgs()->andReturn('secret')->once();
});
}
private function getToken()
{
return m::mock('JacobKiers\OAuth\Token\Token', function ($mock) {
$mock->shouldReceive('getSecret')->withNoArgs()->andReturn('token_secret');
});
}
}

43
tests/RequestTest.php Normal file
View File

@ -0,0 +1,43 @@
<?php
use Mockery as m;
use JacobKiers\OAuth\Request\Request;
class RequestTest extends PHPUnit_Framework_TestCase
{
public function tearDown()
{
m::close();
}
/**
* @expectedException \JacobKiers\OAuth\OAuthException
*/
public function testRequestThrowsExceptionWhenNoOAuthConsumerKeyIsPresent()
{
$request = new Request('POST', 'http://example.com', array());
}
public function testHttpMethodCanBeNormalized()
{
$request = new Request('foo', 'bar', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('FOO', $request->getNormalizedHttpMethod());
}
public function testHttpUrlCanBeNormalized()
{
$request = new Request('foo', 'bar', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('http://bar', $request->getNormalizedHttpUrl());
$request = new Request('foo', 'example.com:80', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('http://example.com', $request->getNormalizedHttpUrl());
$request = new Request('foo', 'example.com:81', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('http://example.com:81', $request->getNormalizedHttpUrl());
$request = new Request('foo', 'https://example.com', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('https://example.com', $request->getNormalizedHttpUrl());
$request = new Request('foo', 'https://example.com:443', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('https://example.com', $request->getNormalizedHttpUrl());
$request = new Request('foo', 'http://example.com/foobar', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('http://example.com/foobar', $request->getNormalizedHttpUrl());
$request = new Request('foo', 'example.org:80/foobar', array('oauth_consumer_key' => 'bar'));
$this->assertEquals('http://example.org/foobar', $request->getNormalizedHttpUrl());
}
}

View File

@ -0,0 +1,80 @@
<?php
use Mockery as m;
use JacobKiers\OAuth\SignatureMethod\SignatureMethod;
/**
* Create concrete class from abstract SignatureMethod.
*
* Have to define two methods which are abstract in SignatureMethod.
*/
class FooBarSignatureMethod extends SignatureMethod
{
public function getName() {
}
public function buildSignature(
\JacobKiers\OAuth\Request\RequestInterface $request,
\JacobKiers\OAuth\Consumer\ConsumerInterface $consumer,
\JacobKiers\OAuth\Token\TokenInterface $token = null
) {
}
}
class SignatureTest extends PHPUnit_Framework_TestCase
{
public function tearDown()
{
m::close();
}
public function testGetSignatureKeyWithoutToken()
{
// Create instance of class to test, with mock objects passed in.
$signature_method = $this->getSignatureMethod();
// Get mock objects
$consumer = $this->getConsumer();
// Run method being tested
$signature_key = $signature_method->getSignatureKey($consumer);
// Check results
$this->assertEquals('secret&', $signature_key);
}
public function testGetSignatureKeyWithToken()
{
// Create instance of class to test, with mock objects passed in.
$signature_method = $this->getSignatureMethod();
// Get mock objects
$consumer = $this->getConsumer();
$token = $this->getToken();
// Run method being tested
$signature_key = $signature_method->getSignatureKey($consumer, $token);
// Check results
$this->assertEquals('secret&token_secret', $signature_key);
}
private function getSignatureMethod()
{
return new FooBarSignatureMethod;
}
private function getConsumer()
{
return m::mock('JacobKiers\OAuth\Consumer\Consumer', function ($mock) {
$mock->shouldReceive('getSecret')->withNoArgs()->andReturn('secret')->once();
});
}
private function getToken()
{
return m::mock('JacobKiers\OAuth\Token\Token', function ($mock) {
$mock->shouldReceive('getSecret')->withNoArgs()->andReturn('token_secret');
});
}
}

30
tests/TokenTest.php Normal file
View File

@ -0,0 +1,30 @@
<?php
use JacobKiers\OAuth\Token\Token;
use JacobKiers\OAuth\Token\NullToken;
use JacobKiers\OAuth\Util;
class TokenTest extends PHPUnit_Framework_TestCase
{
public function testKeyAndSecretAreSet()
{
$token = new Token('foo', 'bar');
$this->assertEquals('foo', $token->getKey());
$this->assertEquals('bar', $token->getSecret());
}
public function testTokenString()
{
$token = new Token('foo', 'bar');
$string = 'oauth_token=' . Util::urlencodeRfc3986('foo') .
'&oauth_token_secret=' . Util::urlencodeRfc3986('bar');
$this->assertEquals($string, $token->toString());
}
public function testNullTokenKeyAndSecretAreEmpty()
{
$token = new NullToken();
$this->assertEmpty($token->getKey());
$this->assertEmpty($token->getSecret());
}
}