diff options
| author | Tristan Zur <tzur@webserver.ccwn.org> | 2015-06-10 20:55:53 +0200 |
|---|---|---|
| committer | Tristan Zur <tzur@webserver.ccwn.org> | 2015-06-10 20:55:53 +0200 |
| commit | 406abd7c4df1ace2cd3e4e17159e8941a2e8c0c4 (patch) | |
| tree | a324be16021f44f2fd6d55e609f47024e945b1db /modules/rest | |
Initial import
Diffstat (limited to 'modules/rest')
| -rw-r--r-- | modules/rest/controllers/rest.php | 121 | ||||
| -rw-r--r-- | modules/rest/helpers/registry_rest.php | 30 | ||||
| -rw-r--r-- | modules/rest/helpers/rest.php | 191 | ||||
| -rw-r--r-- | modules/rest/helpers/rest_event.php | 102 | ||||
| -rw-r--r-- | modules/rest/helpers/rest_installer.php | 52 | ||||
| -rw-r--r-- | modules/rest/libraries/Rest_Exception.php | 37 | ||||
| -rw-r--r-- | modules/rest/models/user_access_key.php | 21 | ||||
| -rw-r--r-- | modules/rest/module.info | 8 | ||||
| -rw-r--r-- | modules/rest/views/error_rest.json.php | 6 | ||||
| -rw-r--r-- | modules/rest/views/reset_api_key_confirm.html.php | 7 | ||||
| -rw-r--r-- | modules/rest/views/user_profile_rest.html.php | 13 |
11 files changed, 588 insertions, 0 deletions
diff --git a/modules/rest/controllers/rest.php b/modules/rest/controllers/rest.php new file mode 100644 index 0000000..b3d59e0 --- /dev/null +++ b/modules/rest/controllers/rest.php @@ -0,0 +1,121 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2013 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 { + const ALLOW_PRIVATE_GALLERY = true; + + public function index() { + $username = Input::instance()->post("user"); + $password = Input::instance()->post("password"); + + if (empty($username) || auth::too_many_failures($username)) { + throw new Rest_Exception("Forbidden", 403); + } + + $user = identity::lookup_user_by_name($username); + if (empty($user) || !identity::is_correct_password($user, $password)) { + module::event("user_login_failed", $username); + throw new Rest_Exception("Forbidden", 403); + } + + auth::login($user); + + rest::reply(rest::access_key()); + } + + public function reset_api_key_confirm() { + $form = new Forge("rest/reset_api_key", "", "post", array("id" => "g-reset-api-key")); + $group = $form->group("confirm_reset")->label(t("Confirm resetting your REST API key")); + $group->submit("")->value(t("Reset")); + $v = new View("reset_api_key_confirm.html"); + $v->form = $form; + print $v; + } + + public function reset_api_key() { + access::verify_csrf(); + rest::reset_access_key(); + message::success(t("Your REST API key has been reset.")); + json::reply(array("result" => "success")); + } + + public function __call($function, $args) { + try { + $input = Input::instance(); + $request = new stdClass(); + + switch ($method = strtolower($input->server("REQUEST_METHOD"))) { + case "get": + $request->params = (object) $input->get(); + break; + + default: + $request->params = (object) $input->post(); + if (isset($_FILES["file"])) { + $request->file = upload::save("file"); + system::delete_later($request->file); + } + break; + } + + if (isset($request->params->entity)) { + $request->params->entity = json_decode($request->params->entity); + } + if (isset($request->params->members)) { + $request->params->members = json_decode($request->params->members); + } + + $request->method = strtolower($input->server("HTTP_X_GALLERY_REQUEST_METHOD", $method)); + $request->access_key = $input->server("HTTP_X_GALLERY_REQUEST_KEY"); + + if (empty($request->access_key) && !empty($request->params->access_key)) { + $request->access_key = $request->params->access_key; + } + + $request->url = url::abs_current(true); + if ($suffix = Kohana::config('core.url_suffix')) { + $request->url = substr($request->url, 0, strlen($request->url) - strlen($suffix)); + } + + rest::set_active_user($request->access_key); + + $handler_class = "{$function}_rest"; + $handler_method = $request->method; + + if (!class_exists($handler_class) || !method_exists($handler_class, $handler_method)) { + throw new Rest_Exception("Bad Request", 400); + } + + $response = call_user_func(array($handler_class, $handler_method), $request); + if ($handler_method == "post") { + // post methods must return a response containing a URI. + header("HTTP/1.1 201 Created"); + header("Location: {$response['url']}"); + } + rest::reply($response); + } catch (ORM_Validation_Exception $e) { + // Note: this is totally insufficient because it doesn't take into account localization. We + // either need to map the result values to localized strings in the application code, or every + // client needs its own l10n string set. + throw new Rest_Exception("Bad Request", 400, $e->validation->errors()); + } catch (Kohana_404_Exception $e) { + throw new Rest_Exception("Not Found", 404); + } + } +}
\ No newline at end of file diff --git a/modules/rest/helpers/registry_rest.php b/modules/rest/helpers/registry_rest.php new file mode 100644 index 0000000..2b4087a --- /dev/null +++ b/modules/rest/helpers/registry_rest.php @@ -0,0 +1,30 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2013 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 registry_rest_Core { + static function get($request) { + $results = array(); + foreach (module::active() as $module) { + foreach (glob(MODPATH . "{$module->name}/helpers/*_rest.php") as $filename) { + $results[] = str_replace("_rest.php", "", basename($filename)); + } + } + return array_unique($results); + } +} diff --git a/modules/rest/helpers/rest.php b/modules/rest/helpers/rest.php new file mode 100644 index 0000000..c6be1e1 --- /dev/null +++ b/modules/rest/helpers/rest.php @@ -0,0 +1,191 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2013 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 { + const API_VERSION = "3.0"; + + static function reply($data=array()) { + Session::instance()->abort_save(); + + header("X-Gallery-API-Version: " . rest::API_VERSION); + switch (Input::instance()->get("output", "json")) { + case "json": + json::reply($data); + break; + + case "jsonp": + if (!($callback = Input::instance()->get("callback", ""))) { + throw new Rest_Exception( + "Bad Request", 400, array("errors" => array("callback" => "missing"))); + } + + if (preg_match('/^[$A-Za-z_][0-9A-Za-z_]*$/', $callback) == 1) { + header("Content-type: application/javascript; charset=UTF-8"); + print "$callback(" . json_encode($data) . ")"; + } else { + throw new Rest_Exception( + "Bad Request", 400, array("errors" => array("callback" => "invalid"))); + } + break; + + case "html": + header("Content-type: text/html; charset=UTF-8"); + if ($data) { + $html = preg_replace( + "#([\w]+?://[\w]+[^ \'\"\n\r\t<]*)#ise", "'<a href=\"\\1\" >\\1</a>'", + var_export($data, 1)); + } else { + $html = t("Empty response"); + } + print "<pre>$html</pre>"; + if (gallery::show_profiler()) { + Profiler::enable(); + $profiler = new Profiler(); + $profiler->render(); + } + break; + + default: + throw new Rest_Exception("Bad Request", 400); + } + } + + static function set_active_user($access_key) { + if (empty($access_key)) { + if (module::get_var("rest", "allow_guest_access")) { + identity::set_active_user(identity::guest()); + return; + } else { + throw new Rest_Exception("Forbidden", 403); + } + } + + $key = ORM::factory("user_access_key") + ->where("access_key", "=", $access_key) + ->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); + } + + static function reset_access_key() { + $key = ORM::factory("user_access_key") + ->where("user_id", "=", identity::active_user()->id) + ->find(); + if ($key->loaded()) { + $key->delete(); + } + return rest::access_key(); + } + + static function access_key() { + $key = ORM::factory("user_access_key") + ->where("user_id", "=", identity::active_user()->id) + ->find(); + + if (!$key->loaded()) { + $key->user_id = identity::active_user()->id; + $key->access_key = md5(random::hash() . access::private_key()); + $key->save(); + } + + return $key->access_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) { + if ($suffix = Kohana::config('core.url_suffix')) { + $relative_url = substr($url, strlen(url::abs_site("rest")) - strlen($suffix)); + } else { + $relative_url = substr($url, strlen(url::abs_site("rest"))); + } + + $path = parse_url($relative_url, PHP_URL_PATH); + $components = explode("/", $path, 3); + + if (count($components) != 3) { + throw new Kohana_404_Exception($url); + } + + $class = "$components[1]_rest"; + if (!class_exists($class) || !method_exists($class, "resolve")) { + throw new Kohana_404_Exception($url); + } + + return call_user_func(array($class, "resolve"), !empty($components[2]) ? $components[2] : null); + } + + /** + * 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 (!class_exists($class) || !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") { + if (strpos($url, "?") === false) { + $url .= "?output=html"; + } else { + $url .= "&output=html"; + } + } + return $url; + } + + 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 (class_exists($class) && method_exists($class, "relationships")) { + if ($tmp = call_user_func(array($class, "relationships"), $resource_type, $resource)) { + $results = array_merge($results, $tmp); + } + } + } + } + + return $results; + } +} diff --git a/modules/rest/helpers/rest_event.php b/modules/rest/helpers/rest_event.php new file mode 100644 index 0000000..991e2b6 --- /dev/null +++ b/modules/rest/helpers/rest_event.php @@ -0,0 +1,102 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2013 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) { + db::build() + ->delete("user_access_keys") + ->where("id", "=", $user->id) + ->execute(); + } + + + static function change_provider($new_provider) { + db::build() + ->delete("user_access_keys") + ->execute(); + } + + /** + * 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_key"); + $key->user_id = $user->id; + $key->access_key = random::hash(); + $key->save(); + } + + /** + * Called when admin is editing a user + */ + static function user_edit_form_admin($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_key") + ->where("user_id", "=", $user->id) + ->find(); + + if (!$key->loaded()) { + $key->user_id = $user->id; + $key->access_key = random::hash(); + $key->save(); + } + + $form->edit_user->input("user_access_key") + ->value($key->access_key) + ->readonly("readonly") + ->class("g-form-static") + ->label(t("Remote access key")); + } + + static function show_user_profile($data) { + // Guests can't see a REST key + if (identity::active_user()->guest) { + return; + } + + // Only logged in users can see their own REST key + if (identity::active_user()->id != $data->user->id) { + return; + } + + $view = new View("user_profile_rest.html"); + $key = ORM::factory("user_access_key") + ->where("user_id", "=", $data->user->id) + ->find(); + + if (!$key->loaded()) { + $key->user_id = $data->user->id; + $key->access_key = random::hash(); + $key->save(); + } + $view->rest_key = $key->access_key; + $data->content[] = (object)array("title" => t("REST API"), "view" => $view); + } +} diff --git a/modules/rest/helpers/rest_installer.php b/modules/rest/helpers/rest_installer.php new file mode 100644 index 0000000..96f8acf --- /dev/null +++ b/modules/rest/helpers/rest_installer.php @@ -0,0 +1,52 @@ +<?php defined("SYSPATH") or die("No direct script access."); +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2013 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_keys} ( + `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_var("rest", "allow_guest_access", false); + } + + static function upgrade($version) { + $db = Database::instance(); + if ($version == 1) { + if (in_array("user_access_tokens", Database::instance()->list_tables())) { + $db->query("RENAME TABLE {user_access_tokens} TO {user_access_keys}"); + } + module::set_version("rest", $version = 2); + } + + if ($version == 2) { + module::set_var("rest", "allow_guest_access", false); + module::set_version("rest", $version = 3); + } + } + + static function uninstall() { + Database::instance()->query("DROP TABLE IF EXISTS {user_access_keys}"); + } +} diff --git a/modules/rest/libraries/Rest_Exception.php b/modules/rest/libraries/Rest_Exception.php new file mode 100644 index 0000000..bd08aa8 --- /dev/null +++ b/modules/rest/libraries/Rest_Exception.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-2013 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_Exception_Core extends Kohana_Exception { + var $response = array(); + + public function __construct($message, $code, $response=array()) { + parent::__construct($message, null, $code); + $this->response = $response; + } + + public function sendHeaders() { + if (!headers_sent()) { + header("HTTP/1.1 " . $this->getCode() . " " . $this->getMessage()); + } + } + + public function getTemplate() { + return "error_rest.json"; + } +}
\ No newline at end of file diff --git a/modules/rest/models/user_access_key.php b/modules/rest/models/user_access_key.php new file mode 100644 index 0000000..d464380 --- /dev/null +++ b/modules/rest/models/user_access_key.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-2013 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_Key_Model_Core extends ORM { +} diff --git a/modules/rest/module.info b/modules/rest/module.info new file mode 100644 index 0000000..93a7873 --- /dev/null +++ b/modules/rest/module.info @@ -0,0 +1,8 @@ +name = "REST API Module" +description = "A REST-based API that allows desktop clients and other apps to interact with Gallery 3" + +version = 3 +author_name = "Gallery Team" +author_url = "http://codex.galleryproject.org/Gallery:Team" +info_url = "http://codex.galleryproject.org/Gallery3:Modules:rest" +discuss_url = "http://galleryproject.org/forum_module_rest" diff --git a/modules/rest/views/error_rest.json.php b/modules/rest/views/error_rest.json.php new file mode 100644 index 0000000..8c99ef4 --- /dev/null +++ b/modules/rest/views/error_rest.json.php @@ -0,0 +1,6 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<? +// Log error response to ease debugging +Kohana_Log::add("error", "Rest error details: " . print_r($e->response, 1)); +?> +<?= json_encode($e->response);
\ No newline at end of file diff --git a/modules/rest/views/reset_api_key_confirm.html.php b/modules/rest/views/reset_api_key_confirm.html.php new file mode 100644 index 0000000..3aae2a9 --- /dev/null +++ b/modules/rest/views/reset_api_key_confirm.html.php @@ -0,0 +1,7 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<div id="g-rest-reset-api-key" class="ui-helper-clearfix"> + <p> + <?= t("Do you really want to reset your REST API key? Any clients that use this key will need to be updated with the new value.") ?> + </p> + <?= $form ?> +</div> diff --git a/modules/rest/views/user_profile_rest.html.php b/modules/rest/views/user_profile_rest.html.php new file mode 100644 index 0000000..3e5d3db --- /dev/null +++ b/modules/rest/views/user_profile_rest.html.php @@ -0,0 +1,13 @@ +<?php defined("SYSPATH") or die("No direct script access.") ?> +<div id="g-rest-detail"> + <ul> + <li id="g-rest-key"> + <p> + <?= t("<b>Key</b>: %key", array("key" => $rest_key)) ?> + <a class="g-button ui-state-default ui-corner-all g-dialog-link" href="<?= url::site("rest/reset_api_key_confirm") ?>"> + <?= t("reset") ?> + </a> + </p> + </li> + </ul> +</div> |
