Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117881828
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
53 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/kolab_auth/kolab_auth.php b/plugins/kolab_auth/kolab_auth.php
index e6171d21..7c615537 100644
--- a/plugins/kolab_auth/kolab_auth.php
+++ b/plugins/kolab_auth/kolab_auth.php
@@ -1,541 +1,545 @@
<?php
/**
* Kolab Authentication (based on ldap_authentication plugin)
*
* Authenticates on LDAP server, finds canonized authentication ID for IMAP
* and for new users creates identity based on LDAP information.
*
* Supports impersonate feature (login as another user). To use this feature
* imap_auth_type/smtp_auth_type must be set to DIGEST-MD5 or PLAIN.
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2011-2012, 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/>.
*/
class kolab_auth extends rcube_plugin
{
static $ldap;
private $data = array();
public function init()
{
$rcmail = rcube::get_instance();
$this->add_hook('authenticate', array($this, 'authenticate'));
$this->add_hook('startup', array($this, 'startup'));
$this->add_hook('user_create', array($this, 'user_create'));
// Hooks related to "Login As" feature
$this->add_hook('template_object_loginform', array($this, 'login_form'));
$this->add_hook('storage_connect', array($this, 'imap_connect'));
$this->add_hook('managesieve_connect', array($this, 'imap_connect'));
$this->add_hook('smtp_connect', array($this, 'smtp_connect'));
$this->add_hook('identity_form', array($this, 'identity_form'));
$this->add_hook('write_log', array($this, 'write_log'));
// TODO: This section does not actually seem to work
if ($rcmail->config->get('kolab_auth_auditlog', false)) {
$rcmail->config->set('debug_level', 1);
$rcmail->config->set('devel_mode', true);
$rcmail->config->set('smtp_log', true);
$rcmail->config->set('log_logins', true);
$rcmail->config->set('log_session', true);
$rcmail->config->set('sql_debug', true);
$rcmail->config->set('memcache_debug', true);
$rcmail->config->set('imap_debug', true);
$rcmail->config->set('ldap_debug', true);
$rcmail->config->set('smtp_debug', true);
}
}
public function startup($args)
{
// Arguments are task / action, not interested
if (!empty($_SESSION['user_roledns'])) {
$this->load_user_role_plugins_and_settings($_SESSION['user_roledns']);
}
return $args;
}
public function load_user_role_plugins_and_settings($role_dns)
{
$rcmail = rcube::get_instance();
$this->load_config();
// Check role dependent plugins to enable and settings to modify
// Example 'kolab_auth_role_plugins' =
//
// Array(
// '<role_dn>' => Array('plugin1', 'plugin2'),
// );
//
// NOTE that <role_dn> may in fact be something like: 'cn=role,%dc'
$role_plugins = $rcmail->config->get('kolab_auth_role_plugins');
// Example $rcmail_config['kolab_auth_role_settings'] =
//
// Array(
// '<role_dn>' => Array(
// '$setting' => Array(
// 'mode' => '(override|merge)', (default: override)
// 'value' => <>,
// 'allow_override' => (true|false) (default: false)
// ),
// ),
// );
//
// NOTE that <role_dn> may in fact be something like: 'cn=role,%dc'
$role_settings = $rcmail->config->get('kolab_auth_role_settings');
$ldap = self::ldap();
if (!$ldap || !$ldap->ready) {
$args['abort'] = true;
return $args;
}
if (!empty($role_plugins)) {
foreach ($role_plugins as $role_dn => $plugins) {
$role_plugins[$ldap->parse_vars($role_dn)] = $plugins;
}
}
if (!empty($role_settings)) {
foreach ($role_settings as $role_dn => $settings) {
$role_settings[$ldap->parse_vars($role_dn)] = $settings;
}
}
foreach ($role_dns as $role_dn) {
if (isset($role_plugins[$role_dn]) && is_array($role_plugins[$role_dn])) {
foreach ($role_plugins[$role_dn] as $plugin) {
$this->require_plugin($plugin);
}
}
if (isset($role_settings[$role_dn]) && is_array($role_settings[$role_dn])) {
foreach ($role_settings[$role_dn] as $setting_name => $setting) {
if (!isset($setting['mode'])) {
$setting['mode'] = 'override';
}
if ($setting['mode'] == "override") {
$rcmail->config->set($setting_name, $setting['value']);
} elseif ($setting['mode'] == "merge") {
$orig_setting = $rcmail->config->get($setting_name);
if (!empty($orig_setting)) {
if (is_array($orig_setting)) {
$rcmail->config->set($setting_name, array_merge($orig_setting, $setting['value']));
}
} else {
$rcmail->config->set($setting_name, $setting['value']);
}
}
$dont_override = (array) $rcmail->config->get('dont_override');
if (!isset($setting['allow_override']) || !$setting['allow_override']) {
$rcmail->config->set('dont_override', array_merge($dont_override, array($setting_name)));
}
else {
if (in_array($setting_name, $dont_override)) {
$_dont_override = array();
foreach ($dont_override as $_setting) {
if ($_setting != $setting_name) {
$_dont_override[] = $_setting;
}
}
$rcmail->config->set('dont_override', $_dont_override);
}
}
}
}
}
}
public function write_log($args)
{
$rcmail = rcube::get_instance();
if (!$rcmail->config->get('kolab_auth_auditlog', false)) {
return $args;
}
$args['abort'] = true;
if ($rcmail->config->get('log_driver') == 'syslog') {
$prio = $args['name'] == 'errors' ? LOG_ERR : LOG_INFO;
syslog($prio, $args['line']);
return $args;
}
else {
$line = sprintf("[%s]: %s\n", $args['date'], $args['line']);
// log_driver == 'file' is assumed here
$log_dir = $rcmail->config->get('log_dir', INSTALL_PATH . 'logs');
$log_path = $log_dir.'/'.strtolower($_SESSION['kolab_auth_admin']).'/'.strtolower($_SESSION['username']);
// Append original username + target username
if (!is_dir($log_path)) {
// Attempt to create the directory
if (@mkdir($log_path, 0750, true)) {
$log_dir = $log_path;
}
}
else {
$log_dir = $log_path;
}
// try to open specific log file for writing
$logfile = $log_dir.'/'.$args['name'];
if ($fp = fopen($logfile, 'a')) {
fwrite($fp, $line);
fflush($fp);
fclose($fp);
return $args;
}
else {
trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING);
}
}
return $args;
}
/**
* Sets defaults for new user.
*/
public function user_create($args)
{
if (!empty($this->data['user_email'])) {
// addresses list is supported
if (array_key_exists('email_list', $args)) {
$email_list = array_unique($this->data['user_email']);
// add organization to the list
if (!empty($this->data['user_organization'])) {
foreach ($email_list as $idx => $email) {
$email_list[$idx] = array(
'organization' => $this->data['user_organization'],
'email' => $email,
);
}
}
$args['email_list'] = $email_list;
}
else {
$args['user_email'] = $this->data['user_email'][0];
}
}
if (!empty($this->data['user_name'])) {
$args['user_name'] = $this->data['user_name'];
}
return $args;
}
/**
* Modifies login form adding additional "Login As" field
*/
public function login_form($args)
{
$this->load_config();
$this->add_texts('localization/');
$rcmail = rcube::get_instance();
$admin_login = $rcmail->config->get('kolab_auth_admin_login');
$group = $rcmail->config->get('kolab_auth_group');
$role_attr = $rcmail->config->get('kolab_auth_role');
// Show "Login As" input
if (empty($admin_login) || (empty($group) && empty($role_attr))) {
return $args;
}
$input = new html_inputfield(array('name' => '_loginas', 'id' => 'rcmloginas',
'type' => 'text', 'autocomplete' => 'off'));
$row = html::tag('tr', null,
html::tag('td', 'title', html::label('rcmloginas', Q($this->gettext('loginas'))))
. html::tag('td', 'input', $input->show(trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST))))
);
$args['content'] = preg_replace('/<\/tbody>/i', $row . '</tbody>', $args['content']);
return $args;
}
/**
* Find user credentials In LDAP.
*/
public function authenticate($args)
{
// get username and host
$host = $args['host'];
$user = $args['user'];
$pass = $args['pass'];
$loginas = trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST));
if (empty($user) || empty($pass)) {
$args['abort'] = true;
return $args;
}
$ldap = self::ldap();
if (!$ldap || !$ldap->ready) {
$args['abort'] = true;
return $args;
}
// Find user record in LDAP
$record = $ldap->get_user_record($user, $host);
if (empty($record)) {
$args['abort'] = true;
return $args;
}
$rcmail = rcube::get_instance();
$admin_login = $rcmail->config->get('kolab_auth_admin_login');
$admin_pass = $rcmail->config->get('kolab_auth_admin_password');
$login_attr = $rcmail->config->get('kolab_auth_login');
$name_attr = $rcmail->config->get('kolab_auth_name');
$email_attr = $rcmail->config->get('kolab_auth_email');
$org_attr = $rcmail->config->get('kolab_auth_organization');
$role_attr = $rcmail->config->get('kolab_auth_role');
if (!empty($role_attr) && !empty($record[$role_attr])) {
$_SESSION['user_roledns'] = (array)($record[$role_attr]);
}
// Login As...
if (!empty($loginas) && $admin_login) {
// Authenticate to LDAP
$result = $ldap->bind($record['dn'], $pass);
if (!$result) {
$args['abort'] = true;
return $args;
}
// check if the original user has/belongs to administrative role/group
$isadmin = false;
$group = $rcmail->config->get('kolab_auth_group');
$role_dn = $rcmail->config->get('kolab_auth_role_value');
// check role attribute
if (!empty($role_attr) && !empty($role_dn) && !empty($record[$role_attr])) {
$role_dn = $ldap->parse_vars($role_dn, $user, $host);
if (in_array($role_dn, (array)$record[$role_attr])) {
$isadmin = true;
}
}
// check group
if (!$isadmin && !empty($group)) {
$groups = $ldap->get_user_groups($record['dn'], $user, $host);
if (in_array($group, $groups)) {
$isadmin = true;
}
}
// Save original user login for log (see below)
if ($login_attr) {
$origname = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr];
}
else {
$origname = $user;
}
$record = null;
// user has the privilage, get "login as" user credentials
if ($isadmin) {
$record = $ldap->get_user_record($loginas, $host);
}
if (empty($record)) {
$args['abort'] = true;
return $args;
}
$args['user'] = $loginas;
// Mark session to use SASL proxy for IMAP authentication
$_SESSION['kolab_auth_admin'] = strtolower($origname);
$_SESSION['kolab_auth_login'] = $rcmail->encrypt($admin_login);
$_SESSION['kolab_auth_password'] = $rcmail->encrypt($admin_pass);
}
// Store UID and DN of logged user in session for use by other plugins
$_SESSION['kolab_uid'] = is_array($record['uid']) ? $record['uid'][0] : $record['uid'];
$_SESSION['kolab_dn'] = $record['dn'];
// Set user login
if ($login_attr) {
$this->data['user_login'] = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr];
}
if ($this->data['user_login']) {
$args['user'] = $this->data['user_login'];
}
// User name for identity (first log in)
foreach ((array)$name_attr as $field) {
$name = is_array($record[$field]) ? $record[$field][0] : $record[$field];
if (!empty($name)) {
$this->data['user_name'] = $name;
break;
}
}
// User email(s) for identity (first log in)
foreach ((array)$email_attr as $field) {
$email = is_array($record[$field]) ? array_filter($record[$field]) : $record[$field];
if (!empty($email)) {
$this->data['user_email'] = array_merge((array)$this->data['user_email'], (array)$email);
}
}
// Organization name for identity (first log in)
foreach ((array)$org_attr as $field) {
$organization = is_array($record[$field]) ? $record[$field][0] : $record[$field];
if (!empty($organization)) {
$this->data['user_organization'] = $organization;
break;
}
}
// Log "Login As" usage
if (!empty($origname)) {
rcube::write_log('userlogins', sprintf('Admin login for %s by %s from %s',
$args['user'], $origname, rcube_utils::remote_ip()));
}
return $args;
}
/**
* Sets SASL Proxy login/password for IMAP and Managesieve auth
*/
public function imap_connect($args)
{
if (!empty($_SESSION['kolab_auth_admin'])) {
$rcmail = rcube::get_instance();
$admin_login = $rcmail->decrypt($_SESSION['kolab_auth_login']);
$admin_pass = $rcmail->decrypt($_SESSION['kolab_auth_password']);
$args['auth_cid'] = $admin_login;
$args['auth_pw'] = $admin_pass;
}
return $args;
}
/**
* Sets SASL Proxy login/password for SMTP auth
*/
public function smtp_connect($args)
{
if (!empty($_SESSION['kolab_auth_admin'])) {
$rcmail = rcube::get_instance();
$admin_login = $rcmail->decrypt($_SESSION['kolab_auth_login']);
$admin_pass = $rcmail->decrypt($_SESSION['kolab_auth_password']);
$args['options']['smtp_auth_cid'] = $admin_login;
$args['options']['smtp_auth_pw'] = $admin_pass;
}
return $args;
}
/**
* Hook to replace the plain text input field for email address by a drop-down list
* with all email addresses (including aliases) from this user's LDAP record.
*/
public function identity_form($args)
{
$ldap = self::ldap();
if (!$ldap || !$ldap->ready || empty($_SESSION['kolab_dn'])) {
return $args;
}
$emails = array();
$rcmail = rcube::get_instance();
$user_record = $ldap->get_record($_SESSION['kolab_dn']);
foreach ((array)$rcmail->config->get('kolab_auth_email', array()) as $col) {
$values = rcube_addressbook::get_col_values($col, $user_record, true);
if (!empty($values))
$emails = array_merge($emails, array_filter($values));
}
+ // kolab_delegation might want to modify this addresses list
+ $plugin = $rcmail->plugins->exec_hook('kolab_auth_emails', array('emails' => $emails));
+ $emails = $plugin['emails'];
+
if (!empty($emails)) {
$args['form']['addressing']['content']['email'] = array(
'type' => 'select',
'options' => array_combine($emails, $emails),
);
}
return $args;
}
/**
* Initializes LDAP object and connects to LDAP server
*/
public static function ldap()
{
if (self::$ldap) {
return self::$ldap;
}
$rcmail = rcube::get_instance();
// $this->load_config();
// we're in static method, load config manually
$fpath = $rcmail->plugins->dir . '/kolab_auth/config.inc.php';
if (is_file($fpath) && !$rcmail->config->load_from_file($fpath)) {
rcube::raise_error(array(
'code' => 527, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Failed to load config from $fpath"), true, false);
}
$addressbook = $rcmail->config->get('kolab_auth_addressbook');
if (!is_array($addressbook)) {
$ldap_config = (array)$rcmail->config->get('ldap_public');
$addressbook = $ldap_config[$addressbook];
}
if (empty($addressbook)) {
return null;
}
require_once __DIR__ . '/kolab_auth_ldap.php';
self::$ldap = new kolab_auth_ldap($addressbook);
return self::$ldap;
}
}
diff --git a/plugins/kolab_auth/kolab_auth_ldap.php b/plugins/kolab_auth/kolab_auth_ldap.php
index 4584c60b..2cddb3f5 100644
--- a/plugins/kolab_auth/kolab_auth_ldap.php
+++ b/plugins/kolab_auth/kolab_auth_ldap.php
@@ -1,485 +1,487 @@
<?php
/**
* Kolab Authentication
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2011-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/>.
*/
/**
* Wrapper class for rcube_ldap_generic
*/
class kolab_auth_ldap extends rcube_ldap_generic
{
private $icache = array();
function __construct($p)
{
$rcmail = rcube::get_instance();
$this->debug = (bool) $rcmail->config->get('ldap_debug');
$this->fieldmap = $p['fieldmap'];
$this->fieldmap['uid'] = 'uid';
$p['attributes'] = array_values($this->fieldmap);
// Connect to the server (with bind)
parent::__construct($p);
$this->_connect();
$rcmail->add_shutdown_function(array($this, 'close'));
}
/**
* Establish a connection to the LDAP server
*/
private function _connect()
{
$rcube = rcube::get_instance();
// try to connect + bind for every host configured
// with OpenLDAP 2.x ldap_connect() always succeeds but ldap_bind will fail if host isn't reachable
// see http://www.php.net/manual/en/function.ldap-connect.php
foreach ((array)$this->config['hosts'] as $host) {
// skip host if connection failed
if (!$this->connect($host)) {
continue;
}
$bind_pass = $this->config['bind_pass'];
$bind_user = $this->config['bind_user'];
$bind_dn = $this->config['bind_dn'];
if (empty($bind_pass)) {
$this->ready = true;
}
else {
if (!empty($bind_dn)) {
$this->ready = $this->bind($bind_dn, $bind_pass);
}
else if (!empty($this->config['auth_cid'])) {
$this->ready = $this->sasl_bind($this->config['auth_cid'], $bind_pass, $bind_user);
}
else {
$this->ready = $this->sasl_bind($bind_user, $bind_pass);
}
}
// connection established, we're done here
if ($this->ready) {
break;
}
} // end foreach hosts
if (!is_resource($this->conn)) {
rcube::raise_error(array('code' => 100, 'type' => 'ldap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not connect to any LDAP server, last tried $host"), true);
$this->ready = false;
}
return $this->ready;
}
/**
* Fetches user data from LDAP addressbook
*/
function get_user_record($user, $host)
{
$rcmail = rcube::get_instance();
$filter = $rcmail->config->get('kolab_auth_filter');
$filter = $this->parse_vars($filter, $user, $host);
$base_dn = $this->parse_vars($this->config['base_dn'], $user, $host);
$scope = $this->config['scope'];
// @TODO: print error if filter is empty
// get record
if ($result = parent::search($base_dn, $filter, $scope, $this->attributes)) {
if ($result->count() == 1) {
$entries = $result->entries(true);
$dn = key($entries);
$entry = array_pop($entries);
+ $entry = rcube_ldap_generic::normalize_entry($entry);
$entry = $this->field_mapping($dn, $entry);
return $entry;
}
}
}
/**
* Fetches user data from LDAP addressbook
*/
function get_user_groups($dn, $user, $host)
{
if (empty($dn) || empty($this->config['groups'])) {
return array();
}
$base_dn = $this->parse_vars($this->config['groups']['base_dn'], $user, $host);
$name_attr = $this->config['groups']['name_attr'] ? $this->config['groups']['name_attr'] : 'cn';
$member_attr = $this->get_group_member_attr();
$filter = "(member=$dn)(uniqueMember=$dn)";
if ($member_attr != 'member' && $member_attr != 'uniqueMember')
$filter .= "($member_attr=$dn)";
$filter = strtr("(|$filter)", array("\\" => "\\\\"));
$result = parent::search($base_dn, $filter, 'sub', array('dn', $name_attr));
if (!$result) {
return array();
}
$groups = array();
foreach ($result as $entry) {
$entry = rcube_ldap_generic::normalize_entry($entry);
if (!$entry['dn']) {
$entry['dn'] = $result->get_dn();
}
$groups[$entry['dn']] = $entry[$name_attr];
}
return $groups;
}
/**
* Get a specific LDAP record
*
* @param string DN
*
* @return array Record data
*/
function get_record($dn)
{
if (!$this->ready) {
return;
}
if ($rec = $this->get_entry($dn)) {
$rec = rcube_ldap_generic::normalize_entry($rec);
$rec = $this->field_mapping($dn, $rec);
}
return $rec;
}
/**
* Replace LDAP record data items
*
* @param string $dn DN
* @param array $entry LDAP entry
*
* return bool True on success, False on failure
*/
function replace($dn, $entry)
{
// fields mapping
foreach ($this->fieldmap as $field => $attr) {
if (array_key_exists($field, $entry)) {
$entry[$attr] = $entry[$field];
unset($entry[$field]);
}
}
return $this->mod_replace($dn, $entry);
}
/**
* Search records (simplified version of rcube_ldap::search)
*
* @param mixed $fields The field name of array of field names to search in
* @param mixed $value Search value (or array of values when $fields is array)
* @param int $mode Matching mode:
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* @param boolean $select True if results are requested, False if count only
* @param array $required List of fields that cannot be empty
* @param int $limit Number of records
*
* @return array List or false on error
*/
function search($fields, $value, $mode=1, $required = array(), $limit = 0)
{
$mode = intval($mode);
// use AND operator for advanced searches
$filter = is_array($value) ? '(&' : '(|';
// set wildcards
$wp = $ws = '';
if (!empty($this->config['fuzzy_search']) && $mode != 1) {
$ws = '*';
if (!$mode) {
$wp = '*';
}
}
foreach ((array)$fields as $idx => $field) {
$val = is_array($value) ? $value[$idx] : $value;
if ($attrs = (array) $this->fieldmap[$field]) {
if (count($attrs) > 1)
$filter .= '(|';
foreach ($attrs as $f)
$filter .= "($f=$wp" . rcube_ldap_generic::quote_string($val) . "$ws)";
if (count($attrs) > 1)
$filter .= ')';
}
}
$filter .= ')';
// add required (non empty) fields filter
$req_filter = '';
foreach ((array)$required as $field) {
if (in_array($field, (array)$fields)) // required field is already in search filter
continue;
if ($attrs = (array) $this->fieldmap[$field]) {
if (count($attrs) > 1)
$req_filter .= '(|';
foreach ($attrs as $f)
$req_filter .= "($f=*)";
if (count($attrs) > 1)
$req_filter .= ')';
}
}
if (!empty($req_filter)) {
$filter = '(&' . $req_filter . $filter . ')';
}
// avoid double-wildcard if $value is empty
$filter = preg_replace('/\*+/', '*', $filter);
// add general filter to query
if (!empty($this->config['filter'])) {
$filter = '(&(' . preg_replace('/^\(|\)$/', '', $this->config['filter']) . ')' . $filter . ')';
}
$base_dn = $this->parse_vars($this->config['base_dn']);
$scope = $this->config['scope'];
$attrs = array_values($this->fieldmap);
$list = array();
if ($result = parent::search($base_dn, $filter, $scope, $attrs)) {
$i = 0;
foreach ($result as $entry) {
if ($limit && $limit <= $i) {
break;
}
- $dn = $result->get_dn();
+ $dn = $result->get_dn();
+ $entry = rcube_ldap_generic::normalize_entry($entry);
$list[$dn] = $this->field_mapping($dn, $entry);
$i++;
}
}
return $list;
}
/**
* Set filter used in search()
*/
function set_filter($filter)
{
$this->config['filter'] = $filter;
}
/**
* Maps LDAP attributes to defined fields
*/
protected function field_mapping($dn, $entry)
{
$entry['dn'] = $dn;
// fields mapping
foreach ($this->fieldmap as $field => $attr) {
if (isset($entry[$attr])) {
$entry[$field] = $entry[$attr];
}
}
return $entry;
}
/**
* Detects group member attribute name
*/
private function get_group_member_attr($object_classes = array())
{
if (empty($object_classes)) {
$object_classes = $this->config['groups']['object_classes'];
}
if (!empty($object_classes)) {
foreach ((array)$object_classes as $oc) {
switch (strtolower($oc)) {
case 'group':
case 'groupofnames':
case 'kolabgroupofnames':
$member_attr = 'member';
break;
case 'groupofuniquenames':
case 'kolabgroupofuniquenames':
$member_attr = 'uniqueMember';
break;
}
}
}
if (!empty($member_attr)) {
return $member_attr;
}
if (!empty($this->config['groups']['member_attr'])) {
return $this->config['groups']['member_attr'];
}
return 'member';
}
/**
* Prepares filter query for LDAP search
*/
function parse_vars($str, $user = null, $host = null)
{
// When authenticating user $user is always set
// if not set it means we use this LDAP object for other
// purposes, e.g. kolab_delegation, then username with
// correct domain is in a session
if (!$user) {
$user = $_SESSION['username'];
}
else if (isset($this->icache[$user])) {
list($user, $dc) = $this->icache[$user];
}
else {
$orig_user = $user;
$rcmail = rcube::get_instance();
// get default domain
if ($username_domain = $rcmail->config->get('username_domain')) {
if ($host && is_array($username_domain) && isset($username_domain[$host])) {
$domain = rcube_utils::parse_host($username_domain[$host], $host);
}
else if (is_string($username_domain)) {
$domain = rcube_utils::parse_host($username_domain, $host);
}
}
// realmed username (with domain)
if (strpos($user, '@')) {
list($usr, $dom) = explode('@', $user);
// unrealm domain, user login can contain a domain alias
if ($dom != $domain && ($dc = $this->find_domain($dom))) {
// @FIXME: we should replace domain in $user, I suppose
}
}
else if ($domain) {
$user .= '@' . $domain;
}
$this->icache[$orig_user] = array($user, $dc);
}
// replace variables in filter
list($u, $d) = explode('@', $user);
// hierarchal domain string
if (empty($dc)) {
$dc = 'dc=' . strtr($d, array('.' => ',dc='));
}
$replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $user, '%u' => $u);
return strtr($str, $replaces);
}
/**
* Find root domain for specified domain
*
* @param string $domain Domain name
*
* @return string Domain DN string
*/
function find_domain($domain)
{
if (empty($domain) || empty($this->config['domain_base_dn']) || empty($this->config['domain_filter'])) {
return null;
}
$base_dn = $this->config['domain_base_dn'];
$filter = $this->config['domain_filter'];
$name_attr = $this->config['domain_name_attribute'];
if (empty($name_attr)) {
$name_attr = 'associateddomain';
}
$filter = str_replace('%s', rcube_ldap_generic::quote_string($domain), $filter);
$result = parent::search($base_dn, $filter, 'sub', array($name_attr, 'inetdomainbasedn'));
if (!$result) {
return null;
}
$entries = $result->entries(true);
$entry_dn = key($entries);
$entry = $entries[$entry_dn];
if (is_array($entry)) {
if (!empty($entry['inetdomainbasedn'])) {
return $entry['inetdomainbasedn'];
}
$domain = is_array($entry[$name_attr]) ? $entry[$name_attr][0] : $entry[$name_attr];
return $domain ? 'dc=' . implode(',dc=', explode('.', $domain)) : null;
}
}
/**
* HTML-safe DN string encoding
*
* @param string $str DN string
*
* @return string Encoded HTML identifier string
*/
static function dn_encode($str)
{
return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
}
/**
* Decodes DN string encoded with _dn_encode()
*
* @param string $str Encoded HTML identifier string
*
* @return string DN string
*/
static function dn_decode($str)
{
$str = str_pad(strtr($str, '-_', '+/'), strlen($str) % 4, '=', STR_PAD_RIGHT);
return base64_decode($str);
}
}
diff --git a/plugins/kolab_delegation/kolab_delegation.php b/plugins/kolab_delegation/kolab_delegation.php
index 156e81f4..23e0107c 100644
--- a/plugins/kolab_delegation/kolab_delegation.php
+++ b/plugins/kolab_delegation/kolab_delegation.php
@@ -1,494 +1,518 @@
<?php
/**
* Delegation configuration utility for Kolab accounts
*
* @version @package_version@
* @author Aleksander Machniak <machniak@kolabsys.com>
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2011-2012, 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/>.
*/
class kolab_delegation extends rcube_plugin
{
public $task = 'login|mail|settings|calendar';
private $rc;
private $engine;
/**
* Plugin initialization.
*/
public function init()
{
$this->rc = rcube::get_instance();
$this->require_plugin('libkolab');
$this->require_plugin('kolab_auth');
// on-login delegation initialization
$this->add_hook('login_after', array($this, 'login_hook'));
// on-check-recent delegation support
$this->add_hook('check_recent', array($this, 'check_recent_hook'));
// on-message-send delegation support
$this->add_hook('message_before_send', array($this, 'message_before_send'));
// delegation support in Calendar plugin
$this->add_hook('message_load', array($this, 'message_load'));
$this->add_hook('calendar_user_emails', array($this, 'calendar_user_emails'));
$this->add_hook('calendar_list_filter', array($this, 'calendar_list_filter'));
$this->add_hook('calendar_load_itip', array($this, 'calendar_load_itip'));
+ // delegation support in kolab_auth plugin
+ $this->add_hook('kolab_auth_emails', array($this, 'kolab_auth_emails'));
+
if ($this->rc->task == 'settings') {
// delegation management interface
$this->register_action('plugin.delegation', array($this, 'controller_ui'));
$this->register_action('plugin.delegation-delete', array($this, 'controller_action'));
$this->register_action('plugin.delegation-save', array($this, 'controller_action'));
$this->register_action('plugin.delegation-autocomplete', array($this, 'controller_action'));
if ($this->rc->action == 'plugin.delegation' || empty($_REQUEST['_framed'])) {
$this->add_texts('localization/', array('tabtitle', 'deleteconfirm', 'savingdata', 'yes', 'no'));
$this->include_script('kolab_delegation.js');
$this->skin_path = $this->local_skin_path();
$this->include_stylesheet($this->skin_path . '/style.css');
}
}
// Calendar plugin UI bindings
else if ($this->rc->task == 'calendar' && empty($_REQUEST['_framed'])) {
$this->calendar_ui();
}
}
/**
* Engine object getter
*/
private function engine()
{
if (!$this->engine) {
require_once $this->home . '/kolab_delegation_engine.php';
$this->load_config();
$this->engine = new kolab_delegation_engine();
}
return $this->engine;
}
/**
* On-login action
*/
public function login_hook($args)
{
// Manage (create) identities for delegator's email addresses
// and subscribe to delegator's folders. Also remove identities
// after delegation is removed
$engine = $this->engine();
$engine->delegation_init();
return $args;
}
/**
* Check-recent action
*/
public function check_recent_hook($args)
{
// Checking for new messages shall be extended to Inbox folders of all
// delegators if 'check_all_folders' is set to false.
if ($this->rc->task != 'mail') {
return $args;
}
if (!empty($args['all'])) {
return $args;
}
if (empty($_SESSION['delegators'])) {
return $args;
}
$storage = $this->rc->get_storage();
$other_ns = $storage->get_namespace('other');
$folders = $storage->list_folders_subscribed('', '*', 'mail');
foreach (array_keys($_SESSION['delegators']) as $uid) {
foreach ($other_ns as $ns) {
$folder = $ns[0] . $uid;
if (in_array($folder, $folders) && !in_array($folder, $args['folders'])) {
$args['folders'][] = $folder;
}
}
}
return $args;
}
/**
* Mail send action
*/
public function message_before_send($args)
{
// Checking headers of email being send, we'll add
// Sender: header if mail is send on behalf of someone else
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_delivery_filter($args);
}
return $args;
}
/**
* E-mail message loading action
*/
public function message_load($args)
{
// This is a place where we detect delegate context
// So we can handle event invitations on behalf of delegator
// @TODO: should we do this only in delegators' folders?
$engine = $this->engine();
$context = $engine->delegator_context_from_message($args['object']);
if ($context) {
$this->rc->output->set_env('delegator_context', $context);
$this->include_script('kolab_delegation.js');
}
return $args;
}
/**
* calendar::get_user_emails() handler
*/
public function calendar_user_emails($args)
{
// In delegator context we'll use delegator's addresses
// instead of current user addresses
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_emails_filter($args);
}
return $args;
}
/**
* calendar_driver::list_calendars() handler
*/
public function calendar_list_filter($args)
{
// In delegator context we'll use delegator's folders
// instead of current user folders
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_folder_filter($args);
}
return $args;
}
/**
* calendar::load_itip() handler
*/
public function calendar_load_itip($args)
{
// In delegator context we'll use delegator's address/name
// for invitation responses
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$engine->delegator_identity_filter($args);
}
return $args;
}
/**
* Delegation support in Calendar plugin UI
*/
public function calendar_ui()
{
// Initialize handling of delegators' identities in event form
if (!empty($_SESSION['delegators'])) {
$engine = $this->engine();
$this->rc->output->set_env('namespace', $engine->namespace_js());
$this->rc->output->set_env('delegators', $engine->list_delegators_js());
$this->include_script('kolab_delegation.js');
}
}
+ /**
+ * Delegation support in kolab_auth plugin
+ */
+ public function kolab_auth_emails($args)
+ {
+ // Add delegators addresses to address selector in user identity form
+
+ if (!empty($_SESSION['delegators'])) {
+ // @TODO: Consider not adding all delegator addresses to the list.
+ // Instead add only address of currently edited identity
+ foreach ($_SESSION['delegators'] as $emails) {
+ $args['emails'] = array_merge($args['emails'], $emails);
+ }
+
+ $args['emails'] = array_unique($args['emails']);
+ sort($args['emails']);
+ }
+
+ return $args;
+ }
+
/**
* Delegation UI handler
*/
public function controller_ui()
{
// main interface (delegates list)
if (empty($_REQUEST['_framed'])) {
$this->register_handler('plugin.delegatelist', array($this, 'delegate_list'));
$this->rc->output->include_script('list.js');
$this->rc->output->send('kolab_delegation.settings');
}
// delegate frame
else {
$this->register_handler('plugin.delegateform', array($this, 'delegate_form'));
$this->register_handler('plugin.delegatefolders', array($this, 'delegate_folders'));
$this->rc->output->set_env('autocomplete_max', (int)$this->rc->config->get('autocomplete_max', 15));
$this->rc->output->set_env('autocomplete_min_length', $this->rc->config->get('autocomplete_min_length'));
$this->rc->output->add_label('autocompletechars', 'autocompletemore');
$this->rc->output->send('kolab_delegation.editform');
}
}
/**
* Delegation action handler
*/
public function controller_action()
{
$this->add_texts('localization/', true);
$engine = $this->engine();
// Delegate delete
if ($this->rc->action == 'plugin.delegation-delete') {
$id = get_input_value('id', RCUBE_INPUT_GPC);
$success = $engine->delegate_delete($id, (bool) get_input_value('acl', RCUBE_INPUT_GPC));
if ($success) {
$this->rc->output->show_message($this->gettext('deletesuccess'), 'confirmation');
$this->rc->output->command('plugin.delegate_save_complete', array('deleted' => $id));
}
else {
$this->rc->output->show_message($this->gettext('deleteerror'), 'error');
}
}
// Delegate add/update
else if ($this->rc->action == 'plugin.delegation-save') {
$id = get_input_value('id', RCUBE_INPUT_GPC);
$acl = get_input_value('folders', RCUBE_INPUT_GPC);
// update
if ($id) {
$delegate = $engine->delegate_get($id);
$success = $engine->delegate_acl_update($delegate['uid'], $acl);
if ($success) {
$this->rc->output->show_message($this->gettext('updatesuccess'), 'confirmation');
$this->rc->output->command('plugin.delegate_save_complete', array('updated' => $id));
}
else {
$this->rc->output->show_message($this->gettext('updateerror'), 'error');
}
}
// new
else {
$login = get_input_value('newid', RCUBE_INPUT_GPC);
$delegate = $engine->delegate_get_by_name($login);
$success = $engine->delegate_add($delegate, $acl);
if ($success) {
$this->rc->output->show_message($this->gettext('createsuccess'), 'confirmation');
$this->rc->output->command('plugin.delegate_save_complete', array(
'created' => $delegate['ID'],
'name' => $delegate['name'],
));
}
else {
$this->rc->output->show_message($this->gettext('createerror'), 'error');
}
}
}
// Delegate autocompletion
else if ($this->rc->action == 'plugin.delegation-autocomplete') {
$search = get_input_value('_search', RCUBE_INPUT_GPC, true);
$sid = get_input_value('_id', RCUBE_INPUT_GPC);
$users = $engine->list_users($search);
$this->rc->output->command('ksearch_query_results', $users, $search, $sid);
}
$this->rc->output->send();
}
/**
* Template object of delegates list
*/
public function delegate_list($attrib = array())
{
$attrib += array('id' => 'delegate-list');
$engine = $this->engine();
$list = $engine->list_delegates();
$table = new html_table();
// sort delegates list
asort($list, SORT_LOCALE_STRING);
foreach ($list as $id => $delegate) {
$table->add_row(array('id' => 'rcmrow' . $id));
$table->add(null, Q($delegate));
}
$this->rc->output->add_gui_object('delegatelist', $attrib['id']);
$this->rc->output->set_env('delegatecount', count($list));
return $table->show($attrib);
}
/**
* Template object of delegate form
*/
public function delegate_form($attrib = array())
{
$engine = $this->engine();
$table = new html_table(array('cols' => 2));
$id = get_input_value('_id', RCUBE_INPUT_GPC);
$field_id = 'delegate';
if ($id) {
$delegate = $engine->delegate_get($id);
}
if ($delegate) {
$input = new html_hiddenfield(array('name' => $field_id, 'id' => $field_id, 'size' => 40));
$input = Q($delegate['name']) . $input->show($id);
$this->rc->output->set_env('active_delegate', $id);
$this->rc->output->command('parent.enable_command','delegate-delete', true);
}
else {
$input = new html_inputfield(array('name' => $field_id, 'id' => $field_id, 'size' => 40));
$input = $input->show();
}
$table->add('title', html::label($field_id, $this->gettext('delegate')));
$table->add(null, $input);
if ($attrib['form']) {
$this->rc->output->add_gui_object('editform', $attrib['form']);
}
return $table->show($attrib);
}
/**
* Template object of folders list
*/
public function delegate_folders($attrib = array())
{
if (!$attrib['id']) {
$attrib['id'] = 'delegatefolders';
}
$engine = $this->engine();
$id = get_input_value('_id', RCUBE_INPUT_GPC);
if ($id) {
$delegate = $engine->delegate_get($id);
}
$folder_data = $engine->list_folders($delegate['uid']);
$rights = array();
$folder_groups = array();
foreach ($folder_data as $folder_name => $folder) {
$folder_groups[$folder['type']][] = $folder_name;
$rights[$folder_name] = $folder['rights'];
}
// build block for every folder type
foreach ($folder_groups as $type => $group) {
if (empty($group)) {
continue;
}
$attrib['type'] = $type;
$html .= html::div('foldersblock',
html::tag('h3', $type, $this->gettext($type)) .
$this->delegate_folders_block($group, $attrib, $rights));
}
$this->rc->output->add_gui_object('folderslist', $attrib['id']);
return html::div($attrib, $html);
}
/**
* List of folders in specified group
*/
private function delegate_folders_block($a_folders, $attrib, $rights)
{
$path = 'plugins/kolab_delegation/' . $this->skin_path . '/';
$read_ico = $attrib['readicon'] ? html::img(array('src' => $path . $attrib['readicon'], 'title' => $this->gettext('read'))) : '';
$write_ico = $attrib['writeicon'] ? html::img(array('src' => $path . $attrib['writeicon'], 'title' => $this->gettext('write'))) : '';
$table = new html_table(array('cellspacing' => 0));
$table->add_header('read', $read_ico);
$table->add_header('write', $write_ico);
$table->add_header('foldername', $this->rc->gettext('folder'));
$checkbox_read = new html_checkbox(array('name' => 'read[]', 'class' => 'read'));
$checkbox_write = new html_checkbox(array('name' => 'write[]', 'class' => 'write'));
$names = array();
foreach ($a_folders as $folder) {
$foldername = $origname = preg_replace('/^INBOX »\s+/', '', kolab_storage::object_name($folder));
// find folder prefix to truncate (the same code as in kolab_addressbook plugin)
for ($i = count($names)-1; $i >= 0; $i--) {
if (strpos($foldername, $names[$i].' » ') === 0) {
$length = strlen($names[$i].' » ');
$prefix = substr($foldername, 0, $length);
$count = count(explode(' » ', $prefix));
$foldername = str_repeat(' ', $count-1) . '» ' . substr($foldername, $length);
break;
}
}
$folder_id = 'rcmf' . html_identifier($folder);
$names[] = $origname;
$classes = array('mailbox');
if ($folder_class = $this->rc->folder_classname($folder)) {
$foldername = html::quote($this->rc->gettext($folder_class));
$classes[] = $folder_class;
}
$table->add_row();
$table->add('read', $checkbox_read->show(
$rights[$folder] >= kolab_delegation_engine::ACL_READ ? $folder : null,
array('value' => $folder)));
$table->add('write', $checkbox_write->show(
$rights[$folder] >= kolab_delegation_engine::ACL_WRITE ? $folder : null,
array('value' => $folder, 'id' => $folder_id)));
$table->add(join(' ', $classes), html::label($folder_id, $foldername));
}
return $table->show();
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Apr 6, 12:09 AM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18831668
Default Alt Text
(53 KB)
Attached To
Mode
rRPK roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline