summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Machniak <alec@alec.pl>2013-05-29 10:50:02 (GMT)
committerAleksander Machniak <alec@alec.pl>2013-05-29 10:50:02 (GMT)
commitabb7ec1a168ac313c45c14a8bbd1477206bd8d71 (patch)
tree71c33a9ef5f05b1466266f9aef1a8dd6f1a24ca5
parentad9b5530aad39c81d7f978acf192382930e75462 (diff)
downloadiRony-abb7ec1a168ac313c45c14a8bbd1477206bd8d71.tar.gz
Replace CacheAPC with rcube_cache_shared (Bug #1894)
Improve login process, do not require kolab_auth plugin
-rw-r--r--config/dav.inc.php.sample9
-rw-r--r--lib/Kolab/DAV/Auth/HTTPBasic.php219
-rw-r--r--lib/Kolab/Utils/CacheAPC.php107
-rw-r--r--public_html/index.php7
4 files changed, 167 insertions, 175 deletions
diff --git a/config/dav.inc.php.sample b/config/dav.inc.php.sample
index 59cb765..4c43fc3 100644
--- a/config/dav.inc.php.sample
+++ b/config/dav.inc.php.sample
@@ -33,3 +33,12 @@ $rcmail_config['kolab_dav_debug'] = false;
// User agent string written to kolab storage MIME messages
$rcmail_config['useragent'] = 'Kolab DAV Server libkolab/' . RCUBE_VERSION;
+// Roundcube plugins. Not all are supported here.
+$rcmail_config['kolabdav_plugins'] = array('kolab_auth');
+
+// Type of Auth cache. Supported values: 'db', 'apc' and 'memcache'.
+// Note: This is only for username canonification map.
+$rcmail_config['kolabdav_auth_cache'] = 'apc';
+
+// lifetime of the Auth cache, possible units: s, m, h, d, w
+$rcmail_config['kolabdav_auth_cache_ttl'] = '1h';
diff --git a/lib/Kolab/DAV/Auth/HTTPBasic.php b/lib/Kolab/DAV/Auth/HTTPBasic.php
index e16c894..1f841e9 100644
--- a/lib/Kolab/DAV/Auth/HTTPBasic.php
+++ b/lib/Kolab/DAV/Auth/HTTPBasic.php
@@ -50,83 +50,40 @@ class HTTPBasic extends \Sabre\DAV\Auth\Backend\AbstractBasic
protected function validateUserPass($username, $password)
{
$rcube = rcube::get_instance();
- $cache = CacheAPC::get_instance('kolabdav:auth');
- // Here we need IDNA ASCII
- $host = rcube_utils::idn_to_ascii($rcube->config->get('default_host', 'localhost'));
- $user = rcube_utils::idn_to_ascii($username);
- $port = $rcube->config->get('default_port', 143);
-
- $_host = parse_url($host);
- if ($_host['host']) {
- $host = $_host['host'];
- $ssl = (isset($_host['scheme']) && in_array($_host['scheme'], array('ssl','imaps','tls'))) ? $_host['scheme'] : null;
- if (!empty($_host['port']))
- $port = $_host['port'];
- else if ($ssl && $ssl != 'tls' && (!$port || $port == 143))
- $port = 993;
- }
-
- // check if we already canonified this username
- if ($auth_user = $cache->get($user)) {
- $user = $auth_user;
- }
- else { // load kolab_auth plugin to resolve the canonical username
- $rcube->plugins->load_plugin('kolab_auth');
+ // use shared cache for kolab_auth plugin result (username canonification)
+ $cache = $rcube->get_cache_shared('kolabdav_auth');
+ $cache_key = md5($username . '::' . $password);
+
+ if (!$cache || !($auth = $cache->get($cache_key))) {
+ $auth = $rcube->plugins->exec_hook('authenticate', array(
+ 'host' => $this->_select_host($username),
+ 'user' => $username,
+ 'pass' => $password,
+ ));
+
+ if ($cache) {
+ $cache->set($cache_key, array(
+ 'user' => $auth['user'],
+ 'host' => $auth['host'],
+ ));
+ }
}
-
- // let plugins do their work
- $auth = $rcube->plugins->exec_hook('authenticate', array(
- 'host' => $host,
- 'user' => $user,
- 'pass' => $password,
- ));
-
- // user already registered?
- if ($user_object = rcube_user::query($auth['user'], $auth['host'])) {
- $auth['user'] = $user_object->data['username'];
+ else {
+ $auth['pass'] = $password;
}
// authenticate user against the IMAP server
- $imap = $rcube->get_storage();
- $success = $imap->connect($auth['host'], $auth['user'], $auth['pass'], $port, $ssl);
-
- if ($success) {
- // No user in database, but IMAP auth works
- if (!is_object($user_object)) {
- if ($rcube->config->get('auto_create_user')) {
- // create a new user record
- $user_object = rcube_user::create($auth['user'], $auth['host']);
-
- if (!$user_object) {
- rcube::raise_error(array(
- 'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Failed to create a user record",
- ), true, false);
- return false;
- }
- }
- else {
- rcube::raise_error(array(
- 'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Access denied for new user $user. 'auto_create_user' is disabled",
- ), true, false);
- return false;
- }
- }
+ $user_id = $this->_login($auth['user'], $auth['pass'], $auth['host']);
+ if ($user_id) {
self::$current_user = $auth['user'];
- self::$current_pass = $rcube->password = $password;
- if (!$auth_user) {
- $cache->set($user, $auth['user']);
- }
+ self::$current_pass = $auth['pass'];
- // register a rcube_user object for global access
- $rcube->user = $user_object;
- $_SESSION['imap_host'] = $auth['host'];
+ return true;
}
- return $success;
+ return false;
}
/**
@@ -141,4 +98,132 @@ class HTTPBasic extends \Sabre\DAV\Auth\Backend\AbstractBasic
// return the canonic user name
return self::$current_user;
}
+
+ /**
+ * Storage host selection
+ */
+ protected function _select_host($username)
+ {
+ // Get IMAP host
+ $rcube = rcube::get_instance();
+ $host = $rcube->config->get('default_host', 'localhost');
+
+ if (is_array($host)) {
+ list($user, $domain) = explode('@', $username);
+
+ // try to select host by mail domain
+ if (!empty($domain)) {
+ foreach ($host as $storage_host => $mail_domains) {
+ if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) {
+ $host = $storage_host;
+ break;
+ }
+ else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) {
+ $host = is_numeric($storage_host) ? $mail_domains : $storage_host;
+ break;
+ }
+ }
+ }
+
+ // take the first entry if $host is not found
+ if (is_array($host)) {
+ list($key, $val) = each($default_host);
+ $host = is_numeric($key) ? $val : $key;
+ }
+ }
+
+ return rcube_utils::parse_host($host);
+ }
+
+ /**
+ * Authenticates a user in IMAP and returns Roundcube user ID.
+ */
+ protected function _login($username, $password, $host)
+ {
+ if (empty($username)) {
+ return null;
+ }
+
+ $rcube = rcube::get_instance();
+ $storage = $rcube->get_storage();
+ $login_lc = $rcube->config->get('login_lc');
+ $default_port = $rcube->config->get('default_port', 143);
+
+ // parse $host
+ $a_host = parse_url($host);
+ if ($a_host['host']) {
+ $host = $a_host['host'];
+ $ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
+ if (!empty($a_host['port'])) {
+ $port = $a_host['port'];
+ }
+ else if ($ssl && $ssl != 'tls' && (!$default_port || $default_port == 143)) {
+ $port = 993;
+ }
+ }
+
+ if (!$port) {
+ $port = $default_port;
+ }
+
+ // Convert username to lowercase. If storage backend
+ // is case-insensitive we need to store always the same username
+ if ($login_lc) {
+ if ($login_lc == 2 || $login_lc === true) {
+ $username = mb_strtolower($username);
+ }
+ else if (strpos($username, '@')) {
+ // lowercase domain name
+ list($local, $domain) = explode('@', $username);
+ $username = $local . '@' . mb_strtolower($domain);
+ }
+ }
+
+ // Here we need IDNA ASCII
+ // Only rcube_contacts class is using domain names in Unicode
+ $host = rcube_utils::idn_to_ascii($host);
+ $username = rcube_utils::idn_to_ascii($username);
+
+ // user already registered?
+ if ($user = rcube_user::query($username, $host)) {
+ $username = $user->data['username'];
+ }
+
+ // authenticate user in IMAP
+ if (!$storage->connect($host, $username, $password, $port, $ssl)) {
+ return null;
+ }
+
+ // No user in database, but IMAP auth works
+ if (!is_object($user)) {
+ if ($rcube->config->get('auto_create_user')) {
+ // create a new user record
+ $user = rcube_user::create($username, $host);
+
+ if (!$user) {
+ rcube::raise_error(array(
+ 'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Failed to create a user record",
+ ), true, false);
+ return null;
+ }
+ }
+ else {
+ rcube::raise_error(array(
+ 'code' => 620, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Access denied for new user $username. 'auto_create_user' is disabled",
+ ), true, false);
+ return null;
+ }
+ }
+
+ // overwrite config with user preferences
+ $rcube->user = $user;
+ $rcube->config->set_user_prefs((array)$rcube->user->get_prefs());
+ $rcube->password = $password;
+
+ setlocale(LC_ALL, 'en_US.utf8', 'en_US.UTF-8');
+
+ return $user->ID;
+ }
}
diff --git a/lib/Kolab/Utils/CacheAPC.php b/lib/Kolab/Utils/CacheAPC.php
deleted file mode 100644
index 3e7aeb0..0000000
--- a/lib/Kolab/Utils/CacheAPC.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-/**
- * Utility class prividing a simple API to PHP's APC cache
- *
- * @author Thomas Bruederli <bruederli@kolabsys.com>
- *
- * Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
- *
- * 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/>.
- */
-
-namespace Kolab\Utils;
-
-/**
- * Utility class that provides a simple API to local APC cache
- */
-class CacheAPC
-{
- private static $instances = array();
-
- private $prefix = 'kolabdav:';
- private $ttl = 600; // Default Time To Live
- private $enabled = false; // APC enabled?
- private $local = array(); // local in-memory cache
-
- /**
- * Singleton getter
- *
- * @param string Cache domain used to prefix cache entries
- * @return object CacheAPC instance for the given domain
- */
- public static function get_instance($domain = '')
- {
- if (!self::$instances[$domain]) {
- self::$instances[$domain] = new CacheAPC($domain);
- }
-
- return self::$instances[$domain];
- }
-
- /**
- * Private constructor
- */
- private function __construct($domain)
- {
- if (!empty($domain))
- $this->prefix = $domain . ':';
-
- $this->enabled = extension_loaded('apc');
- }
-
- /**
- * Get data from cache
- */
- function get($key)
- {
- if (isset($this->local[$key])) {
- return $this->local[$key];
- }
-
- if ($this->enabled) {
- $success = false;
- $data = apc_fetch($this->prefix . $key, $success);
- return $success ? $data : null;
- }
- }
-
- /**
- * Save data to cache
- */
- function set($key, $data, $ttl = 0)
- {
- $this->local[$key] = $data;
- if ($this->enabled) {
- return apc_store($this->prefix . $key, $data, $ttl ?: $this->ttl);
- }
-
- return true;
- }
-
- /**
- * Deelete a cache entry
- */
- function del($key)
- {
- unset($this->local[$key]);
-
- if ($this->enabled) {
- apc_delete($this->prefix . $key);
- }
- }
-
-}
-
-
diff --git a/public_html/index.php b/public_html/index.php
index 7a9cf8c..d201ed8 100644
--- a/public_html/index.php
+++ b/public_html/index.php
@@ -60,8 +60,13 @@ require_once KOLAB_DAV_ROOT . '/lib/Roundcube/bootstrap.php';
// Roundcube framework initialization
$rcube = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS);
$rcube->config->load_from_file(RCUBE_CONFIG_DIR . 'dav.inc.php');
+
+// Load plugins
+$plugins = (array)$rcube->config->get('kolabdav_plugins', array('kolab_auth'));
+$required = array('libkolab', 'libcalendaring');
+
$rcube->plugins->init($rcube);
-$rcube->plugins->load_plugins(array('libkolab','libcalendaring'));
+$rcube->plugins->load_plugins($plugins, $required);
// convenience function, you know it well :-)
function console() { call_user_func_array(array('rcube', 'console'), func_get_args()); }