summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Hoffend <dh@dotlan.net>2015-02-14 01:59:36 (GMT)
committerDaniel Hoffend <dh@dotlan.net>2015-02-14 02:02:47 (GMT)
commit6c11665338354181ab1f4eb8db9166a0df9d28cd (patch)
tree67088af605ccb679b15c2b4ee8cdeed874dc2084
parent79b9ed1f6f80683ec8b57becf3f4a662cbcd274e (diff)
downloadkolab-wap-6c11665338354181ab1f4eb8db9166a0df9d28cd.tar.gz
add initial version of hooks to support external checks on api calls
-rw-r--r--doc/sample-hooks.php92
-rw-r--r--lib/api/kolab_api_service_form_value.php27
-rw-r--r--lib/kolab_api_controller.php26
-rw-r--r--lib/kolab_hooks.php131
-rw-r--r--public_html/api/index.php2
-rw-r--r--public_html/index.php2
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);