Commit: e167b7527764b3cfc045527716a85ff18ebf0e44

Author: gwoo | Date: 2009-11-27 15:24:41 -0800
initial commit
diff --git a/extensions/service/Oauth.php b/extensions/service/Oauth.php new file mode 100644 index 0000000..3002d6c --- /dev/null +++ b/extensions/service/Oauth.php @@ -0,0 +1,218 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\extensions\service; + +/** + * Oauth service class for handling requests/response to consumers and from providers + * + * + */ +class Oauth extends \lithium\core\Object { + + protected $_autoConfig = array('classes' => 'merge'); + + /** + * Fully-namespaced class references + * + * @var array + */ + protected $_classes = array( + 'service' => '\lithium\http\Service', + 'storage' => '\li3_oauth\extensions\storage\File' + ); + + /** + * Constructor + * + * @param array $config + * - host: the oauth domain + * - oauth_consumer_key: key from oauth service provider + * - oauth_consumer_secret: secret from oauth service provider + * - oauth_consumer_key: key from oauth service provider + * - authorize: path to authorize url + * - request_token: path to request token url + * - access_token: path to access token url + */ + public function __construct($config = array()) { + $defaults = array( + 'host' => null, + 'authorize' => 'oauth/authorize', + 'request_token' => 'oauth/request_token', + 'access_token' => 'oauth/access_token', + 'oauth_consumer_key' => 'key', + 'oauth_consumer_secret' => 'secret' + ); + $config += $defaults; + + parent::__construct($config); + } + + /** + * Initialize classes to be used + * + * @return void + */ + public function _init() { + parent::_init(); + $this->service = new $this->_classes['service']($this->_config); + $this->store = new $this->_classes['storage']($this->_config); + } + + /** + * If a key is set returns the value of that key + * Without a key it will return config array + * + * @param string $key eg `oauth_consumer_key` + * @return void + */ + public function config($key = null) { + if (isset($this->_config[$key])) { + return $this->_config[$key]; + } + if ($key) { + return $key; + } + return $this->_config; + } + + /** + * Send request + * + * @param string $method + * @param string $path + * @param string $data + * @param string $options + * @return void + */ + public function send($path = null, $data = null, $options = array()) { + $url = $this->config($path); + $method = !empty($options['method']) ? $options['method'] : 'post'; + $data = $this->sign($data + compact('url')); + $response = $this->service->send($method, $url, $data, $options); + if (strpos($response, 'oauth') !== false) { + return $this->_decode($response); + } + return $response; + } + + /** + * undocumented function + * + * @param string $url + * @return void + */ + public function url($url) { + $url = $this->config($url); + return "http://{$this->_config['host']}/{$url}"; + } + + /** + * undocumented function + * + * @param string $options + * - hash: HMAC-SHA1 + * - secret: config['oauth_consumer_secret'] + * - params: extra params for to sign request + * - url: url of request + * - data: post data for request + * - token: array with keys oauth_token, oauth_token_secret + * @return void + */ + public function sign($options = array()) { + $defaults = array( + 'hash' => 'HMAC-SHA1', 'secret' => $this->_config['oauth_consumer_secret'], + 'params' => array(), 'method' => 'POST', 'url' => '/', 'data' => array(), + 'token' => array('oauth_token' => null, 'oauth_token_secret' => null), + ); + $options += $defaults; + $params = $this->_build($options['params'] + (array)$options['token']) + $options['data']; + $base = $this->_base($options['type'], $options['url'], $params); + $key = join("&", array( + rawurlencode($options['secret']), rawurlencode($options['token']['oauth_token_secret']) + )); + switch ($options['hash']) { + case 'HMAC-SHA1': + $signature = base64_encode(hash_hmac('sha1', $base, $key, true)); + break; + default: + return $options['secret']; + break; + } + $params['oauth_signature'] = $signature; + return $params; + } + + /** + * undocumented function + * + * @param string $method + * @param string $url + * @param string $params + * @return void + */ + protected function _base($method, $url, $params) { + $path = $this->url($url); + $base = join("&", array( + $method, rawurlencode($path), + rawurlencode(http_build_query($params)) + )); + return $base; + } + + /** + * undocumented function + * + * @param string $params + * @return void + */ + protected function _build($params = array()) { + $defaults = array( + 'oauth_consumer_key' => 'key', + 'oauth_nonce' => sha1(time() . mt_rand()), + 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_timestamp' => time(), + 'oauth_token' => '', + 'oauth_version' => '1.0' + ); + $result = array(); + foreach ($defaults as $key => $value) { + if (isset($params[$key])) { + $result[$key] = $params[$key]; + continue; + } + if (isset($this->_config[$key])) { + $result[$key] = $this->_config[$key]; + continue; + } + if ($value) { + $result[$key] = $value; + } + } + ksort($result); + return $result; + } + + /** + * undocumented function + * + * @param string $path + * @return void + */ + protected function _decode($query = null) { + $token = array(); + $result = array_filter(explode('&', $query), function ($value) use (&$token) { + if ($parts = explode("=", $value)) { + $token[rawurldecode($parts[0])] = rawurldecode($parts[1]); + } + return false; + }); + return $token; + } +} +?> \ No newline at end of file diff --git a/extensions/storage/File.php b/extensions/storage/File.php new file mode 100644 index 0000000..077b641 --- /dev/null +++ b/extensions/storage/File.php @@ -0,0 +1,35 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\extensions\storage; + +class File extends \lithium\core\Object { + + public $file = null; + + protected function _init() { + parent::_init(); + $this->file = LITHIUM_APP_PATH . '/tmp/storage/oauth.ini'; + } + + public function write($key, $value) { + $value = json_encode($value); + $data = "{$key}=\"{$value}\""; + return file_put_contents($this->file, $data, FILE_APPEND); + } + + public function read($key) { + $data = parse_ini_file($this->file); + if (isset($data[$key])) { + return json_decode($data[$key]); + } + return null; + } + +} +?> \ No newline at end of file diff --git a/models/Consumer.php b/models/Consumer.php new file mode 100644 index 0000000..113ae19 --- /dev/null +++ b/models/Consumer.php @@ -0,0 +1,113 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\models; + +use \li3_oauth\extensions\service\Oauth; + +class Consumer extends \lithium\core\StaticObject { + + /** + * Holds an instance of the oauth service class + * + * @see \li3_oauth\extensions\services\Oauth + */ + protected static $_service = null; + + /** + * Configure the Consumer to access the Oauth service layer + * {{{ + * Consumer::config(array( + * 'host' => 'localhost', + * 'oauth_consumer_key' => 'key', + * 'oauth_consumer_secret' => 'secret', + * 'request_token' => 'libraries/oauth_php/example/request_token.php', + * 'access_token' => 'libraries/oauth_php/example/access_token.php', + * )); + * }}} + * + * @param array $config + * - host: the oauth domain + * - oauth_consumer_key: key from oauth service provider + * - oauth_consumer_secret: secret from oauth service provider + * - oauth_consumer_key: key from oauth service provider + * - authorize: path to authorize url + * - request_token: path to request token url + * - access_token: path to access token url + * + * @return void + */ + public static function config($config) { + static::$_service = new Oauth($config); + } + + /** + * Signs and Sends a post request to the request token endpoint with optional params + * + * @param array $options optional params for the request + * @return string + */ + public static function request($options = array()) { + return static::$_service->send('request_token', $options + array( + 'hash' => 'HMAC-SHA1', 'method' => 'POST' + )); + } + + /** + * Signs and Sends request to access token endpoint with the token returned from request method + * + * @param array $token return value from `Consumer::request()` + * @return string + */ + public static function access($token, $options = array()) { + return static::$_service->send('access_token', $options + array( + 'hash' => 'HMAC-SHA1', 'method' => 'POST', 'token' => (array) $token, + )); + } + + /** + * Signs and Sends a post request to the given url + * + * @param string $url request path that follows host: eg `/statues/update.json` + * @param array $token the token from a request + * @param array $data data to send as the body of the request + * @return string + */ + public static function post($url, $token, $data = array(), $options = array()) { + return static::$_service->send($url, $options + array( + 'hash' => 'HMAC-SHA1', 'method' => 'POST', 'token' => (array) $token, 'data' => $data + )); + } + + /** + * get url from remote authorization endpoint along with request params + * + * @param mixed $token + * @return string + */ + public static function authorize($token) { + $token = (is_array($token) && isset($token['oauth_token'])) ? $token['oauth_token'] : $token; + $url = static::$_service->url('authorize'); + return "{$url}?oauth_token={$token}"; + } + + /** + * get url from remote authenticated endpoint along with token + * + * @param mixed $token + * @return string + */ + public static function authenticate($token) { + $token = (is_array($token) && isset($token['oauth_token'])) ? $token['oauth_token'] : $token; + $url = static::$_service->url('authenticate'); + return "{$url}?oauth_token={$token}"; + } + +} + +?> \ No newline at end of file diff --git a/tests/cases/extensions/service/OauthTest.php b/tests/cases/extensions/service/OauthTest.php new file mode 100644 index 0000000..f1b056e --- /dev/null +++ b/tests/cases/extensions/service/OauthTest.php @@ -0,0 +1,85 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\tests\cases\extensions\service; + +use \li3_oauth\tests\mocks\extensions\service\MockOauth; + +class OauthTest extends \lithium\test\Unit { + + protected $_testConfig = array( + 'classes' => array( + 'service' => '\li3_oauth\tests\mocks\extensions\service\MockService', + ), + 'persistent' => false, + 'protocol' => 'http', + 'host' => 'localhost', + 'login' => 'root', + 'password' => '', + 'port' => 80, + 'timeout' => 1 + ); + + public function testDefaultConfig() { + $oauth = new MockOauth($this->_testConfig); + $config = $oauth->config(); + + $expected = 'oauth/request_token'; + $result = $config['request_token']; + $this->assertEqual($expected, $result); + } + + public function testCustomConfig() { + $this->_testConfig['request_token'] = 'request_token.php'; + $oauth = new MockOauth($this->_testConfig); + $config = $oauth->config(); + + $expected = 'request_token.php'; + $result = $config['request_token']; + $this->assertEqual($expected, $result); + } + + public function testDecode() { + $oauth = new MockOauth($this->_testConfig); + + $expected = array('oauth_token' => 12345, 'oauth_secret' => 54321); + $result = $oauth->decode('oauth_token=12345&oauth_secret=54321'); + $this->assertTrue($result); + } + + public function testPostRequestToken() { + $oauth = new MockOauth($this->_testConfig); + + $expected = array( + 'oauth_token' => 'requestkey', + 'oauth_token_secret' => 'requestsecret' + ); + $result = $oauth->send('request_token', array( + 'hash' => 'HMAC-SHA1', 'method' => 'POST', 'params' => array() + )); + $this->assertEqual($expected, $result); + } + + public function testPostAcceesToken() { + $oauth = new MockOauth($this->_testConfig); + + $expected = array( + 'oauth_token' => 'accesskey', + 'oauth_token_secret' => 'accesssecret' + ); + $result = $oauth->send('access_token', array( + 'hash' => 'HMAC-SHA1', 'method' => 'POST', 'params' => array(), + 'token' => array( + 'oauth_token' => 'requestkey', + 'oauth_token_secret' => 'requestsecret' + ) + )); + $this->assertEqual($expected, $result); + } +} +?> \ No newline at end of file diff --git a/tests/cases/extensions/storage/FileTest.php b/tests/cases/extensions/storage/FileTest.php new file mode 100644 index 0000000..b723c65 --- /dev/null +++ b/tests/cases/extensions/storage/FileTest.php @@ -0,0 +1,18 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\test\cases\extensions\storage; + +use \li3_oauth\extensions\storage\File; + +class FileTest extends \lithium\test\Unit { + + +} + +?> \ No newline at end of file diff --git a/tests/cases/models/ConsumerTest.php b/tests/cases/models/ConsumerTest.php new file mode 100644 index 0000000..b53bd13 --- /dev/null +++ b/tests/cases/models/ConsumerTest.php @@ -0,0 +1,36 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\tests\cases\models; + +use \li3_oauth\models\Consumer; + +class ConsumerTest extends \lithium\test\Unit { + + public function setUp() { + Consumer::config(array( + 'host' => 'localhost', + 'oauth_consumer_key' => 'key', + 'oauth_consumer_secret' => 'secret', + 'request_token' => 'libraries/oauth_php/example/request_token.php', + 'access_token' => 'libraries/oauth_php/example/access_token.php', + 'port' => 30500 + )); + } + + public function testAuthorize() { + $expected = 'http://localhost/oauth/authorize?oauth_token=requestkey&oauth_token_secret=requestsecret'; + $result = Consumer::authorize(array( + 'oauth_token' => 'requestkey', + 'oauth_token_secret' => 'requestsecret' + )); + $this->assertEqual($expected, $result); + } +} + +?> \ No newline at end of file diff --git a/tests/cases/models/ProviderTest.php b/tests/cases/models/ProviderTest.php new file mode 100644 index 0000000..8ff768d --- /dev/null +++ b/tests/cases/models/ProviderTest.php @@ -0,0 +1,18 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\test\cases\models; + +use \li3_oauth\models\Provider; + +class ProviderTest extends \lithium\test\Unit { + + +} + +?> \ No newline at end of file diff --git a/tests/mocks/extensions/service/MockOauth.php b/tests/mocks/extensions/service/MockOauth.php new file mode 100644 index 0000000..11369b5 --- /dev/null +++ b/tests/mocks/extensions/service/MockOauth.php @@ -0,0 +1,18 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\tests\mocks\extensions\service; + +class MockOauth extends \li3_oauth\extensions\service\Oauth { + + public function decode($body) { + return $this->_decode($body); + } +} + +?> \ No newline at end of file diff --git a/tests/mocks/extensions/service/MockService.php b/tests/mocks/extensions/service/MockService.php new file mode 100644 index 0000000..4c14ac9 --- /dev/null +++ b/tests/mocks/extensions/service/MockService.php @@ -0,0 +1,22 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\tests\mocks\extensions\service; + +class MockService extends \lithium\http\Service { + + public function send($method, $path = null, $data = null, $options = array()) { + if (strpos($path, 'request_token') !== false) { + return 'oauth_token=requestkey&oauth_token_secret=requestsecret'; + } + if (strpos($path, 'access_token') !== false) { + return 'oauth_token=accesskey&oauth_token_secret=accesssecret'; + } + } + +} \ No newline at end of file diff --git a/tests/mocks/extensions/service/MockSocket.php b/tests/mocks/extensions/service/MockSocket.php new file mode 100644 index 0000000..fbe1b70 --- /dev/null +++ b/tests/mocks/extensions/service/MockSocket.php @@ -0,0 +1,56 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oath\tests\mocks\extensions\service; + +class MockSocket extends \lithium\util\Socket { + + public $data = null; + + public function open() { + return true; + } + + public function close() { + return true; + } + + public function eof() { + return true; + } + + public function read() { + return join("\r\n", array( + 'HTTP/1.1 200 OK', + 'Header: Value', + 'Connection: close', + 'Content-Type: text/html;charset=UTF-8', + '', + 'Test!' + )); + } + + public function write($data) { + return $data; + } + + public function timeout($time) { + return true; + } + + public function encoding($charset) { + return true; + } + + public function send($message, $options = array()) { + $this->write($message); + return $this->read(); + } +} + +?> \ No newline at end of file diff --git a/tests/mocks/extensions/service/MockStorage.php b/tests/mocks/extensions/service/MockStorage.php new file mode 100644 index 0000000..d95bdb2 --- /dev/null +++ b/tests/mocks/extensions/service/MockStorage.php @@ -0,0 +1,23 @@ +<?php +/** + * Lithium: the most rad php framework + * + * @copyright Copyright 2009, Union of RAD (http://union-of-rad.org) + * @license http://opensource.org/licenses/bsd-license.php The BSD License + */ + +namespace li3_oauth\tests\mocks\extensions\service; + +class MockStorage extends \lithium\http\Service { + + protected $_data; + + public function read() { + return $this->_data; + } + + public function write($data, $options = array()) { + $this->_data - $data; + } + +} \ No newline at end of file