323 lines
10 KiB
PHP
323 lines
10 KiB
PHP
<?php defined('SYSPATH') or die('No direct access allowed.');
|
|
|
|
/**
|
|
* This class takes care of communicating with LDAP
|
|
*
|
|
* @package Kohana/Database
|
|
* @category Drivers
|
|
* @author Deon George
|
|
* @copyright (c) 2013 phpLDAPadmin Development Team
|
|
* @license http://dev.phpldapadmin.org/license.html
|
|
*/
|
|
abstract class Kohana_Database_LDAP extends Kohana_LDAP {
|
|
// LDAP doesnt use an identifier
|
|
protected $_identifier = '';
|
|
|
|
/**
|
|
* @defunct This required abstruct function is defunct for LDAP
|
|
*/
|
|
public function begin($mode = NULL) { throw HTTP_Exception::factory(501,'We shouldnt be here: :method',array(':method'=>__METHOD__)); }
|
|
/**
|
|
* @defunct This required abstruct function is defunct for LDAP
|
|
*/
|
|
public function commit() { throw HTTP_Exception::factory(501,'We shouldnt be here: :method',array(':method'=>__METHOD__)); }
|
|
/**
|
|
* @defunct This required abstruct function is defunct for LDAP
|
|
*/
|
|
public function list_tables($like = NULL) { throw HTTP_Exception::factory(501,'We shouldnt be here: :method',array(':method'=>__METHOD__)); }
|
|
/**
|
|
* @defunct This required abstruct function is defunct for LDAP
|
|
*/
|
|
public function query($type, $sql, $as_object = FALSE, array $params = NULL) { throw HTTP_Exception::factory(501,'We shouldnt be here: :method',array(':method'=>__METHOD__)); }
|
|
/**
|
|
* @defunct This required abstruct function is defunct for LDAP
|
|
*/
|
|
public function rollback() { throw HTTP_Exception::factory(501,'We shouldnt be here: :method',array(':method'=>__METHOD__)); }
|
|
/**
|
|
* @defunct This required abstruct function is defunct for LDAP
|
|
*/
|
|
public function set_charset($charset) { throw HTTP_Exception::factory(501,'We shouldnt be here: :method',array(':method'=>__METHOD__)); }
|
|
|
|
/** REQUIRED ABSTRACT FUNCTIONS **/
|
|
public function escape($value) { return $value;}
|
|
|
|
/**
|
|
* @override We provide the columns that are in all LDAP objects
|
|
*/
|
|
public function list_columns($table,$like=NULL,$add_prefix=TRUE) {
|
|
return array('dn'=>array('data_type'=>NULL),'objectclass'=>array('data_type'=>NULL));
|
|
}
|
|
|
|
/**
|
|
* @override We override this parent function, since LDAP doesnt quote columns
|
|
*/
|
|
public function quote_column($column) {
|
|
return $column;
|
|
}
|
|
|
|
/** LDAP **/
|
|
|
|
/**
|
|
* Bind to the LDAP server with the creditials
|
|
*
|
|
* If we are successful, we return TRUE, if not FALSE
|
|
*
|
|
* @return boolean TRUE|FALSE
|
|
*/
|
|
private function _bind($u,$p) {
|
|
Log::instance()->add(LOG_NOTICE,':instance :method BINDing, Username :user, Pass :pass',array(':instance'=>$this->_instance,':method'=>__METHOD__,':user'=>$u,':pass'=>md5($p)));
|
|
|
|
/*
|
|
// @todo To implement
|
|
// If SASL has been configured for binding, then start it now.
|
|
if ($this->isSASLEnabled())
|
|
$br = $this->startSASL($this->_r,$method);
|
|
|
|
// Normal bind...
|
|
else
|
|
*/
|
|
|
|
if (Kohana::$profiling)
|
|
$benchmark = Profiler::start("Database LDAP ({$this->_instance})",__METHOD__);
|
|
|
|
try {
|
|
$br = ldap_bind($this->_connection,$u,$p);
|
|
|
|
} catch (Exception $e) {
|
|
// This benchmark is worthless
|
|
if (isset($benchmark))
|
|
Profiler::delete($benchmark);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (! $br)
|
|
return FALSE;
|
|
|
|
Log::instance()->add(LOG_NOTICE,':instance :method BOUND, Username :user, Pass :pass',array(':instance'=>$this->_instance,':method'=>__METHOD__,':user'=>$u,':pass'=>md5($p)));
|
|
$this->_connected = TRUE;
|
|
|
|
/*
|
|
// @todo To implement
|
|
# If this is a proxy session, we need to switch to the proxy user
|
|
if ($this->isProxyEnabled() && $bind['id'] && $method != 'anon')
|
|
if (! $this->startProxy($this->_r,$method)) {
|
|
$CACHE[$this->index][$method] = null;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// @todo To implement
|
|
if (function_exists('run_hook'))
|
|
run_hook('post_connect',array('server_id'=>$this->index,'method'=>$method,'id'=>$bind['id']));
|
|
*/
|
|
|
|
if (isset($benchmark))
|
|
Profiler::stop($benchmark);
|
|
|
|
return $br;
|
|
}
|
|
|
|
/**
|
|
* Bind to the LDAP server
|
|
*
|
|
* If we have been passed a login_attr that is not DN, we'll try and find the
|
|
* DN to bind with.
|
|
*
|
|
* @param string User attribute to connect with, or blank for anonymous
|
|
* @param string Bind password to use with a DN or blank for anonymous
|
|
* @return mixed $this|FALSE
|
|
*/
|
|
public function bind($user,$pass) {
|
|
// If we are already connected, no need to re-bind.
|
|
if ($this->_connected)
|
|
return $this;
|
|
|
|
// Make sure we are connected.
|
|
$this->_connection OR $this->connect();
|
|
|
|
Log::instance()->add(LOG_NOTICE,':instance :method BINDing, Username :user, Pass :pass, LoginAttr :login_attr',array(':instance'=>$this->_instance,':method'=>__METHOD__,':user'=>$user,':pass'=>md5($pass),':login_attr'=>$this->_config['login_attr']));
|
|
|
|
// Do we need to do an search to find the DN
|
|
if (! empty($this->_config['login_attr']) AND strtoupper($this->_config['login_attr']) != 'DN') {
|
|
Log::instance()->add(LOG_NOTICE,':instance :method BINDing, Searching for DN',array(':instance'=>$this->_instance,':method'=>__METHOD__));
|
|
|
|
// Do we need to authenticate for this search?
|
|
// Extract the connection parameters, adding required variabels
|
|
extract($this->_config['connection'] + array(
|
|
'username' => '',
|
|
'password' => '',
|
|
'hostname' => '',
|
|
'port' => '',
|
|
));
|
|
|
|
// Prevent this information from showing up in traces
|
|
unset($this->_config['connection']['username'], $this->_config['connection']['password']);
|
|
|
|
// Sanity check
|
|
if ($this->_instance == 'auth')
|
|
throw new Kohana_Exception('We shouldnt be authing an auth');
|
|
|
|
$config = Arr::merge($this->_config,array(
|
|
'login_attr'=>'DN',
|
|
'connection'=>array(
|
|
'hostname'=>$hostname,
|
|
'port'=>$port,
|
|
),
|
|
));
|
|
|
|
try {
|
|
Log::instance()->add(LOG_NOTICE,':instance :method AUTH BINDing, Username :user, Pass :pass, LoginAttr :login_attr',array(':instance'=>$this->_instance,':method'=>__METHOD__,':user'=>$username,':pass'=>md5($password)));
|
|
$x = LDAP::factory('auth',NULL,$config);
|
|
|
|
// Our Auth Bind credentials are wrong
|
|
if (! $x->bind($username,$password))
|
|
return FALSE;
|
|
|
|
$u = $x->search(NULL)
|
|
->scope('sub')
|
|
->where($this->_config['login_attr'],'=',$user)
|
|
->execute(NULL,'Model_LDAP');
|
|
|
|
if (! $u)
|
|
return FALSE;
|
|
|
|
} catch (Exception $e) {
|
|
// If we are a command line, we can just print the error
|
|
echo _('Unable to bind to LDAP server with CONFIG settings, please check them.');
|
|
echo _('The error message is').': '.$e->getMessage();
|
|
die();
|
|
}
|
|
|
|
foreach ($u as $dn => $leaf)
|
|
if ($this->_bind($dn,$pass))
|
|
return ORM::factory('LDAP',$dn);
|
|
|
|
// We didnt find an AUTH DN to bind with
|
|
return FALSE;
|
|
}
|
|
|
|
// Bind
|
|
if ($this->_bind($user,$pass))
|
|
return $this;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Create a connection to an LDAP server
|
|
*/
|
|
public function connect() {
|
|
if ($this->_connection)
|
|
return;
|
|
|
|
// Extract the connection parameters, adding required variabels
|
|
extract($this->_config['connection'] + array(
|
|
'hostname' => '',
|
|
'port' => '',
|
|
));
|
|
|
|
Log::instance()->add(LOG_NOTICE,':instance :method CONNECT:- host :hostname, port :port',array(':instance'=>$this->_instance,':method'=>__METHOD__,':hostname'=>$hostname,':port'=>$port));
|
|
|
|
/*
|
|
// @todo To implement
|
|
if (function_exists('run_hook'))
|
|
run_hook('pre_connect',array('server_id'=>$this->index,'method'=>$method));
|
|
*/
|
|
|
|
// Benchmark this connection for the current instance
|
|
if (Kohana::$profiling)
|
|
$benchmark = Profiler::start("Database LDAP ({$this->_instance})",__METHOD__);
|
|
|
|
$r = (! empty($this->_config['port'])) ? ldap_connect($hostname,$port) : ldap_connect($hostname);
|
|
|
|
if (! is_resource($r)) {
|
|
// This benchmark is worthless
|
|
if (isset($benchmark))
|
|
Profiler::delete($benchmark);
|
|
|
|
throw HTTP_Exception::factory(501,'UNHANDLED, $r is not a resource');
|
|
}
|
|
|
|
// Go with LDAP version 3 if possible (needed for renaming and Novell schema fetching)
|
|
ldap_set_option($r,LDAP_OPT_PROTOCOL_VERSION,3);
|
|
|
|
/* Disabling this makes it possible to browse the tree for Active Directory, and seems
|
|
* to not affect other LDAP servers (tested with OpenLDAP) as phpLDAPadmin explicitly
|
|
* specifies deref behavior for each ldap_search operation. */
|
|
ldap_set_option($r,LDAP_OPT_REFERRALS,0);
|
|
|
|
/*
|
|
// @todo To implement
|
|
# Try to fire up TLS is specified in the config
|
|
if ($this->isTLSEnabled())
|
|
$this->startTLS($this->_connection);
|
|
*/
|
|
|
|
if (isset($benchmark))
|
|
Profiler::stop($benchmark);
|
|
|
|
Log::instance()->add(LOG_NOTICE,':instance :method connectED',array(':instance'=>$this->_instance,':method'=>__METHOD__));
|
|
|
|
$this->_connection = $r;
|
|
}
|
|
|
|
public function disconnect() {
|
|
try {
|
|
// Database is assumed disconnected
|
|
$status = TRUE;
|
|
|
|
if (is_resource($this->_connection)) {
|
|
if ($status = ldap_unbind($this->_connection)) {
|
|
// Clear the connection
|
|
$this->_connection = NULL;
|
|
|
|
// Clear the instance
|
|
parent::disconnect();
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
// Database is probably not disconnected
|
|
$status = ! is_resource($this->_connection);
|
|
}
|
|
|
|
return $status;
|
|
}
|
|
|
|
public function getSchema() {
|
|
// Make sure our login_attr is DN
|
|
if ($this->_instance == 'schema' AND $this->_config['login_attr'] != 'DN')
|
|
$this->_config['login_attr'] = 'DN';
|
|
|
|
$x = LDAP::factory('schema',NULL,$this->_config);
|
|
|
|
try {
|
|
// @todo We should bind as specific shema DN, logged in User or anonymous.
|
|
if ($x->bind((isset($this->_config['schema']['dn']) ? $this->_config['schema']['dn'] : FALSE),(isset($this->_config['schema']['password']) ? $this->_config['schema']['password'] : FALSE))) {
|
|
$u = $x->search(array(''))
|
|
->scope('base')
|
|
// @todo: This should be a config item
|
|
->cached(86400)
|
|
->execute();
|
|
|
|
if (! $u OR ! isset($u[''][0]['subschemasubentry'][0]))
|
|
throw new Kohana_Exception('Couldnt find schema?');
|
|
|
|
$x->setSchema(ORM::factory('LDAP_Schema',$u[''][0]['subschemasubentry'][0]));
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
// If we are a command line, we can just print the error
|
|
echo _('Unable to retrieve the SCHEMA from the LDAP server.');
|
|
echo _('The error message is').': '.$e->getMessage();
|
|
die();
|
|
}
|
|
}
|
|
|
|
public function search($base=array()) {
|
|
return new Database_LDAP_Search($this,$base);
|
|
}
|
|
}
|
|
?>
|