diff options
author | Daniel Hoffend <dh@dotlan.net> | 2015-02-14 01:59:36 (GMT) |
---|---|---|
committer | Daniel Hoffend <dh@dotlan.net> | 2015-02-14 02:02:47 (GMT) |
commit | 6c11665338354181ab1f4eb8db9166a0df9d28cd (patch) | |
tree | 67088af605ccb679b15c2b4ee8cdeed874dc2084 | |
parent | 79b9ed1f6f80683ec8b57becf3f4a662cbcd274e (diff) | |
download | kolab-wap-6c11665338354181ab1f4eb8db9166a0df9d28cd.tar.gz |
add initial version of hooks to support external checks on api calls
-rw-r--r-- | doc/sample-hooks.php | 92 | ||||
-rw-r--r-- | lib/api/kolab_api_service_form_value.php | 27 | ||||
-rw-r--r-- | lib/kolab_api_controller.php | 26 | ||||
-rw-r--r-- | lib/kolab_hooks.php | 131 | ||||
-rw-r--r-- | public_html/api/index.php | 2 | ||||
-rw-r--r-- | public_html/index.php | 2 |
6 files changed, 274 insertions, 6 deletions
diff --git a/doc/sample-hooks.php b/doc/sample-hooks.php new file mode 100644 index 0000000..33a8780 --- /dev/null +++ b/doc/sample-hooks.php @@ -0,0 +1,92 @@ +<?php + +/** + * this is an example hooks file to show how you can limit the + * maximum numbers of accounts and maximum overall quota for a domain + * + * to use this example, copy this file to /etc/kolab/hooks.php and + * adjust your kolab.conf + * + * ---------------------------------------- + * /etc/kolab/kolab.conf + * ---------------------------------------- + * [kolab_wap] + * custom_hooks = /etc/kolab/hooks.phpA + * ... + * [example.org] + * max_accounts = 10 + * max_quota = 10 + * ... + * + * [example.net] + * max_accounts = 5 + * max_quota = 50 + * ---------------------------------------- + * + * keep in mind this is an example. Feel free to adjust everything to your needs + * or attach it to your external billing system. Also this example might not be + * suited for installations with multiple thousend users. + */ + +/** + * hook to limit the maximum amount of accounts + */ +kolab_hooks::register('kolab_api_service_user::user_add', function($args) { + $auth = Auth::get_instance(); + $conf = Conf::get_instance(); + $basedn = $auth->subject_base_dn('user',false); + $max = (int)$conf->get($auth->domain, 'max_accounts'); + + // check maximum of accounts + $count = $auth->search($basedn, 'objectclass=kolabinetorgperson','sub',array('dn'))->count(); + if ($max && $count >= $max) { + throw new Exception('Maximum of '.$max.' accounts has been reached'); + } + + return $args; +}); + +/** + * hook to limit the overall quota for a given domain + */ +kolab_hooks::register('kolab_api_service_form_value::validate_mailquota', function($args) { + // this hook only applies to users + if ($args['postdata']['object_type'] != 'user') { + return $args; + } + + $auth = Auth::get_instance(); + $conf = Conf::get_instance(); + $basedn = $auth->subject_base_dn('user',false); + $max = (int)$conf->get($auth->domain, 'max_quota'); + + // unlimited quota not allowed if max_quota set + if ($max && $args['value'] <= 0) { + throw new Exception('Unlimited Mailquota not allowed'); + } + + // check overall quota + $qattr = $conf->get("quota_attribute"); + $users = $auth->search($basedn, 'objectclass=kolabinetorgperson','sub',array($qattr)); + $sum = 0; + foreach ($users AS $user) { + // skip current user + if (strtolower($user['dn']) == strtolower('uid='.$args['postdata']['uid'].','.$args['postdata']['ou'])) { + continue; + } + + if (isset($user[$qattr][0])) { + $sum += $user[$qattr][0]; + } + } + + // add new current user quota + $sum += $args['value']; + + // throw error if maximum quota has been reached + if ($max && $sum > $max) { + throw new Exception('Overall Mailquota for your domain has been reached: '.$sum.' > '.$max); + } + + return $args; +}); diff --git a/lib/api/kolab_api_service_form_value.php b/lib/api/kolab_api_service_form_value.php index 230cff6..c36f359 100644 --- a/lib/api/kolab_api_service_form_value.php +++ b/lib/api/kolab_api_service_form_value.php @@ -1198,7 +1198,12 @@ class kolab_api_service_form_value extends kolab_api_service $this->_email_addresses_in_use($value, 'alias', $postdata); } - return 'OK'; + return kolab_hooks::run(__METHOD__, array( + 'value' => $value, + 'postdata' => $postdata, + 'validation_type' => $validation_type, + 'return' => 'OK' + ))['return']; } private function validate_associateddomain($value, $postdata = array(), $validation_type = null) @@ -1253,7 +1258,12 @@ class kolab_api_service_form_value extends kolab_api_service } } - return 'OK'; + return kolab_hooks::run(__METHOD__, array( + 'value' => $value, + 'postdata' => $postdata, + 'validation_type' => $validation_type, + 'return' => 'OK' + ))['return']; } private function validate_mailquota($value, $postdata = array(), $validation_type = null) @@ -1267,7 +1277,11 @@ class kolab_api_service_form_value extends kolab_api_service } } - return (string) intval($value); + return (string) intval(kolab_hooks::run(__METHOD__, array( + 'value' => $value, + 'postdata' => $postdata, + 'validation_type' => $validation_type + ))['value']); } private function validate_mailalternateaddress($value, $postdata = array(), $validation_type = null) @@ -1296,7 +1310,12 @@ class kolab_api_service_form_value extends kolab_api_service } } - return 'OK'; + return kolab_hooks::run(__METHOD__, array( + 'value' => $value, + 'postdata' => $postdata, + 'validation_type' => $validation_type, + 'return' => 'OK' + ))['return']; } private function _list_options_members($postdata, $attribs = array()) diff --git a/lib/kolab_api_controller.php b/lib/kolab_api_controller.php index ed716cf..9ce4708 100644 --- a/lib/kolab_api_controller.php +++ b/lib/kolab_api_controller.php @@ -166,17 +166,39 @@ class kolab_api_controller // get only public methods $service_methods = get_class_methods($service_handler); + // determine the method if (in_array($method, $service_methods)) { - $result = $service_handler->$method($_GET, $postdata); + $call_method = $method; } else if (in_array($service . "_" . $method, $service_methods)) { $call_method = $service . "_" . $method; - $result = $service_handler->$call_method($_GET, $postdata); } else { throw new Exception("Unknown method", 405); } + // checks before api execution + $ret = kolab_hooks::run( + get_class($service_handler).'::'.$call_method, + array( + '_GET' => $_GET, + 'postdata' => $postdata + ) + ); + + // execute api call + $result = $service_handler->$call_method($ret['_GET'], $ret['postdata']); + + // post api run hooks + $result = kolab_hooks::run( + get_class($service_handler).'::'.$call_method.'::post', + array( + 'result' => $result, + '_GET' => $ret['_GET'], + 'postdata' => $ret['postdata'] + ) + )['result']; + // send response if ($result !== false) { $this->output->success($result); diff --git a/lib/kolab_hooks.php b/lib/kolab_hooks.php new file mode 100644 index 0000000..1ffff31 --- /dev/null +++ b/lib/kolab_hooks.php @@ -0,0 +1,131 @@ +<?php +/* + +--------------------------------------------------------------------------+ + | This file is part of the Kolab Web Admin Panel | + | | + | Copyright (C) 2011-2012, Kolab Systems AG | + | | + | This program is free software: you can redistribute it and/or modify | + | it under the terms of the GNU Affero General Public License as published | + | by the Free Software Foundation, either version 3 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 Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public License | + | along with this program. If not, see <http://www.gnu.org/licenses/> | + +--------------------------------------------------------------------------+ + | Author: Daniel Hoffend <dh@dotlan.net> | + +--------------------------------------------------------------------------+ +*/ + +class kolab_hooks { + + /** + * contains all registeres callbacks + * + * @var array + */ + private static $handlers = array(); + + /** + * contains the list of current executions, used for loop prevention + */ + private static $stack = array(); + + /** + * initialize the hook system + * if kolab_wap.custom_hooks is set an points to a custom php file + * it will be included + * + * example: + * [kolab_wap] + * custom_hooks = /etc/kolab/hooks.php + * + * @static + */ + public static function init() + { + $config = Conf::get_instance(); + $hooks = $config->get('kolab_wap', 'custom_hooks'); + if ($hooks && is_readable($hooks)) { + Log::debug(__METHOD__.'() Loading '.$hooks); + require_once($hooks); + } + } + + /** + * register a callback function for a certain hook + * + * @static + * @param string $hook Hook name + * @param function|string $callback function/closure, function string or array($obj, 'method') + */ + public static function register($hook, $callback) + { + // if callable, register it + if (is_callable($callback)) { + self::$handlers[$hook][] = $callback; + + } + + // otherwise Log error + else { + Log::error('could not register callback for hook: '.$hook,$callback); + } + } + + /** + * unregister a callback function for a certain hook + * + * @static + * @param string $hook Hook name + * @param function|string $callback function/closure, function string or array($obj, 'method') + */ + public static function unregister($hook, $callback) + { + $callback_id = array_search($callback, (array) self::$handlers[$hook]); + if ($callback_id !== false) { + array_splice(self::$handlers[$hook], $callback_id, 1); + } + } + + + /** + * execute a hook and return the 'modified' or 'enhanced' arguments' + * + * @static + * @param string $hook Hook Name + * @param array $args given arguments to work with + * @return array $args + */ + public static function run($hook, array $args = array()) + { + if (in_array($hook, self::$stack)) { + Log::warning(__METHOD__.' possible recursion in execution of hook: '.$hook); + } + + array_push(self::$stack, $hook); + + // Use for loop here, so handlers added in the hook will be executed too + Log::trace(__METHOD__.' execute '.count(self::$handlers[$hook]).' hook(s) for '.$hook); + for ($i = 0; $i < count(self::$handlers[$hook]); $i++) { + Log::trace(__METHOD__.' execute hook['.$i.'] for '.$hook, self::$handlers[$hook][$i]); + $ret = call_user_func(self::$handlers[$hook][$i], $args); + if ($ret && is_array($ret)) { + $args = $ret + $args; + } + + if ($args['break']) { + break; + } + } + + array_pop(self::$stack); + return $args; + } + +} diff --git a/public_html/api/index.php b/public_html/api/index.php index b5be580..9598935 100644 --- a/public_html/api/index.php +++ b/public_html/api/index.php @@ -25,6 +25,8 @@ require_once dirname(__FILE__) . "/../../lib/functions.php"; +kolab_hooks::init(); + // init frontend controller $controller = new kolab_api_controller; diff --git a/public_html/index.php b/public_html/index.php index 1f9f1ed..4e5f48f 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -26,6 +26,8 @@ // environment initialization require_once '../lib/functions.php'; +kolab_hooks::init(); + // starting task $task = kolab_utils::get_input('task', kolab_utils::REQUEST_GET); |