Support user_specific source in kolab_users_directory (Bifrost#T236416)
Move kolab_auth/kolab_auth_ldap to libkolab/lib/kolab_ldap. It ends up much simpler to add user_specific support and unify some code than replace use of kolab_auth_ldap with rcube_ldap. This means that libkolab plugin does not depend on kolab_auth plugin anymore, but kolab_auth depends on libkolab, which is better situation.
This commit is contained in:
parent
14e1a98d8c
commit
80a5241a9d
6 changed files with 193 additions and 82 deletions
|
@ -25,6 +25,7 @@
|
|||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"roundcube/plugin-installer": ">=0.1.3"
|
||||
"roundcube/plugin-installer": ">=0.1.3",
|
||||
"kolab/libkolab": ">=3.5.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ class kolab_auth extends rcube_plugin
|
|||
$rcmail = rcube::get_instance();
|
||||
|
||||
$this->load_config();
|
||||
$this->require_plugin('libkolab');
|
||||
|
||||
$this->add_hook('authenticate', array($this, 'authenticate'));
|
||||
$this->add_hook('startup', array($this, 'startup'));
|
||||
|
@ -796,28 +797,12 @@ class kolab_auth extends rcube_plugin
|
|||
*/
|
||||
public static function ldap()
|
||||
{
|
||||
self::$ldap = kolab_storage::ldap('kolab_auth_addressbook');
|
||||
|
||||
if (self::$ldap) {
|
||||
return self::$ldap;
|
||||
self::$ldap->extend_fieldmap(array('uniqueid' => 'nsuniqueid'));
|
||||
}
|
||||
|
||||
$rcmail = rcube::get_instance();
|
||||
$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;
|
||||
}
|
||||
|
||||
$addressbook['fieldmap']['uniqueid'] = 'nsuniqueid';
|
||||
|
||||
require_once __DIR__ . '/kolab_auth_ldap.php';
|
||||
|
||||
self::$ldap = new kolab_auth_ldap($addressbook);
|
||||
|
||||
return self::$ldap;
|
||||
}
|
||||
|
||||
|
@ -834,7 +819,7 @@ class kolab_auth extends rcube_plugin
|
|||
|
||||
/**
|
||||
* Parses LDAP DN string with replacing supported variables.
|
||||
* See kolab_auth_ldap::parse_vars()
|
||||
* See kolab_ldap::parse_vars()
|
||||
*
|
||||
* @param string $str LDAP DN string
|
||||
*
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"roundcube/plugin-installer": ">=0.1.3",
|
||||
"kolab/libkolab": ">=3.4.0",
|
||||
"kolab/kolab_auth": ">=3.4.0"
|
||||
"kolab/libkolab": ">=3.5.1",
|
||||
"kolab/kolab_auth": ">=3.5.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ class kolab_delegation_engine
|
|||
|
||||
// add delegate to the list
|
||||
$list[] = $dn;
|
||||
$list = array_map(array('kolab_auth_ldap', 'dn_decode'), $list);
|
||||
$list = array_map(array('kolab_ldap', 'dn_decode'), $list);
|
||||
|
||||
// update user record
|
||||
$result = $this->user_update_delegates($list);
|
||||
|
@ -149,7 +149,7 @@ class kolab_delegation_engine
|
|||
// remove delegate from the list
|
||||
unset($list[$dn]);
|
||||
$list = array_keys($list);
|
||||
$list = array_map(array('kolab_auth_ldap', 'dn_decode'), $list);
|
||||
$list = array_map(array('kolab_ldap', 'dn_decode'), $list);
|
||||
$user[$this->ldap_delegate_field] = $list;
|
||||
|
||||
// update user record
|
||||
|
@ -181,7 +181,7 @@ class kolab_delegation_engine
|
|||
}
|
||||
|
||||
// Get delegate
|
||||
$user = $ldap->get_record(kolab_auth_ldap::dn_decode($dn));
|
||||
$user = $ldap->get_record(kolab_ldap::dn_decode($dn));
|
||||
|
||||
if (empty($user)) {
|
||||
return array();
|
||||
|
@ -230,27 +230,9 @@ class kolab_delegation_engine
|
|||
return $this->ldap;
|
||||
}
|
||||
|
||||
if ($addressbook = $this->rc->config->get('kolab_delegation_addressbook')) {
|
||||
if (!is_array($addressbook)) {
|
||||
$ldap_config = (array) $this->rc->config->get('ldap_public');
|
||||
$addressbook = $ldap_config[$addressbook];
|
||||
}
|
||||
$this->ldap = kolab_storage::ldap('kolab_delegation_addressbook');
|
||||
|
||||
if (!empty($addressbook)) {
|
||||
require_once __DIR__ . '/../kolab_auth/kolab_auth_ldap.php';
|
||||
|
||||
$ldap = new kolab_auth_ldap($addressbook);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to kolab_auth plugin's addressbook
|
||||
if (!$ldap) {
|
||||
$ldap = kolab_auth::ldap();
|
||||
}
|
||||
|
||||
$this->ldap = $ldap;
|
||||
|
||||
if (!$ldap || !$ldap->ready) {
|
||||
if (!$this->ldap || !$this->ldap->ready) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -270,10 +252,10 @@ class kolab_delegation_engine
|
|||
// Name of the LDAP field with organization name for identities
|
||||
$this->ldap_org_field = $this->rc->config->get('kolab_delegation_organization_field', $this->rc->config->get('kolab_auth_organization'));
|
||||
|
||||
$ldap->set_filter($this->ldap_filter);
|
||||
$ldap->extend_fieldmap(array($this->ldap_delegate_field => $this->ldap_delegate_field));
|
||||
$this->ldap->set_filter($this->ldap_filter);
|
||||
$this->ldap->extend_fieldmap(array($this->ldap_delegate_field => $this->ldap_delegate_field));
|
||||
|
||||
return $ldap;
|
||||
return $this->ldap;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -540,7 +522,7 @@ class kolab_delegation_engine
|
|||
}
|
||||
|
||||
return array(
|
||||
'ID' => kolab_auth_ldap::dn_encode($dn),
|
||||
'ID' => kolab_ldap::dn_encode($dn),
|
||||
'uid' => $uid,
|
||||
'name' => $name,
|
||||
'realname' => $realname,
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Kolab Authentication
|
||||
* Kolab Authentication and User Base
|
||||
*
|
||||
* @version @package_version@
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2011-2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2011-2019, 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
|
||||
|
@ -25,10 +24,11 @@
|
|||
/**
|
||||
* Wrapper class for rcube_ldap_generic
|
||||
*/
|
||||
class kolab_auth_ldap extends rcube_ldap_generic
|
||||
class kolab_ldap extends rcube_ldap_generic
|
||||
{
|
||||
private $conf = array();
|
||||
private $fieldmap = array();
|
||||
private $rcache;
|
||||
|
||||
|
||||
function __construct($p)
|
||||
|
@ -44,6 +44,11 @@ class kolab_auth_ldap extends rcube_ldap_generic
|
|||
$p['attributes'] = array_values($this->fieldmap);
|
||||
$p['debug'] = (bool) $rcmail->config->get('ldap_debug');
|
||||
|
||||
if ($cache_type = $rcmail->config->get('ldap_cache', 'db')) {
|
||||
$cache_ttl = $rcmail->config->get('ldap_cache_ttl', '10m');
|
||||
$this->cache = $rcmail->get_cache('LDAP.kolab_cache', $cache_type, $cache_ttl);
|
||||
}
|
||||
|
||||
// Connect to the server (with bind)
|
||||
parent::__construct($p);
|
||||
$this->_connect();
|
||||
|
@ -65,19 +70,145 @@ class kolab_auth_ldap extends rcube_ldap_generic
|
|||
continue;
|
||||
}
|
||||
|
||||
$bind_pass = $this->config['bind_pass'];
|
||||
$bind_user = $this->config['bind_user'];
|
||||
$bind_dn = $this->config['bind_dn'];
|
||||
$bind_pass = $this->config['bind_pass'];
|
||||
$bind_user = $this->config['bind_user'];
|
||||
$bind_dn = $this->config['bind_dn'];
|
||||
$base_dn = $this->config['base_dn'];
|
||||
$groups_base_dn = $this->config['groups']['base_dn'] ?: $base_dn;
|
||||
|
||||
// User specific access, generate the proper values to use.
|
||||
if ($this->config['user_specific']) {
|
||||
$rcube = rcube::get_instance();
|
||||
|
||||
// No password set, use the session password
|
||||
if (empty($bind_pass)) {
|
||||
$bind_pass = $rcube->get_user_password();
|
||||
}
|
||||
|
||||
// Get the pieces needed for variable replacement.
|
||||
if ($fu = ($rcube->get_user_email() ?: $this->config['username'])) {
|
||||
list($u, $d) = explode('@', $fu);
|
||||
}
|
||||
else {
|
||||
$d = $this->config['mail_domain'];
|
||||
}
|
||||
|
||||
$dc = 'dc=' . strtr($d, array('.' => ',dc=')); // hierarchal domain string
|
||||
|
||||
// resolve $dc through LDAP
|
||||
if (!empty($this->config['domain_filter']) && !empty($this->config['search_bind_dn'])) {
|
||||
$this->bind($this->config['search_bind_dn'], $this->config['search_bind_pw']);
|
||||
$dc = $this->domain_root_dn($d);
|
||||
}
|
||||
|
||||
$replaces = array('%dn' => '', '%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u);
|
||||
|
||||
// Search for the dn to use to authenticate
|
||||
if ($this->config['search_base_dn'] && $this->config['search_filter']
|
||||
&& (strstr($bind_dn, '%dn') || strstr($base_dn, '%dn') || strstr($groups_base_dn, '%dn'))
|
||||
) {
|
||||
$search_attribs = array('uid');
|
||||
if ($search_bind_attrib = (array) $this->config['search_bind_attrib']) {
|
||||
foreach ($search_bind_attrib as $r => $attr) {
|
||||
$search_attribs[] = $attr;
|
||||
$replaces[$r] = '';
|
||||
}
|
||||
}
|
||||
|
||||
$search_bind_dn = strtr($this->config['search_bind_dn'], $replaces);
|
||||
$search_base_dn = strtr($this->config['search_base_dn'], $replaces);
|
||||
$search_filter = strtr($this->config['search_filter'], $replaces);
|
||||
|
||||
$cache_key = 'DN.' . md5("$host:$search_bind_dn:$search_base_dn:$search_filter:" . $this->config['search_bind_pw']);
|
||||
|
||||
if ($this->cache && ($dn = $this->cache->get($cache_key))) {
|
||||
$replaces['%dn'] = $dn;
|
||||
}
|
||||
else {
|
||||
$ldap = $this;
|
||||
if (!empty($search_bind_dn) && !empty($this->config['search_bind_pw'])) {
|
||||
// To protect from "Critical extension is unavailable" error
|
||||
// we need to use a separate LDAP connection
|
||||
if (!empty($this->config['vlv'])) {
|
||||
$ldap = new rcube_ldap_generic($this->config);
|
||||
$ldap->config_set(array('cache' => $this->cache, 'debug' => $this->debug));
|
||||
if (!$ldap->connect($host)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$ldap->bind($search_bind_dn, $this->config['search_bind_pw'])) {
|
||||
continue; // bind failed, try next host
|
||||
}
|
||||
}
|
||||
|
||||
$res = $ldap->search($search_base_dn, $search_filter, 'sub', $search_attribs);
|
||||
if ($res) {
|
||||
$res->rewind();
|
||||
$replaces['%dn'] = key($res->entries(true));
|
||||
|
||||
// add more replacements from 'search_bind_attrib' config
|
||||
if ($search_bind_attrib) {
|
||||
$res = $res->current();
|
||||
foreach ($search_bind_attrib as $r => $attr) {
|
||||
$replaces[$r] = $res[$attr][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($ldap != $this) {
|
||||
$ldap->close();
|
||||
}
|
||||
}
|
||||
|
||||
// DN not found
|
||||
if (empty($replaces['%dn'])) {
|
||||
if (!empty($this->config['search_dn_default']))
|
||||
$replaces['%dn'] = $this->config['search_dn_default'];
|
||||
else {
|
||||
rcube::raise_error(array(
|
||||
'code' => 100, 'type' => 'ldap',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "DN not found using LDAP search."), true);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->cache && !empty($replaces['%dn'])) {
|
||||
$this->cache->set($cache_key, $replaces['%dn']);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the bind_dn and base_dn variables.
|
||||
$bind_dn = strtr($bind_dn, $replaces);
|
||||
$base_dn = strtr($base_dn, $replaces);
|
||||
$groups_base_dn = strtr($groups_base_dn, $replaces);
|
||||
|
||||
// replace placeholders in filter settings
|
||||
if (!empty($this->config['filter'])) {
|
||||
$this->config['filter'] = strtr($this->config['filter'], $replaces);
|
||||
}
|
||||
|
||||
foreach (array('base_dn', 'filter', 'member_filter') as $k) {
|
||||
if (!empty($this->config['groups'][$k])) {
|
||||
$this->config['groups'][$k] = strtr($this->config['groups'][$k], $replaces);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($bind_user)) {
|
||||
$bind_user = $u;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($bind_pass)) {
|
||||
$this->ready = true;
|
||||
}
|
||||
else {
|
||||
if (!empty($bind_dn)) {
|
||||
$this->ready = $this->bind($bind_dn, $bind_pass);
|
||||
if (!empty($this->config['auth_cid'])) {
|
||||
$this->ready = $this->sasl_bind($this->config['auth_cid'], $bind_pass, $bind_dn);
|
||||
}
|
||||
else if (!empty($this->config['auth_cid'])) {
|
||||
$this->ready = $this->sasl_bind($this->config['auth_cid'], $bind_pass, $bind_user);
|
||||
else if (!empty($bind_dn)) {
|
||||
$this->ready = $this->bind($bind_dn, $bind_pass);
|
||||
}
|
||||
else {
|
||||
$this->ready = $this->sasl_bind($bind_user, $bind_pass);
|
||||
|
@ -220,7 +351,7 @@ class kolab_auth_ldap extends rcube_ldap_generic
|
|||
* @param int $limit Number of records
|
||||
* @param int $count Returns the number of records found
|
||||
*
|
||||
* @return array List or false on error
|
||||
* @return array List of LDAP records found
|
||||
*/
|
||||
function dosearch($fields, $value, $mode=1, $required = array(), $limit = 0, &$count = 0)
|
||||
{
|
|
@ -48,10 +48,10 @@ class kolab_storage
|
|||
private static $subscriptions;
|
||||
private static $ldapcache = array();
|
||||
private static $typedata = array();
|
||||
private static $ldap = array();
|
||||
private static $states;
|
||||
private static $config;
|
||||
private static $imap;
|
||||
private static $ldap;
|
||||
|
||||
|
||||
// Default folder names
|
||||
|
@ -96,7 +96,7 @@ class kolab_storage
|
|||
'message' => "required kolabformat module not found"
|
||||
), true);
|
||||
}
|
||||
else {
|
||||
else if (self::$imap->get_error_code()) {
|
||||
rcube::raise_error(array(
|
||||
'code' => 900, 'type' => 'php', 'message' => "IMAP error"
|
||||
), true);
|
||||
|
@ -116,16 +116,23 @@ class kolab_storage
|
|||
|
||||
/**
|
||||
* Initializes LDAP object to resolve Kolab users
|
||||
*
|
||||
* @param string $name Name of the configuration option with LDAP config
|
||||
*/
|
||||
public static function ldap()
|
||||
public static function ldap($name = 'kolab_users_directory')
|
||||
{
|
||||
if (self::$ldap) {
|
||||
return self::$ldap;
|
||||
}
|
||||
|
||||
self::setup();
|
||||
|
||||
$config = self::$config->get('kolab_users_directory', self::$config->get('kolab_auth_addressbook'));
|
||||
$config = self::$config->get($name);
|
||||
|
||||
if (empty($config)) {
|
||||
$name = 'kolab_auth_addressbook';
|
||||
$config = self::$config->get($name);
|
||||
}
|
||||
|
||||
if (self::$ldap[$name]) {
|
||||
return self::$ldap[$name];
|
||||
}
|
||||
|
||||
if (!is_array($config)) {
|
||||
$ldap_config = (array)self::$config->get('ldap_public');
|
||||
|
@ -136,17 +143,21 @@ class kolab_storage
|
|||
return null;
|
||||
}
|
||||
|
||||
$ldap = new kolab_ldap($config);
|
||||
|
||||
// overwrite filter option
|
||||
if ($filter = self::$config->get('kolab_users_filter')) {
|
||||
self::$config->set('kolab_auth_filter', $filter);
|
||||
}
|
||||
|
||||
// re-use the LDAP wrapper class from kolab_auth plugin
|
||||
require_once rtrim(RCUBE_PLUGINS_DIR, '/') . '/kolab_auth/kolab_auth_ldap.php';
|
||||
$user_attrib = self::$config->get('kolab_users_id_attrib', self::$config->get('kolab_auth_login', 'mail'));
|
||||
|
||||
self::$ldap = new kolab_auth_ldap($config);
|
||||
//$ldap->set_filter($this->ldap_filter);
|
||||
$ldap->extend_fieldmap(array($user_attrib => $user_attrib));
|
||||
|
||||
return self::$ldap;
|
||||
self::$ldap[$name] = $ldap;
|
||||
|
||||
return $ldap;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1485,6 +1496,7 @@ class kolab_storage
|
|||
}
|
||||
|
||||
/**
|
||||
* Search users in Kolab LDAP storage
|
||||
*
|
||||
* @param mixed $query Search value (or array of field => value pairs)
|
||||
* @param int $mode Matching mode: 0 - partial (*abc*), 1 - strict (=), 2 - prefix (abc*)
|
||||
|
@ -1492,19 +1504,23 @@ class kolab_storage
|
|||
* @param int $limit Maximum number of records
|
||||
* @param int $count Returns the number of records found
|
||||
*
|
||||
* @return array List or false on error
|
||||
* @return array List of users
|
||||
*/
|
||||
public static function search_users($query, $mode = 1, $required = array(), $limit = 0, &$count = 0)
|
||||
{
|
||||
$query = str_replace('*', '', $query);
|
||||
|
||||
// requires a working LDAP setup
|
||||
if (!self::ldap() || strlen($query) == 0) {
|
||||
if (!strlen($query) || !($ldap = self::ldap())) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$root = self::namespace_root('other');
|
||||
$user_attrib = self::$config->get('kolab_users_id_attrib', self::$config->get('kolab_auth_login', 'mail'));
|
||||
$search_attrib = self::$config->get('kolab_users_search_attrib', array('cn','mail','alias'));
|
||||
|
||||
// search users using the configured attributes
|
||||
$results = self::$ldap->dosearch(self::$config->get('kolab_users_search_attrib', array('cn','mail','alias')), $query, $mode, $required, $limit, $count);
|
||||
$results = $ldap->dosearch($search_attrib, $query, $mode, $required, $limit, $count);
|
||||
|
||||
// exclude myself
|
||||
if ($_SESSION['kolab_dn']) {
|
||||
|
@ -1512,9 +1528,6 @@ class kolab_storage
|
|||
}
|
||||
|
||||
// resolve to IMAP folder name
|
||||
$root = self::namespace_root('other');
|
||||
$user_attrib = self::$config->get('kolab_users_id_attrib', self::$config->get('kolab_auth_login', 'mail'));
|
||||
|
||||
array_walk($results, function(&$user, $dn) use ($root, $user_attrib) {
|
||||
list($localpart, ) = explode('@', $user[$user_attrib]);
|
||||
$user['kolabtargetfolder'] = $root . $localpart;
|
||||
|
@ -1652,7 +1665,6 @@ class kolab_storage
|
|||
|
||||
/**
|
||||
* Get user attributes for specified other user (imap) folder identifier.
|
||||
* Note: This uses LDAP config/code from kolab_auth.
|
||||
*
|
||||
* @param string $folder_id Folder name w/o path (imap user identifier)
|
||||
* @param bool $as_string Return configured display name attribute value
|
||||
|
@ -1692,7 +1704,7 @@ class kolab_storage
|
|||
$user = $cache->get($token);
|
||||
}
|
||||
|
||||
if (empty($user) && ($ldap = kolab_storage::ldap())) {
|
||||
if (empty($user) && ($ldap = self::ldap())) {
|
||||
$user = $ldap->get_user_record($token, $_SESSION['imap_host']);
|
||||
|
||||
if (!empty($user)) {
|
||||
|
|
Loading…
Add table
Reference in a new issue