From 8e51918f64ecb980982a5771ba1fe5df950be81b Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 8 Jun 2015 15:58:52 +0200 Subject: [PATCH] Small refactoring of the 2FA storage layer + also use it for determining the active factors --- plugins/kolab_2fa/kolab_2fa.php | 70 +++++++++++++------ .../kolab_2fa/lib/Kolab2FA/Driver/Base.php | 25 +++++-- .../kolab_2fa/lib/Kolab2FA/Storage/Base.php | 9 +++ .../lib/Kolab2FA/Storage/RcubeUser.php | 29 ++++---- 4 files changed, 93 insertions(+), 40 deletions(-) diff --git a/plugins/kolab_2fa/kolab_2fa.php b/plugins/kolab_2fa/kolab_2fa.php index a04f26f1..97758c68 100644 --- a/plugins/kolab_2fa/kolab_2fa.php +++ b/plugins/kolab_2fa/kolab_2fa.php @@ -30,6 +30,7 @@ class kolab_2fa extends rcube_plugin protected $login_verified = null; protected $login_factors = array(); protected $drivers = array(); + protected $storage; /** * Plugin init @@ -47,6 +48,10 @@ class kolab_2fa extends rcube_plugin { $rcmail = rcmail::get_instance(); + // register library namespace to autoloader + $loader = include(INSTALL_PATH . 'vendor/autoload.php'); + $loader->set('Kolab2FA', array($this->home . '/lib')); + if ($args['task'] === 'login' && $this->api->output) { $this->add_texts('localization/', false); $this->add_hook('authenticate', array($this, 'authenticate')); @@ -84,7 +89,7 @@ class kolab_2fa extends rcube_plugin // parse $host URL $a_host = parse_url($args['host']); - $hostname = $a_host['host'] ?: $args['host']; + $hostname = $_SESSION['hostname'] = $a_host['host'] ?: $args['host']; // 1. find user record (and its prefs) before IMAP login if ($user = rcube_user::query($args['user'], $hostname)) { @@ -92,7 +97,7 @@ class kolab_2fa extends rcube_plugin } // 2. check if this user/system has 2FA enabled - if (count($factors = (array)$rcmail->config->get('kolab_2fa_factors', array())) > 0) { + if (($storage = $this->get_storage($args['user'])) && count($factors = (array)$storage->read('active')) > 0) { $args['abort'] = true; // 3. flag session as temporary (no further actions allowed) @@ -101,7 +106,6 @@ class kolab_2fa extends rcube_plugin $_SESSION['kolab_2fa_factors'] = $factors; $_SESSION['username'] = $args['user']; - $_SESSION['hostname'] = $hostname; $_SESSION['host'] = $args['host']; $_SESSION['password'] = $rcmail->encrypt($args['pass']); @@ -143,8 +147,6 @@ class kolab_2fa extends rcube_plugin $expired = $time < time() - $rcmail->config->get('kolab_2fa_timeout', 120); if (!empty($sign) && !empty($factors) && !empty($nonce) && !$expired) { - console('VERIFY', $sign, $factors); - // TODO: check signature // try to verify each configured factor @@ -224,7 +226,8 @@ class kolab_2fa extends rcube_plugin // render input for each configured auth method foreach ($this->login_factors as $i => $method) { if ($i > 0) { - $table->add(array('colspan' => 2, 'class' => 'hint'), $this->gettext('or')); + $table->add(array('colspan' => 2, 'class' => 'title hint', 'style' => 'text-align:center'), + $this->gettext('or')); } $field_id = "rcmlogin2fa$method"; @@ -273,12 +276,6 @@ class kolab_2fa extends rcube_plugin return $this->drivers[$method]; } - // register library namespace to autoloader - if (!class_exists('\\Kolab3FA\\Driver\\Base', false)) { - $loader = include(INSTALL_PATH . 'vendor/autoload.php'); - $loader->set('Kolab2FA', array($this->home . '/lib')); - } - $config = $rcmail->config->get('kolab_2fa_' . $method, array()); // use product name as "issuer"" @@ -292,10 +289,7 @@ class kolab_2fa extends rcube_plugin $driver = \Kolab2FA\Driver\Base::factory($method, $config); // attach storage - $driver->storage = \Kolab2FA\Storage\Base::factory( - $rcmail->config->get('kolab_2fa_storage', 'roundcube'), - $rcmail->config->get('kolab_2fa_storage_config', array()) - ); + $driver->storage = $this->get_storage(); // set user properties from active session if ($rcmail->user->ID) { @@ -320,6 +314,37 @@ class kolab_2fa extends rcube_plugin return false; } + /** + * Getter for a storage instance singleton + */ + public function get_storage($for = null) + { + if (!isset($this->storage) || (!empty($for) && $this->storage->username !== $for)) { + $rcmail = rcmail::get_instance(); + + try { + $this->storage = \Kolab2FA\Storage\Base::factory( + $rcmail->config->get('kolab_2fa_storage', 'roundcube'), + $rcmail->config->get('kolab_2fa_storage_config', array()) + ); + $this->storage->set_username($for); + } + catch (Exception $e) { + $this->storage = false; + + rcube::raise_error(array( + 'code' => 600, + 'type' => 'php', + 'file' => __FILE__, + 'line' => __LINE__, + 'message' => $error), + true, false); + } + } + + return $this->storage; + } + /** * Handler for 'settings_actions' hook */ @@ -365,7 +390,8 @@ class kolab_2fa extends rcube_plugin public function settings_factoradder($attrib) { $rcmail = rcmail::get_instance(); - $active = (array)$rcmail->config->get('kolab_2fa_factors', array()); + $storage = $this->get_storage($rcmail->get_user_name()); + $active = $storage ? (array)$storage->read('active') : array(); $select = new html_select(array('id' => 'kolab2fa-add')); $select->add($this->gettext('addfactor') . '...', ''); @@ -397,8 +423,9 @@ class kolab_2fa extends rcube_plugin public function settings_form($attrib = array()) { $rcmail = rcmail::get_instance(); + $storage = $this->get_storage($rcmail->get_user_name()); + $factors = $storage ? (array)$storage->read('active') : array(); $drivers = (array)$rcmail->config->get('kolab_2fa_drivers', array()); - $factors = (array)$rcmail->config->get('kolab_2fa_factors', array()); foreach ($drivers as $j => $method) { $out .= $this->settings_factor($method, $attrib); @@ -432,10 +459,8 @@ class kolab_2fa extends rcube_plugin $out = ''; $rcmail = rcmail::get_instance(); $attrib += array('class' => 'propform'); - $factors = (array)$rcmail->config->get('kolab_2fa_factors', array()); if ($driver = $this->get_driver($method)) { - $active = in_array($method, $factors); $table = new html_table(array('cols' => 2, 'class' => $attrib['class'])); foreach ($driver->props() as $field => $prop) { @@ -530,7 +555,8 @@ class kolab_2fa extends rcube_plugin $data = @json_decode(rcube_utils::get_input_value('_data', rcube_utils::INPUT_POST), true); $rcmail = rcmail::get_instance(); - $active = (array)$rcmail->config->get('kolab_2fa_factors', array()); + $storage = $this->get_storage($rcmail->get_user_name()); + $active = $storage ? (array)$storage->read('active') : array(); $success = false; $errors = 0; $save_data = array(); @@ -575,7 +601,7 @@ class kolab_2fa extends rcube_plugin // update list of active factors for this user if (!$errors) { - $success = $rcmail->user->save_prefs(array('kolab_2fa_factors' => $active)); + $success = $storage && $storage->write('active', $active); $save_data = $data !== false ? $this->format_props($driver->props()) : array(); } } diff --git a/plugins/kolab_2fa/lib/Kolab2FA/Driver/Base.php b/plugins/kolab_2fa/lib/Kolab2FA/Driver/Base.php index 2acd19da..4e8d28a3 100644 --- a/plugins/kolab_2fa/lib/Kolab2FA/Driver/Base.php +++ b/plugins/kolab_2fa/lib/Kolab2FA/Driver/Base.php @@ -197,7 +197,7 @@ abstract class Base $setter = 'set_' . $key; if (method_exists($this, $setter)) { - call_user_method($this, $setter, $value); + call_user_func(array($this, $setter), $value); } else if (in_array($key, $this->allowed_props)) { $this->props[$key] = $value; @@ -205,7 +205,21 @@ abstract class Base return true; } - + + /** + * Dedicated setter for the username property + */ + public function set_username($username) + { + $this->props['username'] = $username; + + if ($this->storage) { + $this->storage->set_username($username); + } + + return true; + } + /** * Clear data stored for this driver */ @@ -222,7 +236,7 @@ abstract class Base protected function get_user_prop($key) { if (!isset($this->user_props[$key]) && $this->storage) { - $this->user_props = (array)$this->storage->read($this->username . ':' . $this->method); + $this->user_props = (array)$this->storage->read($this->method); } return $this->user_props[$key]; @@ -237,10 +251,9 @@ abstract class Base $this->user_props[$key] = $value; if ($this->user_settings[$key] && $this->storage) { - $storage_key = $this->username . ':' . $this->method; - $props = (array)$this->storage->read($storage_key); + $props = (array)$this->storage->read($this->method); $props[$key] = $value; - $success = $this->storage->write($storage_key, $props); + $success = $this->storage->write($this->method, $props); } return $success; diff --git a/plugins/kolab_2fa/lib/Kolab2FA/Storage/Base.php b/plugins/kolab_2fa/lib/Kolab2FA/Storage/Base.php index 75497f09..dd11f183 100644 --- a/plugins/kolab_2fa/lib/Kolab2FA/Storage/Base.php +++ b/plugins/kolab_2fa/lib/Kolab2FA/Storage/Base.php @@ -25,6 +25,7 @@ namespace Kolab2FA\Storage; abstract class Base { + public $username = null; protected $config = array(); /** @@ -64,6 +65,14 @@ abstract class Base $this->config = array_merge($this->config, $config); } + /** + * Set username to store data for + */ + public function set_username($username) + { + $this->username = $username; + } + /** * Read data for the given key */ diff --git a/plugins/kolab_2fa/lib/Kolab2FA/Storage/RcubeUser.php b/plugins/kolab_2fa/lib/Kolab2FA/Storage/RcubeUser.php index 865ebba7..80bb4ce9 100644 --- a/plugins/kolab_2fa/lib/Kolab2FA/Storage/RcubeUser.php +++ b/plugins/kolab_2fa/lib/Kolab2FA/Storage/RcubeUser.php @@ -29,6 +29,7 @@ use \rcube_user; class RcubeUser extends Base { private $cache = array(); + private $user; public function init(array $config) { @@ -43,11 +44,9 @@ class RcubeUser extends Base */ public function read($key) { - list($username, $method) = $this->split_key($key); - - if (!isset($this->cache[$key]) && ($user = $this->get_user($username))) { + if (!isset($this->cache[$key]) && ($user = $this->get_user($this->username))) { $prefs = $user->get_prefs(); - $pkey = 'kolab_2fa_props_' . $method; + $pkey = 'kolab_2fa_props_' . $key; $this->cache[$key] = $prefs[$pkey]; } @@ -59,11 +58,9 @@ class RcubeUser extends Base */ public function write($key, $value) { - list($username, $method) = $this->split_key($key); - - if ($user = $this->get_user($username)) { + if ($user = $this->get_user($this->username)) { $this->cache[$key] = $value; - $pkey = 'kolab_2fa_props_' . $method; + $pkey = 'kolab_2fa_props_' . $key; return $user->save_prefs(array($pkey => $value), true); } @@ -79,11 +76,15 @@ class RcubeUser extends Base } /** - * Helper method to split the storage key into username and auth-method + * Set username to store data for */ - private function split_key($key) + public function set_username($username) { - return explode(':', $key, 2); + parent::set_username($username); + + // reset cached values + $this->cache = array(); + $this->user = null; } /** @@ -97,7 +98,11 @@ class RcubeUser extends Base return $rcmail->user; } - return rcube_user::query($username, $this->config['hostname']); + if (!$this->user) { + $this->user = rcube_user::query($username, $this->config['hostname']); + } + + return $this->user; } }