diff options
Diffstat (limited to 'modules/rest')
-rw-r--r-- | modules/rest/controllers/rest.php | 75 | ||||
-rw-r--r-- | modules/rest/helpers/rest.php | 156 | ||||
-rw-r--r-- | modules/rest/libraries/Rest_Exception.php | 20 | ||||
-rw-r--r-- | modules/rest/tests/Rest_Controller_Test.php | 249 |
4 files changed, 225 insertions, 275 deletions
diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php index 26e5b31a..ba996b84 100644 --- a/modules/rest/controllers/rest.php +++ b/modules/rest/controllers/rest.php @@ -18,51 +18,54 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class Rest_Controller extends Controller { - public function access_key() { - try { - $request = (object)Input::instance()->get(); - if (empty($request->user) || empty($request->password)) { - throw new Rest_Exception(403, "Forbidden"); - } + public function index() { + $username = Input::instance()->post("user"); + $password = Input::instance()->post("password"); - $user = identity::lookup_user_by_name($request->user); - if (empty($user)) { - throw new Rest_Exception(403, "Forbidden"); - } + $user = identity::lookup_user_by_name($username); + if (empty($user) || !identity::is_correct_password($user, $password)) { + throw new Rest_Exception("Forbidden", 403); + } - if (!identity::is_correct_password($user, $request->password)) { - throw new Rest_Exception(403, "Forbidden"); - } + $key = rest::get_access_token($user->id); + rest::reply($key->access_key); + } + + public function __call($function, $args) { + $input = Input::instance(); + switch ($method = strtolower($input->server("REQUEST_METHOD"))) { + case "get": + $request->params = (object) $input->get(); + break; - $key = ORM::factory("user_access_token") - ->where("user_id", "=", $user->id) - ->find(); - if (!$key->loaded()) { - $key->user_id = $user->id; - $key->access_key = md5($user->name . rand()); - $key->save(); + case "post": + $request->params = (object) $input->post(); + if (isset($_FILES["file"])) { + $request->file = upload::save("file"); } - print rest::success(array("token" => $key->access_key)); - } catch (Rest_Exception $e) { - $e->sendHeaders(); + break; } - } - public function __call($function, $args) { - $request = rest::normalize_request($args); - try { - if (rest::set_active_user($request->access_token)) { - $handler_class = "{$function}_rest"; - $handler_method = $request->method; + $request->method = strtolower($input->server("HTTP_X_GALLERY_REQUEST_METHOD", $method)); + $request->access_token = $input->server("HTTP_X_GALLERY_REQUEST_KEY"); + $request->url = url::abs_current(true); + + rest::set_active_user($request->access_token); - if (!method_exists($handler_class, $handler_method)) { - throw new Rest_Exception(403, "Forbidden"); - } + $handler_class = "{$function}_rest"; + $handler_method = $request->method; - print call_user_func(array($handler_class, $handler_method), $request); + if (!method_exists($handler_class, $handler_method)) { + throw new Rest_Exception("Bad Request", 400); + } + + try { + print rest::reply(call_user_func(array($handler_class, $handler_method), $request)); + } catch (ORM_Validation_Exception $e) { + foreach ($e->validation->errors() as $key => $value) { + $msgs[] = "$key: $value"; } - } catch (Rest_Exception $e) { - $e->sendHeaders(); + throw new Rest_Exception("Bad Request: " . join(", ", $msgs), 400); } } }
\ No newline at end of file diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php index be0644f2..0d2ec9d4 100644 --- a/modules/rest/helpers/rest.php +++ b/modules/rest/helpers/rest.php @@ -18,87 +18,119 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class rest_Core { - /** - * Request failed - */ - static function fail($log_message=null) { - if (!empty($log_message)) { - Kohana_Log::add("info", $log_message); - } - // We don't need to save the session for this request + static function reply($data=array()) { Session::abort_save(); - return json_encode(array("status" => "ERROR", "message" => (string)$message)); + + if ($data) { + if (Input::instance()->get("output") == "html") { + header("Content-type: text/html"); + $html = preg_replace( + "#(^|[\n ])([\w]+?://[\w]+[^ \"\n\r\t<]*)#ise", "'\\1<a href=\"\\2\" >\\2</a>'", + print_r($data, 1)); + print "<pre>$html</pre>"; + } else { + header("Content-type: application/json"); + print json_encode($data); + } + } } - /** - * Success - */ - static function success($response_data=array(), $message=null) { - $response = array("status" => "OK"); - if (!empty($message)) { - $response["message"] = (string)$message; + static function set_active_user($access_token) { + if (empty($access_token)) { + identity::set_active_user(identity::guest()); + return; } - $response = array_merge($response, $response_data); - // We don't need to save the session for this request - Session::abort_save(); - return json_encode($response); + $key = ORM::factory("user_access_token") + ->where("access_key", "=", $access_token) + ->find(); + + if (!$key->loaded()) { + throw new Rest_Exception("Forbidden", 403); + } + + $user = identity::lookup_user($key->user_id); + if (empty($user)) { + throw new Rest_Exception("Forbidden", 403); + } + + identity::set_active_user($user); } - /** - * Validation Error - */ - static function validation_error($error_data) { - $response = array("status" => "VALIDATE_ERROR"); - $response = array_merge($response, array("fields" => $error_data)); + static function get_access_token($user_id) { + $key = ORM::factory("user_access_token") + ->where("user_id", "=", $user_id) + ->find(); - // We don't need to save the session for this request - Session::abort_save(); - return json_encode($response); + if (!$key->loaded()) { + $key->user_id = $user_id; + $key->access_key = md5(rand()); + $key->save(); + } + return $key; } + /** + * Convert a REST url into an object. + * Eg: + * http://example.com/gallery3/index.php/rest/item/35 -> Item_Model + * http://example.com/gallery3/index.php/rest/tag/16 -> Tag_Model + * http://example.com/gallery3/index.php/rest/tagged_item/1,16 -> [Tag_Model, Item_Model] + * + * @param string the fully qualified REST url + * @return mixed the corresponding object (usually a model of some kind) + */ + static function resolve($url) { + $relative_url = substr($url, strlen(url::abs_site("rest"))); + $path = parse_url($relative_url, PHP_URL_PATH); + $components = explode("/", $path, 3); - static function normalize_request($args=array()) { - $input = Input::instance(); - $method = strtolower($input->server("REQUEST_METHOD")); - $request = new stdClass(); - foreach (array_keys($input->get()) as $key) { - $request->$key = $input->get($key); + if (count($components) != 3) { + throw new Kohana_404_Exception($url); } - if ($method != "get") { - foreach (array_keys($input->post()) as $key) { - $request->$key = $input->post($key); - } - foreach (array_keys($_FILES) as $key) { - $request->$key = $_FILES[$key]; - } + + $class = "$components[1]_rest"; + if (!method_exists($class, "resolve")) { + throw new Kohana_404_Exception($url); } - $request->method = strtolower($input->server("HTTP_X_GALLERY_REQUEST_METHOD", $method)); - $request->access_token = $input->server("HTTP_X_GALLERY_REQUEST_KEY"); - $request->arguments = $args; // Let the rest handler figure out what the arguments mean + return call_user_func(array($class, "resolve"), !empty($components[2]) ? $components[2] : null); + } - return $request; + /** + * Return an absolute url used for REST resource location. + * @param string resource type (eg, "item", "tag") + * @param object resource + */ + static function url() { + $args = func_get_args(); + $resource_type = array_shift($args); + + $class = "{$resource_type}_rest"; + if (!method_exists($class, "url")) { + throw new Rest_Exception("Bad Request", 400); + } + + $url = call_user_func_array(array($class, "url"), $args); + if (Input::instance()->get("output") == "html") { + $url .= "?output=html"; + } + return $url; } - static function set_active_user($access_token) { - if (empty($access_token)) { - $user = identity::guest(); - } else { - $key = ORM::factory("user_access_token") - ->where("access_key", "=", $access_token) - ->find(); - - if ($key->loaded()) { - $user = identity::lookup_user($key->user_id); - if (empty($user)) { - throw new Rest_Exception(403, "Forbidden"); + static function relationships($resource_type, $resource) { + $results = array(); + foreach (module::active() as $module) { + foreach (glob(MODPATH . "{$module->name}/helpers/*_rest.php") as $filename) { + $class = str_replace(".php", "", basename($filename)); + if (method_exists($class, "relationships")) { + $results = array_merge( + $results, + call_user_func(array($class, "relationships"), $resource_type, $resource)); } - } else { - throw new Rest_Exception(403, "Forbidden"); } } - identity::set_active_user($user); - return true; + + return $results; } } diff --git a/modules/rest/libraries/Rest_Exception.php b/modules/rest/libraries/Rest_Exception.php index 905b94a0..c3548b7e 100644 --- a/modules/rest/libraries/Rest_Exception.php +++ b/modules/rest/libraries/Rest_Exception.php @@ -17,20 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Rest_Exception_Core extends Exception { - /** - * Set internal properties. - */ - public function __construct($code, $text) { - parent::__construct("$code $text"); +class Rest_Exception_Core extends Kohana_Exception { + public function __construct($message, $code) { + parent::__construct($message, null, $code); } - /** - * Sends the headers, to emulate server behavior. - * - * @return void - */ public function sendHeaders() { - header('HTTP/1.1 {$this->getMessage()}'); + if (!headers_sent()) { + header("HTTP/1.1 " . $this->getCode() . " " . $this->getMessage()); + } } -} // End Rest Exception
\ No newline at end of file +}
\ No newline at end of file diff --git a/modules/rest/tests/Rest_Controller_Test.php b/modules/rest/tests/Rest_Controller_Test.php index 83bd9db6..5e624112 100644 --- a/modules/rest/tests/Rest_Controller_Test.php +++ b/modules/rest/tests/Rest_Controller_Test.php @@ -17,208 +17,129 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -class Rest_Controller_Test extends Unit_Test_Case { +class Rest_Controller_Test extends Gallery_Unit_Test_Case { public function setup() { $this->_save = array($_GET, $_POST, $_SERVER); } - private function _create_user() { - if (empty($this->_user)) { - $this->_user = identity::create_user("access_test" . rand(), "Access Test", "password"); - $this->_key = ORM::factory("user_access_token"); - $this->_key->access_key = md5($this->_user->name . rand()); - $this->_key->user_id = $this->_user->id; - $this->_key->save(); - identity::set_active_user($this->_user); - } - return array($this->_key->access_key, $this->_user); - } - public function teardown() { list($_GET, $_POST, $_SERVER) = $this->_save; - if (!empty($this->_user)) { - try { - $this->_user->delete(); - } catch (Exception $e) { } - } - } - - private function _create_image($parent=null) { - $filename = MODPATH . "gallery/tests/test.jpg"; - $image_name = "image_" . rand(); - if (empty($parent)) { - $parent = ORM::factory("item", 1); - } - return photo::create($parent, $filename, "$image_name.jpg", $image_name); } - public function rest_access_key_exists_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_GET["user"] = $user->name;; - $_GET["password"] = "password"; + public function login_test() { + $user = test::random_user("password"); - $this->assert_equal( - json_encode(array("status" => "OK", "token" => $access_key)), - $this->_call_controller()); - } + // There's no access key at first + $this->assert_false( + ORM::factory("user_access_token")->where("user_id", "=", $user->id)->find()->loaded()); - public function rest_access_key_generated_test() { - list ($access_key, $user) = $this->_create_user(); - ORM::factory("user_access_token") - ->where("access_key", $access_key) - ->delete(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_GET["user"] = $user->name; - $_GET["password"] = "password"; + $_POST["user"] = $user->name; + $_POST["password"] = "password"; - $results = json_decode($this->_call_controller()); + $response = test::call_and_capture(array(new Rest_Controller(), "index")); + $expected = + ORM::factory("user_access_token")->where("user_id", "=", $user->id)->find()->access_key; - $this->assert_equal("OK", $results->status); - $this->assert_false(empty($results->token)); + // Now there is an access key, and it was returned + $this->assert_equal(json_encode($expected), $response); } - public function rest_access_key_no_parameters_test() { - $_SERVER["REQUEST_METHOD"] = "GET"; + public function login_failed_test() { + $user = test::random_user("password"); try { - $this->_call_controller(); + $_POST["user"] = $user->name; + $_POST["password"] = "WRONG PASSWORD"; + test::call_and_capture(array(new Rest_Controller(), "index")); } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); + $this->assert_equal(403, $e->getCode()); + return; } - } - - public function rest_access_key_user_not_found_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; - $_POST["request"] = json_encode(array("user" => "access_test2", "password" => "password")); - try { - $this->_call_controller(); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_true(false, "Shouldn't get here"); } - public function rest_access_key_invalid_password_test() { - $_SERVER["REQUEST_METHOD"] = "POST"; + public function get_test() { + $_SERVER["REQUEST_METHOD"] = "GET"; + $_GET["key"] = "value"; - try { - $this->_call_controller(); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "get", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); } - public function rest_get_resource_no_request_key_test() { - $_SERVER["REQUEST_METHOD"] = "GET"; - $photo = $this->_create_image(); - - $this->assert_equal( - json_encode(array("status" => "OK", "message" => (string)t("Processed"), - "photo" => array("path" => $photo->relative_url(), - "title" => $photo->title, - "thumb_url" => $photo->thumb_url(), - "description" => $photo->description, - "internet_address" => $photo->slug))), - $this->_call_controller("rest", explode("/", $photo->relative_url()))); - } + public function get_with_access_key_test() { + $key = rest::get_access_token(1); // admin user - public function rest_get_resource_invalid_key_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = md5($access_key); // screw up the access key; $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $key->access_key; + $_GET["key"] = "value"; - try { - $this->_call_controller(); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "get", + "access_token" => $key->access_key, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); } - public function rest_get_resource_no_user_for_key_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $access_key; + public function post_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_POST["key"] = "value"; - $user->delete(); + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "post", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); + } - $photo = $this->_create_image(); + public function put_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "put"; + $_POST["key"] = "value"; - try { - $this->_call_controller("rest", explode("/", $photo->relative_url())); - } catch (Rest_Exception $e) { - $this->assert_equal("403 Forbidden", $e->getMessage()); - } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); - } + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "put", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); } - public function rest_get_resource_no_handler_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $access_key; - $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "PUT"; - $photo = $this->_create_image(); + public function delete_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "delete"; + $_POST["key"] = "value"; + $this->assert_array_equal_to_json( + array("params" => array("key" => "value"), + "method" => "delete", + "access_token" => null, + "url" => "http://./index.php/gallery_unit_test"), + test::call_and_capture(array(new Rest_Controller(), "mock"))); + } + + public function bogus_method_test() { + $_SERVER["REQUEST_METHOD"] = "POST"; + $_SERVER["HTTP_X_GALLERY_REQUEST_METHOD"] = "BOGUS"; try { - $this->_call_controller("rest", explode("/", $photo->relative_url())); - } catch (Rest_Exception $e) { - $this->assert_equal("501 Not Implemented", $e->getMessage()); + test::call_and_capture(array(new Rest_Controller(), "mock")); } catch (Exception $e) { - $this->assert_false(true, $e->__toString()); + $this->assert_equal(400, $e->getCode()); + return; } - } - - public function rest_get_resource_test() { - list ($access_key, $user) = $this->_create_user(); - $_SERVER["REQUEST_METHOD"] = "GET"; - $_SERVER["HTTP_X_GALLERY_REQUEST_KEY"] = $access_key; - - $photo = $this->_create_image(); - $this->assert_equal( - json_encode(array("status" => "OK", "message" => (string)t("Processed"), - "photo" => array("path" => $photo->relative_url(), - "title" => $photo->title, - "thumb_url" => $photo->thumb_url(), - "description" => $photo->description, - "internet_address" => $photo->slug))), - $this->_call_controller("rest", explode("/", $photo->relative_url()))); - } - - private function _call_controller($method="access_key", $arg=null) { - $controller = new Rest_Controller(); - - ob_start(); - call_user_func_array(array($controller, $method), $arg); - $results = ob_get_contents(); - ob_end_clean(); - - return $results; + $this->assert_true(false, "Shouldn't get here"); } } -class rest_rest { - static $request = null; - - static function get($request) { - self::$request = $request; - $item = ORM::factory("item") - ->where("relative_url_cache", "=", implode("/", $request->arguments)) - ->find(); - $response["path"] = $item->relative_url(); - $response["title"] = $item->title; - $response["thumb_url"] = $item->thumb_url(); - $response["description"] = $item->description; - $response["internet_address"] = $item->slug; - return rest::success(array($item->type => $response), t("Processed")); - } - -} +class mock_rest { + function get($request) { return $request; } + function post($request) { return $request; } + function put($request) { return $request; } + function delete($request) { return $request; } +}
\ No newline at end of file |