summaryrefslogtreecommitdiff
path: root/modules/rest
diff options
context:
space:
mode:
Diffstat (limited to 'modules/rest')
-rw-r--r--modules/rest/controllers/rest.php68
-rw-r--r--modules/rest/helpers/rest.php105
-rw-r--r--modules/rest/helpers/rest_event.php75
-rw-r--r--modules/rest/helpers/rest_installer.php37
-rw-r--r--modules/rest/libraries/Rest_Exception.php41
-rw-r--r--modules/rest/models/user_access_token.php21
-rw-r--r--modules/rest/module.info4
-rw-r--r--modules/rest/tests/Rest_Controller_Test.php217
8 files changed, 568 insertions, 0 deletions
diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php
new file mode 100644
index 00000000..446ec7cb
--- /dev/null
+++ b/modules/rest/controllers/rest.php
@@ -0,0 +1,68 @@
+<?php defined("SYSPATH") or die("No direct script access.");/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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 extends Controller {
+ public function access_key() {
+ $request = (object)Input::instance()->get();
+ if (empty($request->user) || empty($request->password)) {
+ Rest_Exception::trigger(403, "Forbidden", "No user or password supplied");
+ }
+
+ $user = identity::lookup_user_by_name($request->user);
+ if (empty($user)) {
+ Rest_Exception::trigger(403, "Forbidden", "User '{$request->user}' not found");
+ return;
+ }
+
+ if (!identity::is_correct_password($user, $request->password)) {
+ Rest_Exception::trigger(403, "Forbidden", "Invalid password for '{$request->user}'.");
+ return;
+ }
+
+ $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();
+ }
+ print rest::success(array("token" => $key->access_key));
+ }
+
+ 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;
+
+ if (!method_exists($handler_class, $handler_method)) {
+ Rest_Exception::trigger(501, "Not implemented", "$handler_class::$handler_method");
+ }
+
+ print call_user_func(array($handler_class, $handler_method), $request);
+ }
+ } catch (Rest_Exception $e) {
+ $e->sendHeaders();
+ } catch (Exception $e) {
+ Kohana_Log::add("error", $e->__toString());
+ header("HTTP/1.1 500 Internal Error");
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php
new file mode 100644
index 00000000..7e2445e4
--- /dev/null
+++ b/modules/rest/helpers/rest.php
@@ -0,0 +1,105 @@
+<?php defined("SYSPATH") or die("No direct script access.");/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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_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
+ Session::abort_save();
+ return json_encode(array("status" => "ERROR", "message" => (string)$message));
+ }
+
+ /**
+ * Success
+ */
+ static function success($response_data=array(), $message=null) {
+ $response = array("status" => "OK");
+ if (!empty($message)) {
+ $response["message"] = (string)$message;
+ }
+ $response = array_merge($response, $response_data);
+
+ // We don't need to save the session for this request
+ Session::abort_save();
+ return json_encode($response);
+ }
+
+ /**
+ * Validation Error
+ */
+ static function validation_error($error_data) {
+ $response = array("status" => "VALIDATE_ERROR");
+ $response = array_merge($response, array("fields" => $error_data));
+
+ // We don't need to save the session for this request
+ Session::abort_save();
+ return json_encode($response);
+ }
+
+
+ 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 ($method != "get") {
+ foreach (array_keys($input->post()) as $key) {
+ $request->$key = $input->post($key);
+ }
+ foreach (array_keys($_FILES) as $key) {
+ $request->$key = $_FILES[$key];
+ }
+ }
+
+ $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 $request;
+ }
+
+ 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)) {
+ Rest_Exception::trigger(403, "Forbidden", $log_message,
+ "User not found: {$key->user_id}");
+ }
+ } else {
+ Rest_Exception::trigger(403, "Forbidden", $log_message,
+ "Invalid user access token supplied: {$key->user_id}");
+ }
+ }
+ identity::set_active_user($user);
+ return true;
+ }
+}
diff --git a/modules/rest/helpers/rest_event.php b/modules/rest/helpers/rest_event.php
new file mode 100644
index 00000000..a06f43ea
--- /dev/null
+++ b/modules/rest/helpers/rest_event.php
@@ -0,0 +1,75 @@
+<?php defined("SYSPATH") or die("No direct script access.");/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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_event {
+ /**
+ * Called just before a user is deleted. This will remove the user from
+ * the user_homes directory.
+ */
+ static function user_before_delete($user) {
+ ORM::factory("user_access_token")
+ ->where("id", "=", $user->id)
+ ->delete_all();
+ }
+
+ /**
+ * Called after a user has been added. Just add a remote access key
+ * on every add.
+ */
+ static function user_add_form_admin_completed($user, $form) {
+ $key = ORM::factory("user_access_token");
+ $key->user_id = $user->id;
+ $key->access_key = md5($user->name . rand());
+ $key->save();
+ }
+
+ /**
+ * Called when admin is editing a user
+ */
+ static function user_edit_form_admin($user, $form) {
+ self::_get_access_key_form($user, $form);
+ }
+
+ /**
+ * Called when user is editing their own form
+ */
+ static function user_edit_form($user, $form) {
+ self::_get_access_key_form($user, $form);
+ }
+
+ /**
+ * Get the form fields for user edit
+ */
+ static function _get_access_key_form($user, $form) {
+ $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();
+ }
+
+ $form->edit_user->input("user_access_token")
+ ->value($key->access_key)
+ ->readonly("readonly")
+ ->class("g-form-static")
+ ->label(t("Remote access key"));
+ }
+}
diff --git a/modules/rest/helpers/rest_installer.php b/modules/rest/helpers/rest_installer.php
new file mode 100644
index 00000000..9fbc5b2e
--- /dev/null
+++ b/modules/rest/helpers/rest_installer.php
@@ -0,0 +1,37 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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_installer {
+ static function install() {
+ Database::instance()
+ ->query("CREATE TABLE {user_access_tokens} (
+ `id` int(9) NOT NULL auto_increment,
+ `user_id` int(9) NOT NULL,
+ `access_key` char(32) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY(`access_key`),
+ UNIQUE KEY(`user_id`))
+ DEFAULT CHARSET=utf8;");
+ module::set_version("rest", 1);
+ }
+
+ static function uninstall() {
+ Database::instance()->query("DROP TABLE IF EXISTS {user_access_tokens}");
+ }
+}
diff --git a/modules/rest/libraries/Rest_Exception.php b/modules/rest/libraries/Rest_Exception.php
new file mode 100644
index 00000000..acdcb568
--- /dev/null
+++ b/modules/rest/libraries/Rest_Exception.php
@@ -0,0 +1,41 @@
+<?php defined('SYSPATH') OR die('No direct access allowed.');
+/**
+ * Creates a "Page Not Found" exception.
+ *
+ * $Id: Kohana_404_Exception.php 4679 2009-11-10 01:45:52Z isaiah $
+ *
+ * @package Core
+ * @author Kohana Team
+ * @copyright (c) 2007-2009 Kohana Team
+ * @license http://kohanaphp.com/license
+ */
+
+class Rest_Exception_Core extends Exception {
+ /**
+ * Set internal properties.
+ */
+ public function __construct($code, $text) {
+ parent::__construct("$code $text");
+ }
+
+ /**
+ * Throws a new Rest exception.
+ *
+ * @throws Rest_Exception
+ * @return void
+ */
+ public static function trigger($code, $text, $log_message=null) {
+ $message = "$code: $text" . (!empty($log_message) ? "\n$log_message" : "");
+ Kohana_Log::add("info", $message);
+ throw new Rest_Exception($code, $text);
+ }
+
+ /**
+ * Sends the headers, to emulate server behavior.
+ *
+ * @return void
+ */
+ public function sendHeaders() {
+ header('HTTP/1.1 {$this->getMessage()}');
+ }
+} // End Rest Exception \ No newline at end of file
diff --git a/modules/rest/models/user_access_token.php b/modules/rest/models/user_access_token.php
new file mode 100644
index 00000000..5669d8d1
--- /dev/null
+++ b/modules/rest/models/user_access_token.php
@@ -0,0 +1,21 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+class User_Access_Token_Model extends ORM {
+}
diff --git a/modules/rest/module.info b/modules/rest/module.info
new file mode 100644
index 00000000..45bd79e4
--- /dev/null
+++ b/modules/rest/module.info
@@ -0,0 +1,4 @@
+name = "REST Access Module"
+description = "The RESTful implementation/interface to Gallery3"
+
+version = 1
diff --git a/modules/rest/tests/Rest_Controller_Test.php b/modules/rest/tests/Rest_Controller_Test.php
new file mode 100644
index 00000000..3e0c4063
--- /dev/null
+++ b/modules/rest/tests/Rest_Controller_Test.php
@@ -0,0 +1,217 @@
+<?php defined("SYSPATH") or die("No direct script access.");
+/**
+ * Gallery - a web based photo album viewer and editor
+ * Copyright (C) 2000-2009 Bharat Mediratta
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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 {
+ public function setup() {
+ $this->_save = array($_GET, $_POST, $_SERVER);
+ }
+
+ private function _create_user() {
+ $user = identity::create_user("access_test" . rand(), "Access Test", "password");
+ $key = ORM::factory("user_access_token");
+ $key->access_key = md5($user->name . rand());
+ $key->user_id = $user->id;
+ $key->save();
+ return array($key->access_key, $user);
+ }
+
+ 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 teardown() {
+ list($_GET, $_POST, $_SERVER) = $this->_save;
+ }
+
+ 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";
+
+ $this->assert_equal(
+ json_encode(array("status" => "OK", "token" => $access_key)),
+ $this->_call_controller());
+ }
+
+ 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";
+
+ $results = json_decode($this->_call_controller());
+
+ $this->assert_equal("OK", $results->status);
+ $this->assert_false(empty($results->token));
+ }
+
+ public function rest_access_key_no_parameters_test() {
+ $_SERVER["REQUEST_METHOD"] = "GET";
+
+ try {
+ $this->_call_controller();
+ } catch (Rest_Exception $e) {
+ $this->assert_equal("403 Forbidden", $e->getMessage());
+ } catch (Exception $e) {
+ $this->assert_false(true, $e->__toString());
+ }
+ }
+
+ 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());
+ }
+ }
+
+ public function rest_access_key_invalid_password_test() {
+ $_SERVER["REQUEST_METHOD"] = "POST";
+
+ try {
+ $this->_call_controller();
+ } catch (Rest_Exception $e) {
+ $this->assert_equal("403 Forbidden", $e->getMessage());
+ } catch (Exception $e) {
+ $this->assert_false(true, $e->__toString());
+ }
+ }
+
+ 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 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";
+
+ try {
+ $this->_call_controller();
+ } catch (Rest_Exception $e) {
+ $this->assert_equal("403 Forbidden", $e->getMessage());
+ } catch (Exception $e) {
+ $this->assert_false(true, $e->__toString());
+ }
+ }
+
+ 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;
+
+ $user->delete();
+
+ $photo = $this->_create_image();
+
+ 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());
+ }
+ }
+
+ 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();
+
+ try {
+ $this->_call_controller("rest", explode("/", $photo->relative_url()));
+ } catch (Rest_Exception $e) {
+ $this->assert_equal("501 Not Implemented", $e->getMessage());
+ } catch (Exception $e) {
+ $this->assert_false(true, $e->__toString());
+ }
+ }
+
+ 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;
+ }
+}
+
+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"));
+ }
+
+}