diff --git a/htdocs/includes/pear/DB/ldap.php b/htdocs/includes/pear/DB/ldap.php new file mode 100644 index 00000000000..3d82d6cf60e --- /dev/null +++ b/htdocs/includes/pear/DB/ldap.php @@ -0,0 +1,525 @@ + php net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Authors: Jan Wagner netsols de> | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +require_once DOL_DOCUMENT_ROOT."/includes/pear/Auth/Container.php"; +require_once DOL_DOCUMENT_ROOT."/includes/pear/PEAR.php"; + +/** + * Storage driver for fetching login data from LDAP + * + * This class is heavily based on the DB and File containers. By default it + * connects to localhost:389 and searches for uid=$username with the scope + * "sub". If no search base is specified, it will try to determine it via + * the namingContexts attribute. It takes its parameters in a hash, connects + * to the ldap server, binds anonymously, searches for the user, and tries + * to bind as the user with the supplied password. When a group was set, it + * will look for group membership of the authenticated user. If all goes + * well the authentication was successful. + * + * Parameters: + * + * host: localhost (default), ldap.netsols.de or 127.0.0.1 + * port: 389 (default) or 636 or whereever your server runs + * url: ldap://localhost:389/ + * useful for ldaps://, works only with openldap2 ? + * it will be preferred over host and port + * version: LDAP version to use, ususally 2 (default) or 3, + * must be an integer! + * binddn: If set, searching for user will be done after binding + * as this user, if not set the bind will be anonymous. + * This is reported to make the container work with MS + * Active Directory, but should work with any server that + * is configured this way. + * This has to be a complete dn for now (basedn and + * userdn will not be appended). + * bindpw: The password to use for binding with binddn + * basedn: the base dn of your server + * userdn: gets prepended to basedn when searching for user + * userscope: Scope for user searching: one, sub (default), or base + * userattr: the user attribute to search for (default: uid) + * userfilter: filter that will be added to the search filter + * this way: (&(userattr=username)(userfilter)) + * default: (objectClass=posixAccount) + * attributes: array of additional attributes to fetch from entry. + * these will added to auth data and can be retrieved via + * Auth::getAuthData(). An empty array will fetch all attributes, + * array('') will fetch no attributes at all (default) + * groupdn: gets prepended to basedn when searching for group + * groupattr: the group attribute to search for (default: cn) + * groupfilter: filter that will be added to the search filter when + * searching for a group: + * (&(groupattr=group)(memberattr=username)(groupfilter)) + * default: (objectClass=groupOfUniqueNames) + * memberattr : the attribute of the group object where the user dn + * may be found (default: uniqueMember) + * memberisdn: whether the memberattr is the dn of the user (default) + * or the value of userattr (usually uid) + * group: the name of group to search for + * groupscope: Scope for group searching: one, sub (default), or base + * debug: Enable/Disable debugging output (default: false) + * + * To use this storage container, you have to use the following syntax: + * + * 'localhost', + * 'port' => '389', + * 'version' => 3, + * 'basedn' => 'o=netsols, c=de', + * 'userattr' => 'uid' + * 'binddn' => 'cn=admin, o=netsols, c=de', + * 'bindpw' => 'password')); + * + * $a2 = new Auth('LDAP', array( + * 'url' => 'ldaps://ldap.netsols.de', + * 'basedn' => 'o=netsols, c=de', + * 'userscope' => 'one', + * 'userdn' => 'ou=People', + * 'groupdn' => 'ou=Groups', + * 'groupfilter' => '(objectClass=posixGroup)', + * 'memberattr' => 'memberUid', + * 'memberisdn' => false, + * 'group' => 'admin' + * )); + * + * This is a full blown example with user/group checking to an Active Directory + * + * $a3 = new Auth('LDAP', array( + * 'host' => 'ldap.netsols.de', + * 'port' => 389, + * 'version' => 3, + * 'basedn' => 'dc=netsols, dc=de', + * 'binddn' => 'cn=Jan Wagner, cn=Users, dc=netsols, dc=de', + * 'bindpw' => 'password', + * 'userattr' => 'samAccountName', + * 'userfilter' => '(objectClass=user)', + * 'attributes' => array(''), + * 'group' => 'testing', + * 'groupattr' => 'samAccountName', + * 'groupfilter' => '(objectClass=group)', + * 'memberattr' => 'member', + * 'memberisdn' => true, + * 'groupdn' => 'cn=Users', + * 'groupscope' => 'one', + * 'debug' => true); + * + * The parameter values have to correspond + * to the ones for your LDAP server of course. + * + * When talking to a Microsoft ActiveDirectory server you have to + * use 'samaccountname' as the 'userattr' and follow special rules + * to translate the ActiveDirectory directory names into 'basedn'. + * The 'basedn' for the default 'Users' folder on an ActiveDirectory + * server for the ActiveDirectory Domain (which is not related to + * its DNS name) "win2000.example.org" would be: + * "CN=Users, DC=win2000, DC=example, DC=org' + * where every component of the domain name becomes a DC attribute + * of its own. If you want to use a custom users folder you have to + * replace "CN=Users" with a sequence of "OU" attributes that specify + * the path to your custom folder in reverse order. + * So the ActiveDirectory folder + * "win2000.example.org\Custom\Accounts" + * would become + * "OU=Accounts, OU=Custom, DC=win2000, DC=example, DC=org' + * + * It seems that binding anonymously to an Active Directory + * is not allowed, so you have to set binddn and bindpw for + * user searching, + * + * Example a3 shows a tested example for connenction to Windows 2000 + * Active Directory + * + * @author Jan Wagner netsols de> + * @package Auth + * @version $Revision$ + */ +class Auth_Container_LDAP extends Auth_Container +{ + /** + * Options for the class + * @var array + */ + var $options = array(); + + /** + * Connection ID of LDAP Link + * @var string + */ + var $conn_id = false; + + /** + * Constructor of the container class + * + * @param $params, associative hash with host, port, basedn and userattr key + * @return object Returns an error object if something went wrong + */ + function Auth_Container_LDAP($params) + { + if (false === extension_loaded('ldap')) { + return PEAR::raiseError('Auth_Container_LDAP: LDAP Extension not loaded', 41, PEAR_ERROR_DIE); + } + + $this->_setDefaults(); + + if (is_array($params)) { + $this->_parseOptions($params); + } + } + + // }}} + // {{{ _connect() + + /** + * Connect to the LDAP server using the global options + * + * @access private + * @return object Returns a PEAR error object if an error occurs. + */ + function _connect() + { + // connect + if (isset($this->options['url']) && $this->options['url'] != '') { + $this->_debug('Connecting with URL', __LINE__); + $conn_params = array($this->options['url']); + } else { + $this->_debug('Connecting with host:port', __LINE__); + $conn_params = array($this->options['host'], $this->options['port']); + } + + if (($this->conn_id = @call_user_func_array('ldap_connect', $conn_params)) === false) { + return PEAR::raiseError('Auth_Container_LDAP: Could not connect to server.', 41, PEAR_ERROR_DIE); + } + $this->_debug('Successfully connected to server', __LINE__); + + // switch LDAP version + if (is_int($this->options['version']) && $this->options['version'] > 2) { + $this->_debug("Switching to LDAP version {$this->options['version']}", __LINE__); + @ldap_set_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $this->options['version']); + } + + // bind with credentials or anonymously + if ($this->options['binddn'] && $this->options['bindpw']) { + $this->_debug('Binding with credentials', __LINE__); + $bind_params = array($this->conn_id, $this->options['binddn'], $this->options['bindpw']); + } else { + $this->_debug('Binding anonymously', __LINE__); + $bind_params = array($this->conn_id); + } + // bind for searching + if ((@call_user_func_array('ldap_bind', $bind_params)) == false) { + $this->_debug(); + $this->_disconnect(); + return PEAR::raiseError("Auth_Container_LDAP: Could not bind to LDAP server.", 41, PEAR_ERROR_DIE); + } + $this->_debug('Binding was successful', __LINE__); + } + + /** + * Disconnects (unbinds) from ldap server + * + * @access private + */ + function _disconnect() + { + if ($this->_isValidLink()) { + $this->_debug('disconnecting from server'); + @ldap_unbind($this->conn_id); + } + } + + /** + * Tries to find Basedn via namingContext Attribute + * + * @access private + */ + function _getBaseDN() + { + if ($this->options['basedn'] == "" && $this->_isValidLink()) { + $this->_debug("basedn not set, searching via namingContexts.", __LINE__); + + $result_id = @ldap_read($this->conn_id, "", "(objectclass=*)", array("namingContexts")); + + if (@ldap_count_entries($this->conn_id, $result_id) == 1) { + + $this->_debug("got result for namingContexts", __LINE__); + + $entry_id = @ldap_first_entry($this->conn_id, $result_id); + $attrs = @ldap_get_attributes($this->conn_id, $entry_id); + $basedn = $attrs['namingContexts'][0]; + + if ($basedn != "") { + $this->_debug("result for namingContexts was $basedn", __LINE__); + $this->options['basedn'] = $basedn; + } + } + @ldap_free_result($result_id); + } + + // if base ist still not set, raise error + if ($this->options['basedn'] == "") { + return PEAR::raiseError("Auth_Container_LDAP: LDAP search base not specified!", 41, PEAR_ERROR_DIE); + } + return true; + } + + /** + * determines whether there is a valid ldap conenction or not + * + * @accessd private + * @return boolean + */ + function _isValidLink() + { + if (is_resource($this->conn_id)) { + if (get_resource_type($this->conn_id) == 'ldap link') { + return true; + } + } + return false; + } + + /** + * Set some default options + * + * @access private + */ + function _setDefaults() + { + $this->options['url'] = ''; + $this->options['host'] = 'localhost'; + $this->options['port'] = '389'; + $this->options['version'] = 2; + $this->options['binddn'] = ''; + $this->options['bindpw'] = ''; + $this->options['basedn'] = ''; + $this->options['userdn'] = ''; + $this->options['userscope'] = 'sub'; + $this->options['userattr'] = "uid"; + $this->options['userfilter'] = '(objectClass=posixAccount)'; + $this->options['attributes'] = array(''); // no attributes + $this->options['group'] = ''; + $this->options['groupdn'] = ''; + $this->options['groupscope'] = 'sub'; + $this->options['groupattr'] = 'cn'; + $this->options['groupfilter'] = '(objectClass=groupOfUniqueNames)'; + $this->options['memberattr'] = 'uniqueMember'; + $this->options['memberisdn'] = true; + $this->options['debug'] = false; + } + + /** + * Parse options passed to the container class + * + * @access private + * @param array + */ + function _parseOptions($array) + { + foreach ($array as $key => $value) { + if (array_key_exists($key, $this->options)) { + $this->options[$key] = $value; + } + } + } + + /** + * Get search function for scope + * + * @param string scope + * @return string ldap search function + */ + function _scope2function($scope) + { + switch($scope) { + case 'one': + $function = 'ldap_list'; + break; + case 'base': + $function = 'ldap_read'; + break; + default: + $function = 'ldap_search'; + break; + } + return $function; + } + + /** + * Fetch data from LDAP server + * + * Searches the LDAP server for the given username/password + * combination. + * + * @param string Username + * @param string Password + * @return boolean + */ + function fetchData($username, $password) + { + $this->_connect(); + $this->_getBaseDN(); + + // UTF8 Encode username for LDAPv3 + if (@ldap_get_option($this->conn_id, LDAP_OPT_PROTOCOL_VERSION, $ver) && $ver == 3) { + $this->_debug('UTF8 encoding username for LDAPv3', __LINE__); + $username = utf8_encode($username); + } + // make search filter + $filter = sprintf('(&(%s=%s)%s)', + $this->options['userattr'], + $username, + $this->options['userfilter']); + // make search base dn + $search_basedn = $this->options['userdn']; + if ($search_basedn != '' && substr($search_basedn, -1) != ', ') { + $search_basedn .= ', '; + } + $search_basedn .= $this->options['basedn']; + + // attributes + $attributes = $this->options['attributes']; + + // make functions params array + $func_params = array($this->conn_id, $search_basedn, $filter, $attributes); + + // search function to use + $func_name = $this->_scope2function($this->options['userscope']); + + $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__); + + // search + if (($result_id = @call_user_func_array($func_name, $func_params)) == false) { + $this->_debug('User not found', __LINE__); + } elseif (@ldap_count_entries($this->conn_id, $result_id) == 1) { // did we get just one entry? + + $this->_debug('User was found', __LINE__); + + // then get the user dn + $entry_id = @ldap_first_entry($this->conn_id, $result_id); + $user_dn = @ldap_get_dn($this->conn_id, $entry_id); + + // fetch attributes + if ($attributes = @ldap_get_attributes($this->conn_id, $entry_id)) { + if (is_array($attributes) && isset($attributes['count']) && + $attributes['count'] > 0) + { + $this->_debug('Saving attributes to Auth data', __LINE__); + $this->_auth_obj->setAuthData('attributes', $attributes); + } + } + @ldap_free_result($result_id); + + // need to catch an empty password as openldap seems to return TRUE + // if anonymous binding is allowed + if ($password != "") { + $this->_debug("Bind as $user_dn", __LINE__); + + // try binding as this user with the supplied password + if (@ldap_bind($this->conn_id, $user_dn, $password)) { + $this->_debug('Bind successful', __LINE__); + + // check group if appropiate + if (strlen($this->options['group'])) { + // decide whether memberattr value is a dn or the username + $this->_debug('Checking group membership', __LINE__); + return $this->checkGroup(($this->options['memberisdn']) ? $user_dn : $username); + } else { + $this->_debug('Authenticated', __LINE__); + $this->_disconnect(); + return true; // user authenticated + } // checkGroup + } // bind + } // non-empty password + } // one entry + // default + $this->_debug('NOT authenticated!', __LINE__); + $this->_disconnect(); + return false; + } + + /** + * Validate group membership + * + * Searches the LDAP server for group membership of the + * authenticated user + * + * @param string Distinguished Name of the authenticated User + * @return boolean + */ + function checkGroup($user) + { + // make filter + $filter = sprintf('(&(%s=%s)(%s=%s)%s)', + $this->options['groupattr'], + $this->options['group'], + $this->options['memberattr'], + $user, + $this->options['groupfilter']); + + // make search base dn + $search_basedn = $this->options['groupdn']; + if ($search_basedn != '' && substr($search_basedn, -1) != ', ') { + $search_basedn .= ', '; + } + $search_basedn .= $this->options['basedn']; + + $func_params = array($this->conn_id, $search_basedn, $filter, + array($this->options['memberattr'])); + $func_name = $this->_scope2function($this->options['groupscope']); + + $this->_debug("Searching with $func_name and filter $filter in $search_basedn", __LINE__); + + // search + if (($result_id = @call_user_func_array($func_name, $func_params)) != false) { + if (@ldap_count_entries($this->conn_id, $result_id) == 1) { + @ldap_free_result($result_id); + $this->_debug('User is member of group', __LINE__); + $this->_disconnect(); + return true; + } + } + // default + $this->_debug('User is NOT member of group', __LINE__); + $this->_disconnect(); + return false; + } + + /** + * Outputs debugging messages + * + * @access private + * @param string Debugging Message + * @param integer Line number + */ + function _debug($msg = '', $line = 0) + { + if ($this->options['debug'] === true) { + if ($msg == '' && $this->_isValidLink()) { + $msg = 'LDAP_Error: ' . @ldap_err2str(@ldap_errno($this->_conn_id)); + } + print("$line: $msg
"); + } + } +} + +?> \ No newline at end of file diff --git a/htdocs/includes/pear/DB/ldap2.php b/htdocs/includes/pear/DB/ldap2.php new file mode 100644 index 00000000000..12dcbe6ebbd --- /dev/null +++ b/htdocs/includes/pear/DB/ldap2.php @@ -0,0 +1,1245 @@ + +// +// Based on ldap.php +// Copyright (C) 2002 Ludovico Magnocavallo +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// $Id$ +// + +require_once DOL_DOCUMENT_ROOT."/includes/pear/DB/common.php"; + +/** + * LDAP2 DB interface class + * + * DB_ldap2 extends DB_common to provide DB compliant + * access to LDAP servers with protocol version 2. + * + * @author Piotr Roszatycki + * @version $Revision$ + * @package DB_ldap2 + */ + +class DB_ldap2 extends DB_common +{ + // {{{ properties + + /** + * LDAP connection handler + * @access private + */ + var $connection; + + /** + * list of actions which manipulate data + * @access private + */ + var $action_manip = array( + 'add', 'compare', 'delete', 'modify', 'mod_add', 'mod_del', + 'mod_replace', 'rename'); + + /** + * list of parameters for search actions + * @access private + */ + var $param_search = array( + 'action', 'base_dn', 'attributes', 'attrsonly', 'sizelimit', + 'timelimit', 'deref','sort'); + + /** + * list of parameters for modify actions + * @access private + */ + var $param_modify = array( + 'action', 'attribute', 'value', 'newrdn', 'newparent', + 'deleteoldrdn'); + + /** + * default parameters for query + * @access private + */ + var $param = array(); + + /** + * parameters for last query + * @access private + */ + var $last_param = array(); + + /** + * array contained row counters for last query + * @access private + */ + var $row = array(); + + /** + * array contained number of rows for last query + * @access private + */ + var $num_rows = array(); + + /** + * array contained entry handlers for last query + * @access private + */ + var $entry = array(); + + /** + * array contained number of rows affected by last query + * @access private + */ + var $affected = 0; + + // }}} + // {{{ constructor + + /** + * Constructor, calls DB_common constructor + * + * @see DB_common::DB_common() + */ + function DB_ldap2() + { + $this->DB_common(); + $this->phptype = 'ldap2'; + $this->dbsyntax = 'ldap2'; + $this->features = array( + 'prepare' => false, + 'pconnect' => false, + 'transactions' => false, + 'limit' => false + ); + $this->errorcode_map = array( + 0x10 => DB_ERROR_NOSUCHFIELD, // LDAP_NO_SUCH_ATTRIBUTE + 0x11 => DB_ERROR_NOSUCHFIELD, // LDAP_UNDEFINED_TYPE + 0x12 => DB_ERROR_CONSTRAINT, // LDAP_INAPPROPRIATE_MATCHING + 0x13 => DB_ERROR_CONSTRAINT, // LDAP_CONSTRAINT_VIOLATION + 0x14 => DB_ERROR_ALREADY_EXISTS, // LDAP_TYPE_OR_VALUE_EXISTS + 0x15 => DB_ERROR_INVALID, // LDAP_INVALID_SYNTAX + 0x20 => DB_ERROR_NOSUCHTABLE, // LDAP_NO_SUCH_OBJECT + 0x21 => DB_ERROR_NOSUCHTABLE, // LDAP_ALIAS_PROBLEM + 0x22 => DB_ERROR_INVALID, // LDAP_INVALID_DN_SYNTAX + 0x23 => DB_ERROR_INVALID, // LDAP_IS_LEAF + 0x24 => DB_ERROR_INVALID, // LDAP_ALIAS_DEREF_PROBLEM + 0x30 => DB_ERROR_ACCESS_VIOLATION, // LDAP_INAPPROPRIATE_AUTH + 0x31 => DB_ERROR_ACCESS_VIOLATION, // LDAP_INVALID_CREDENTIALS + 0x32 => DB_ERROR_ACCESS_VIOLATION, // LDAP_INSUFFICIENT_ACCESS + 0x40 => DB_ERROR_MISMATCH, // LDAP_NAMING_VIOLATION + 0x41 => DB_ERROR_CONSTRAINT, // LDAP_OBJECT_CLASS_VIOLATION + 0x44 => DB_ERROR_ALREADY_EXISTS, // LDAP_ALREADY_EXISTS + 0x51 => DB_ERROR_CONNECT_FAILED, // LDAP_SERVER_DOWN + 0x57 => DB_ERROR_SYNTAX // LDAP_FILTER_ERROR + ); + } + + // }}} + // {{{ connect() + + /** + * Connect and bind to LDAPv2 server with either anonymous + * or authenticated bind depending on dsn info + * + * The format of the supplied DSN: + * + * ldap2://binddn:bindpw@host:port/basedn + * + * I.e.: + * + * ldap2://uid=dexter,ou=People,dc=example,dc=net:secret@127.0.0.1/dc=example,dc=net + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param boolean $persistent kept for interface compatibility + * @return int DB_OK if successfully connected. + * A DB error code is returned on failure. + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('ldap')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsninfo; + $type = $dsninfo['phptype']; + $user = $dsninfo['username']; + $pw = $dsninfo['password']; + $host = $dsninfo['hostspec']; + $port = empty($dsninfo['port']) ? 389 : $dsninfo['port']; + + $this->param = array( + 'action' => 'search', + 'base_dn' => $this->base_dn = $dsninfo['database'], + 'attributes' => array(), + 'attrsonly' => 0, + 'sizelimit' => 0, + 'timelimit' => 0, + 'deref' => LDAP_DEREF_NEVER, + 'attribute' => '', + 'value' => '', + 'newrdn' => '', + 'newparent' => '', + 'deleteoldrdn'=>false, + 'sort' => '' + ); + $this->last_param = $this->param; + $this->setOption("seqname_format", "sn=%s," . $dsninfo['database']); + $this->fetchmode = DB_FETCHMODE_ASSOC; + + if ($host) { + $conn = @ldap_connect($host, $port); + } else { + return $this->raiseError("unknown host $host"); + } + if (!$conn) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } + if ($user && $pw) { + $bind = @ldap_bind($conn, $user, $pw); + } else { + $bind = @ldap_bind($conn); + } + if (!$bind) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Unbinds from LDAP server + * + * @return int ldap_unbind() return value + */ + function disconnect() + { + $ret = @ldap_unbind($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Performs a request against the LDAP server + * + * The type of request depend on $query parameter. If $query is string, + * perform simple searching query with filter in $query parameter. + * If $query is array, the first element of array is filter string + * (for reading operations) or data array (for writing operations). + * Another elements of $query array are query parameters which overrides + * the default parameters. + * + * The following parameters can be passed for search queries:
+ *
  • base_dn + *
  • attributes - array, the attributes that shall be returned + *
  • attrsonly + *
  • sizelimit - integer, the max number of results to be returned + *
  • timelimit - integer, the timelimit after which to stop searching + *
  • deref - + *
  • sort - string, which tells the attribute name by which to sort + * + * + * I.e.: + * + * // search queries + * // 'base_dn' is not given, so the one passed to connect() will be used + * $db->simpleQuery("uid=dexter"); + * + * // base_dn is given + * // the 'attributes' key defines the attributes that shall be returned + * // 'sort' defines the sort order of the data + * $db->simpleQuery(array( + * 'uid=dexter', + * 'base_dn' => 'ou=People,dc=example,dc=net', + * 'attributes'=>array('dn','o','l'), + * 'sort'=>'o' + * )); + * + * // use this kind of query for adding data + * $db->simpleQuery( + * array( + * array( + * 'dn' => 'cn=Piotr Roszatycki,dc=example,dc=com', + * 'objectClass' => array('top', 'person'), + * 'cn' => 'Piotr Roszatycki', + * 'sn' => 'Roszatycki'), + * 'action' => 'add' + * )); + * + * @param mixed $query the ldap query + * @return int result from LDAP function for failure queries, + * DB_OK for successful queries or DB Error object if wrong syntax + */ + function simpleQuery( $query) + { + if (is_array($query)) { + $last_param = $query; + $query = (isset($query[0]) ? $query[0] : 'objectClass=*'); + unset($last_param[0]); + } else { + $last_param = array(); + } + $action = (isset($last_param['action']) ? $last_param['action'] : $this->param['action']); + // check if the given action is a valid modifier action, i.e. 'search' + if (!$this->isManip($action)) { + $this->last_param = $this->param; + foreach($this->param_search as $k) { + if (isset($last_param[$k])) { + $this->last_param[$k] = $last_param[$k]; + } + } + extract($this->last_param); + // double escape char for filter: '(o=Przedsi\C4\99biorstwo)' => '(o=Przedsi\\C4\\99biorstwo)' + $this->last_query = $query; + $filter = str_replace('\\', '\\\\', $query); + switch ($action) { + // ldap_search, *list, *read have the same arguments + case 'search': + case 'list': + case 'read': + $ldap_action = "ldap_$action"; + $result = @$ldap_action($this->connection, $base_dn, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref); + break; + default: + return $this->ldapRaiseError(DB_ERROR_SYNTAX); + } + if (!$result) { + return $this->ldapRaiseError(); + } + $this->row[$result] = 0; // reset the row counter. + $numrows = $this->numrows($result); + if (is_object($numrows)) { + return $numrows; + } + $this->num_rows[$result] = $numrows; + $this->affected = 0; + if ($sort) { + ldap_sort($this->connection,$result,$sort); + } + return $result; + } else { + // If first argument is an array, it contains the entry with DN. + if (is_array($query)) { + $entry = $query; + $dn = $entry['dn']; + unset($entry['dn']); + } else { + $entry = array(); + $dn = $query; + } + $this->last_param = $this->param; + foreach($this->param_modify as $k) { + if (isset($last_param[$k])) { + $this->last_param[$k] = $last_param[$k]; + } + } + extract($this->last_param); + $this->last_query = $query; + switch ($action) { + case 'add': + case 'modify': + case 'mod_add': + case 'mod_del': + case 'mod_replace': + $ldap_action = "ldap_$action"; + $result = @$ldap_action($this->connection, $dn, $entry); + break; + case 'compare': + $result = @ldap_compare($this->connection, $dn, $attribute, $value); + break; + case 'delete': + $result = @ldap_delete($this->connection, $dn); + break; + case 'rename': + $result = @ldap_rename($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn); + break; + default: + return $this->ldapRaiseError(DB_ERROR_SYNTAX); + } + if (!$result) { + return $this->ldapRaiseError(); + } + $this->affected = 1; + return DB_OK; + } + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal ldap result pointer to the next available result + * + * @param a valid ldap result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @ldap_next_entry($result); + } + + // }}} + // {{{ fetchRow() + + /** + * Fetch and return a row of data (it uses fetchInto for that) + * @param $result LDAP result identifier + * @param $fetchmode format of fetched row array + * @param $rownum the absolute row number to fetch + * + * @return array a row of data, or false on error + */ + function fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null) + { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + $res = $this->fetchInto($result, $arr, $fetchmode, $rownum); + if ($res !== DB_OK) { + return $res; + } + return $arr; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row and insert the data into an existing array. + * + * DB_FETCHMODE_ORDERED returns a flat array of values + * ("value", "val1", "val2"). + * + * DB_FETCHMODE_ASSOC returns an array of structuralized data + * ("field_name1" => "value", "field_name2" => array("val1", "val2")). + * + * @param $result PostgreSQL result identifier + * @param $arr (reference) array where data from the row is stored + * @param $fetchmode how the array data should be indexed + * @param $rownum the row number to fetch + * + * @return int DB_OK on success, a DB error code on failure + */ + function fetchInto($result, &$arr, $fetchmode, $rownum=null) + { + if ($rownum !== null) { + // $rownum is unimplemented, yet + return null; + } + $rownum = $this->row[$result]; + if ($rownum >= $this->num_rows[$result]) { + return null; + } + if ($rownum == 0) { + $entry = @ldap_first_entry($this->connection, $result); + } else { + $entry = @ldap_next_entry($this->connection, $this->entry[$result]); + } + $this->entry[$result] = $entry; + if (!$entry) { + $errno = ldap_errno($this->connection); + if (!$err) { + return null; + } + return $this->ldapRaiseError(); + } + + switch ($fetchmode) { + case DB_FETCHMODE_ORDERED: + $arr = array(); + if (!($attr = @ldap_get_attributes($this->connection, $entry))) { + $errno = ldap_errno($this->connection); + if (!$err) { + return null; + } + return $this->ldapRaiseError(); + } + if ($attr["count"] == 0) { + if (!($arr[] = @ldap_get_dn($this->connection, $entry))) { + $errno = ldap_errno($this->connection); + if (!$err) { + return null; + } + return $this->ldapRaiseError(); + } + } else { + while (list($attr_name, $attr_val) = each($attr)) { + if ($attr_val["count"] == 1) { + $arr[] = $attr_val[0]; + } elseif ($attr_val["count"] > 1) { + for ($i=0; $i<$attr_val["count"]; $i++) { + $arr[] = $attr_val[$i]; + } + } + } + } + break; + case DB_FETCHMODE_ASSOC: + $arr = array(); + if (!($arr["dn"] = @ldap_get_dn($this->connection, $entry))) { + $errno = ldap_errno($this->connection); + if (!$err) { + return null; + } + return $this->ldapRaiseError(); + } + if (!($attr = @ldap_get_attributes($this->connection, $entry))) { + $errno = ldap_errno($this->connection); + if (!$err) { + return null; + } + return $this->ldapRaiseError(); + } + while (list($attr_name, $attr_val) = each($attr)) { + if ($attr_val["count"] == 1) { + $arr[strtolower($attr_name)] = $attr_val[0]; + } elseif ($attr_val["count"] > 1) { + for ($i=0; $i<$attr_val["count"]; $i++) { + $arr[strtolower($attr_name)][$i] = $attr_val[$i]; + } + } + } + break; + } + + $this->row[$result] = ++$rownum; + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Free the internal resources associated with $result. + * + * @param $result int LDAP result identifier or DB statement identifier + * + * @return bool TRUE on success, FALSE if $result is invalid + */ + function freeResult($result) + { + if (is_resource($result)) { + return @ldap_free_result($result); + } + if (!isset($this->prepare_tokens[(int)$result])) { + return false; + } + unset($this->prepare_tokens[(int)$result]); + unset($this->prepare_types[(int)$result]); + unset($this->prepared_queries[(int)$result]); + unset($this->row[(int)$result]); + unset($this->num_rows[(int)$result]); + unset($this->entry[(int)$result]); + $this->affected = 0; + $this->last_param = $this->param; + $this->attributes = null; + $this->sorting = ''; + return true; + } + + // }}} + // {{{ quote() + + /** + * Quote the given string so it can be safely used within string delimiters + * in a query. + * + * @param $string mixed Data to be quoted + * + * @return mixed "NULL" string, quoted string or original data + */ + function quote($str = null) + { + $str = str_replace(array('\\', '"'), array('\\\\', '\\"'), $str); + return $str; + } + + // }}} + // {{{ numCols() + + /** + * Get the number of columns in a result set. This function + * is used only for compatibility reasons. + * + * @param $result resource LDAP result identifier + * + * @return int DB_ERROR_NOT_CAPABLE error code + */ + function numCols($result) + { + return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set. + * + * @param $result resource LDAP result identifier + * + * @return int the number of rows in $result + */ + function numRows($result) + { + $rows = @ldap_count_entries($this->connection, $result); + if ($rows === null) { + return $this->ldapRaiseError(); + } + return $rows; + } + + // }}} + // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @return int native LDAP error code + */ + function errorNative() + { + return ldap_error($this->connection); + } + + // }}} + // {{{ affectedRows() + + /** + * Gets the number of rows affected by the last query. + * if the last query was a select, returns 0. + * + * @return int number of rows affected by the last query or DB_ERROR + */ + function affectedRows() + { + return $this->affected; + } + + // }}} + // {{{ getTables() + + /** + * @deprecated + */ + function getTables() + { + return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ getListOf() + + /** + * Returns the query needed to get some backend info. This function is + * used only for compatibility reasons. + * + * @return int DB_ERROR_NOT_CAPABLE error code + */ + function getListOf($type) + { + return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ isManip() + + /** + * Tell whether an action is a data manipulation action (add, compare, + * delete, modify, mod_add, mod_del, mod_replace, rename) + * + * @param string $action the query + * + * @return boolean whether $query is a data manipulation action + */ + function isManip($action) + { + return(in_array($action, $this->action_manip)); + } + + // }}} + // {{{ base() + + /** + * @deprecated + */ + function base($base_dn = null) + { + $this->q_base_dn = ($base_dn !== null) ? $base_dn : null; + return true; + } + + // }}} + // {{{ ldapSetBaseDN() + + /** + * @deprecated + */ + function ldapSetBaseDN($base_dn = null) + { + $this->base_dn = ($base_dn !== null) ? $base_dn : $this->d_base_dn; + $this->q_base_dn = ''; + return true; + } + + // }}} + // {{{ ldapSetAction() + + /** + * @deprecated + */ + function ldapSetAction($action = 'search') + { + $this->action = $action; + $this->q_action = ''; + return true; + } + + // }}} + // {{{ nextId() + + /** + * Get the next value in a sequence. + * + * LDAP provides transactions for only one entry and we need to + * prevent race condition. If unique value before and after modify + * aren't equal then wait and try again. + * + * @param string $seq_name the sequence name + * @param bool $ondemand whether to create the sequence on demand + * + * @return a sequence integer, or a DB error + */ + function nextId($seq_name, $ondemand = true) + { + $seq_dn = $this->getSequenceName($seq_name); + $repeat = 0; + do { + // Get the sequence entry + $this->expectError(DB_ERROR_NOSUCHTABLE); + $data = $this->getRow(array('objectClass=*', 'action'=>'read', 'base_dn'=>$seq_dn)); + $this->popExpect(); + + if (DB::isError($data)) { + if ($ondemand && $repeat == 0 + && $data->getCode() == DB_ERROR_NOSUCHTABLE) { + // Try to create sequence and repeat + $repeat = 1; + $data = $this->createSequence($seq_name); + if (DB::isError($data)) { + return $this->ldapRaiseError($data); + } + } else { + // Other error + return $this->ldapRaiseError($data); + } + } else { + // Increment sequence value + $data['cn']++; + // Unique identificator of transaction + $seq_unique = mt_rand(); + $data['uid'] = $seq_unique; + // Modify the LDAP entry + $data = $this->simpleQuery(array($data, 'action'=>'modify')); + if (DB::isError($data)) { + return $this->ldapRaiseError($data); + } + // Get the entry and check if it contains our unique value + $data = $this->getRow(array('objectClass=*', 'action'=>'read', 'base_dn'=>$seq_dn)); + if (DB::isError($data)) { + return $this->ldapRaiseError($data); + } + if ($data['uid'] != $seq_unique) { + // It is not our entry. Wait a little time and repeat + sleep(1); + $repeat = 1; + } else { + $repeat = 0; + } + } + } while ($repeat); + + if (DB::isError($data)) { + return $data; + } + return $data['cn']; + } + + // }}} + // {{{ createSequence() + + /** + * Create the sequence + * + * The sequence entry is based on core schema with extensibleObject, + * so it should work with any LDAP server which doesn't check schema + * or supports extensibleObject object class. + * + * Format of the entry: + * + * dn: $seq_dn + * objectClass: top + * objectClass: extensibleObject + * sn: $seq_id + * cn: $seq_value + * uid: $seq_uniq + * + * @param string $seq_name the sequence name + * + * @return mixed DB_OK on success or DB error on error + */ + function createSequence($seq_name) + { + $seq_dn = $this->getSequenceName($seq_name); + + // Create the sequence entry + $data = array( + 'dn' => $seq_dn, + 'objectclass' => array('top', 'extensibleObject'), + 'sn' => $seq_name, + 'cn' => 0, + 'uid' => 0 + ); + + // Add the LDAP entry + $data = $this->simpleQuery(array($data, 'action'=>'add')); + return $data; + } + + // }}} + // {{{ dropSequence() + + /** + * Drop a sequence + * + * @param string $seq_name the sequence name + * + * @return mixed DB_OK on success or DB error on error + */ + function dropSequence($seq_name) + { + $seq_dn = $this->getSequenceName($seq_name); + + // Delete the sequence entry + $data = array( + 'dn' => $seq_dn, + ); + $data = $this->simpleQuery(array($data, 'action'=>'delete')); + return $data; + } + + // }}} + // {{{ ldapRaiseError() + + /** + * Generate error message for LDAP errors. + * + * @param int $errno error number + * + * @return mixed DB_OK on success or DB error on error + */ + function ldapRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode(ldap_errno($this->connection)); + } + if ($this->last_param['action'] !== null) { + return $this->raiseError($errno, null, null, + sprintf('%s base="%s" filter="%s"', + $this->last_param['action'] ? $this->last_param['action'] : $this->param['action'], + $this->last_param['base_dn'] ? $this->last_param['base_dn'] : $this->param['base_dn'], + is_array($this->last_query) ? "" : $this->last_query + ), + $errno == @ldap_error($this->connection) + ); + } else { + return $this->raiseError($errno, null, null, "???", + @ldap_error($this->connection)); + } + } + + // }}} + // {{{ prepare() + + + /** + * Prepares a query for multiple execution with execute(). + * This behaviour is emulated for LDAP backend. + * prepare() requires a generic query as an array with special + * characters (wildcards) as values. + * + * Types of wildcards: + * ? - a quoted scalar value, i.e. strings, integers + * & - requires a file name, the content of the file + * insert into the query (i.e. saving binary data + * in a db) + * ! - value is inserted 'as is' + * + * Example: + * + * $sth = $dbh->prepare( + * array( + * array( + * 'dn' => '?', + * 'objectClass' => '?', + * 'cn' => '?', + * 'sn' => '?', + * 'description' => '&' + * ), + * 'action' => 'add' + * ); + * ); + * + * $sigfile = "/home/dexter/.signature"; + * $res = $dbh->execute($sth, array( + * 'cn=Piotr Roszatycki,dc=example,dc=com', + * array('top', 'person'), + * 'Piotr Roszatycki', 'Roszatycki', $sigfile + * )); + * + * @param mixed the query to prepare + * + * @return resource handle for the query + * + * @see execute + */ + function prepare($query) + { + if (!is_array($query)) { + return parent::prepare($query); + } elseif (is_array($query) && isset($query[0]) && + !$this->isManip(isset($query['action']) ? $query['action'] : $this->param['action']) + ) { + $filter = $query[0]; + $tokens = split("[\&\?\!]", $filter); + $token = 0; + $types = array(); + + for ($i = 0; $i < strlen($filter); $i++) { + switch ($filter[$i]) { + case '?': + $types[$token++] = DB_PARAM_SCALAR; + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + break; + case '!': + $types[$token++] = DB_PARAM_MISC; + break; + } + } + + $this->prepare_tokens[] = &$tokens; + end($this->prepare_tokens); + + $k = key($this->prepare_tokens); + $this->prepare_types[$k] = $types; + $this->prepared_queries[$k] = &$query; + + return $k; + } elseif(is_array($query) && isset($query[0]) && is_array($query[0])) { + $tokens = array(); + $types = array(); + + foreach ($query[0] as $k=>$v) { + $tokens[$k] = $v; + switch ($v) { + case '?': + $types[$k] = DB_PARAM_SCALAR; + break; + case '&': + $types[$k] = DB_PARAM_OPAQUE; + break; + case '!': + $types[$k] = DB_PARAM_MISC; + break; + default: + $types[$k] = null; + } + } + + $this->prepare_tokens[] = &$tokens; + end($this->prepare_tokens); + + $k = key($this->prepare_tokens); + $this->prepare_types[$k] = $types; + $this->prepared_queries[$k] = &$query; + + return $k; + } else { + return parent::prepare($query); + } + } + + // }}} + // {{{ executeEmulateQuery() + + /** + * Emulates the execute statement. + * + * @param resource $stmt query handle from prepare() + * @param array $data numeric array containing the + * data to insert into the query + * + * @return mixed an array containing the real query run when emulating + * prepare/execute. A DB error code is returned on failure. + * + * @see execute() + */ + function executeEmulateQuery($stmt, $data = false) + { + $query = &$this->prepared_queries[$stmt]; + + if (!is_array($query)) { + return parent::executeEmulateQuery($stmt, $data); + } elseif (is_array($query) && isset($query[0]) && + !$this->isManip(isset($query['action']) ? $query['action'] : $this->param['action']) + ) { + $p = &$this->prepare_tokens; + + if (!isset($this->prepare_tokens[$stmt]) || + !is_array($this->prepare_tokens[$stmt]) || + !sizeof($this->prepare_tokens[$stmt])) + { + return $this->raiseError(DB_ERROR_INVALID); + } + + $qq = &$this->prepare_tokens[$stmt]; + $qp = sizeof($qq) - 1; + + if ((!$data && $qp > 0) || + (!is_array($data) && $qp > 1) || + (is_array($data) && $qp > sizeof($data))) + { + $this->last_query = $this->prepared_queries[$stmt]; + return $this->raiseError(DB_ERROR_NEED_MORE_DATA); + } + + $realquery = $query; + $realquery[0] = $qq[0]; + for ($i = 0; $i < $qp; $i++) { + $type = $this->prepare_types[$stmt][$i]; + if ($type == DB_PARAM_OPAQUE) { + if (is_array($data)) { + $fp = fopen($data[$i], 'r'); + } else { + $fp = fopen($data, 'r'); + } + + $pdata = ''; + + if ($fp) { + while (($buf = fread($fp, 4096)) != false) { + $pdata .= $buf; + } + } + } else { + if (is_array($data)) { + $pdata = &$data[$i]; + } else { + $pdata = &$data; + } + } + + $realquery[0] .= ($type != DB_PARAM_MISC) ? $this->quote($pdata) : $pdata; + $realquery[0] .= $qq[$i + 1]; + } + + return $realquery; + + } elseif(is_array($query) && isset($query[0]) && is_array($query[0])) { + + $p = &$this->prepare_tokens; + + if (!isset($this->prepare_tokens[$stmt]) || + !is_array($this->prepare_tokens[$stmt]) || + !sizeof($this->prepare_tokens[$stmt])) + { + return $this->raiseError(DB_ERROR_INVALID); + } + + $qq = &$this->prepare_tokens[$stmt]; + $realquery = $query; + + $i = 0; + foreach ($qq as $k=>$v) { + $type = $this->prepare_types[$stmt][$k]; + + if ($type !== null) { + + if (!isset($data) || + (is_array($data) && !isset($data[$i])) + ) { + $this->last_query = $this->prepared_queries[$stmt]; + return $this->raiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($type == DB_PARAM_OPAQUE) { + if (is_array($data)) { + $fp = fopen($data[$i++], 'r'); + } else { + $fp = fopen($data, 'r'); + } + + $pdata = ''; + + if ($fp) { + while (($buf = fread($fp, 4096)) != false) { + $pdata .= $buf; + } + } + } elseif ($type !== null) { + if (is_array($data)) { + $pdata = &$data[$i++]; + } else { + $pdata = &$data; + } + } + + $realquery[0][$k] = $pdata; + } + } + + return $realquery; + + } else { + return parent::executeEmulateQuery($stmt, $data); + } + } + + // }}} + // {{{ ldapSetParam() + + /** + * Sets the default parameters for query. + * + * @param string $param the name of parameter for search actions (action, + * base_dn, attributes, attrsonly, sizelimit, timelimit, deref) or + * modify actions (action, attribute, value, newrdn, newparent, + * deleteoldrdn). + * @param string $value the value of parameter + * + * @return mixed DB_OK on success or DB error on error + * + * @see ldapGetParam() + */ + function ldapSetParam($param, $value) + { + if (isset($this->param[$param])) { + $this->param[$param] = $value; + return DB_OK; + } + return $this->raiseError("unknown LDAP parameter $param"); + } + + // }}} + // {{{ ldapGetParam() + + /** + * Gets the default parameters for query. + * + * @param string $param the name of parameter for search or modify + * actions. + * + * @return mixed value of parameter on success or DB error on error + * + * @see ldapSetParam() + */ + function ldapGetParam($param) + { + if (isset($this->param[$param])) { + return $this->param[$param]; + } + return $this->raiseError("unknown LDAP parameter $param"); + } + + // }}} + // {{{ ldapSetOption() + + /** + * Sets the value of the given option. + * + * @param int $option the specified option + * @param mixed $newval the value of specified option + * + * @return bool DB_OK on success or DB error on error + * + * @see ldapGetOption() + */ + function ldapSetOption($option, $newval) + { + if (@ldap_set_option($this->connection, $option, $newval)) { + return DB_OK; + } + return $this->raiseError("failed to set LDAP option"); + } + + // }}} + // {{{ ldapGetOption() + + /** + * Gets the current value for given option. + * + * @param int $option the specified option + * @param mixed $retval (reference) the new value of specified option + * + * @return bool DB_OK on success or DB error on error + * + * @see ldapSetOption() + */ + function ldapGetOption($option, &$retval) + { + if (@ldap_get_option($this->connection, $option, $retval)) { + return DB_OK; + } + return $this->raiseError("failed to get LDAP option"); + } + + // }}} + // {{{ ldapExplodeDN() + + /** + * Splits the DN and breaks it up into its component parts. + * Each part is known as Relative Distinguished Name, or RDN. + * + * @param string $dn the DN to split + * @param int $with_attrib 0 to get RDNs with the attributes + * or 1 to get only values. + * + * @return array an array of all those components + */ + function ldapExplodeDN($dn, $with_attrib = 0) + { + $arr = ldap_explode_dn($dn, $with_attrib ? 1 : 0); + unset($arr['count']); + return $arr; + } + +} + +?> diff --git a/htdocs/includes/pear/DB/ldap3.php b/htdocs/includes/pear/DB/ldap3.php new file mode 100644 index 00000000000..4bad4c75129 --- /dev/null +++ b/htdocs/includes/pear/DB/ldap3.php @@ -0,0 +1,118 @@ + +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// $Id$ +// + +require_once DOL_DOCUMENT_ROOT."/includes/pear/DB/common.php"; +require_once DOL_DOCUMENT_ROOT."/includes/pear/DB/ldap2.php"; + +/** + * LDAP3 DB interface class + * + * DB_ldap3 extends DB_ldap2 to provide DB compliant + * access to LDAP servers with protocol version 3. + * + * @author Piotr Roszatycki + * @version $Revision$ + * @package DB_ldap3 + */ + +class DB_ldap3 extends DB_ldap2 +{ + // {{{ connect() + + /** + * Connect and bind to LDAPv3 server with either anonymous + * or authenticated bind depending on dsn info + * + * The format of the supplied DSN: + * + * ldap3://binddn:bindpw@host:port/basedn + * + * I.e.: + * + * ldap3://uid=dexter,ou=People,dc=example,dc=net:secret@127.0.0.1/dc=example,dc=net + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param boolean $persistent kept for interface compatibility + * @return int DB_OK if successfully connected. + * A DB error code is returned on failure. + */ + function connect($dsninfo, $persistent = false) + { + if (!DB::assertExtension('ldap')) + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + + $this->dsn = $dsninfo; + $type = $dsninfo['phptype']; + $user = $dsninfo['username']; + $pw = $dsninfo['password']; + $host = $dsninfo['hostspec']; + $port = empty($dsninfo['port']) ? 389 : $dsninfo['port']; + + $this->param = array( + 'action' => 'search', + 'base_dn' => $this->base_dn = $dsninfo['database'], + 'attributes' => array(), + 'attrsonly' => 0, + 'sizelimit' => 0, + 'timelimit' => 0, + 'deref' => LDAP_DEREF_NEVER, + 'attribute' => '', + 'value' => '', + 'newrdn' => '', + 'newparent' => '', + 'deleteoldrdn'=>false, + 'sort' => '', + ); + $this->last_param = $this->param; + $this->setOption("seqname_format", "sn=%s," . $dsninfo['database']); + $this->fetchmode = DB_FETCHMODE_ASSOC; + + if ($host) { + $conn = @ldap_connect($host, $port); + } else { + return $this->raiseError("unknown host $host"); + } + if (!$conn) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } + if (!@ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3)) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } + if ($user && $pw) { + $bind = @ldap_bind($conn, $user, $pw); + } else { + $bind = @ldap_bind($conn); + } + if (!$bind) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED); + } + $this->connection = $conn; + return DB_OK; + } + + // }}} + +} + +?> diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php index 3640686be17..1ddc705ce55 100644 --- a/htdocs/main.inc.php +++ b/htdocs/main.inc.php @@ -105,6 +105,7 @@ elseif (! empty($_SERVER["REMOTE_USER"])) $_SESSION["dol_user"]=$user; } } + // MODE 3: Identification depuis base de donnees else { @@ -152,7 +153,69 @@ else exit; } } +/* +// MODE 4: Identification depuis ldap +else +{ + if($conf->ldap->enabled) + { + // Authentification Apache KO ou non active, pas de mode force on demande le login + require_once(DOL_DOCUMENT_ROOT."/includes/pear/Auth/Auth.php"); + //if ($conf->global->LDAP_SERVER_PROTOCOLVERSION == 3) + //{ + $ldap = 'ldap://'.$conf->global->LDAP_ADMIN_DN.':'.$conf->global->LDAP_ADMIN_PASS.'@'.$conf->global->LDAP_SERVER_HOST.':'.$conf->global->LDAP_SERVER_PORT.'/'.$conf->global->LDAP_SERVER_DN; + //} + //else + //{ + // $ldap = 'ldap2://'.$conf->global->LDAP_ADMIN_DN.':'.$conf->global->LDAP_ADMIN_PASS.'@'.$conf->global->LDAP_SERVER_HOST.':'.$conf->global->LDAP_SERVER_PORT.'/'.$conf->global->LDAP_SERVER_DN; + //} + + $params = array( + 'dsn' => $ldap, + 'host' => $conf->global->LDAP_SERVER_HOST, + 'port' => $conf->global->LDAP_SERVER_PORT, + 'version' => $conf->global->LDAP_SERVER_PORT, + 'basedn' => $conf->global->LDAP_SERVER_DN, + 'binddn' => $conf->global->LDAP_ADMIN_DN, + 'bindpw' => $conf->global->LDAP_ADMIN_PASS, + 'userattr' => $conf->global->LDAP_FIELD_LOGIN_SAMBA, + 'userfilter' => '(objectClass=user)', + ); + + $aDol = new DOLIAuth("DB", $params, "loginfunction"); + $aDol->setSessionName("DOLSESSID_".$dolibarr_main_db_name); + $aDol->start(); + $result = $aDol->getAuth(); + if ($result) + { + // Authentification Auth OK, on va chercher les infos du user + $user->fetch($aDol->getUsername()); + dolibarr_syslog ("Authentification ok (en mode Pear)"); + if (isset($_POST["loginfunction"])) + { + // Si phase de login initial + $user->update_last_login_date(); + } + } + else + { + if (isset($_POST["loginfunction"])) + { + // Echec authentification + dolibarr_syslog("Authentification ko (en mode Pear) pour '".$_POST["username"]."'"); + } + else + { + // Non authentifie dolibarr_syslog("Authentification non realise; + } + // Le debut de la page a ete affiche par loginfunction. On ferme juste la page + print "\n\n\n"; + exit; + } + } +} +*/ // Si user admin, on force droits sur les modules base if ($user->admin) {