Commit: e167b7527764b3cfc045527716a85ff18ebf0e44
Author: gwoo | Date: 2009-11-27 15:24:41 -0800
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