2013-07-10 12:59:46 +00:00
< ? php defined ( 'SYSPATH' ) or die ( 'No direct access allowed.' );
/**
* This class takes care of communicating with LDAP
*
2014-02-11 00:26:11 +00:00
* @ package Kohana / Database
* @ category Drivers
2013-07-10 12:59:46 +00:00
* @ author Deon George
2013-07-12 00:35:54 +00:00
* @ copyright ( c ) 2013 phpLDAPadmin Development Team
2013-07-10 12:59:46 +00:00
* @ license http :// dev . phpldapadmin . org / license . html
*/
2013-07-12 00:35:54 +00:00
abstract class Kohana_Database_LDAP extends Kohana_LDAP {
// LDAP doesnt use an identifier
protected $_identifier = '' ;
2013-07-10 12:59:46 +00:00
2013-07-12 00:35:54 +00:00
/**
* @ 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 ;}
2013-07-10 12:59:46 +00:00
2013-08-16 02:21:17 +00:00
/**
* @ override We provide the columns that are in all LDAP objects
*/
public function list_columns ( $table , $like = NULL , $add_prefix = TRUE ) {
2014-08-22 13:37:13 +00:00
return array ( 'dn' => array ( 'data_type' => NULL ), 'objectclass' => array ( 'data_type' => NULL ));
2013-08-16 02:21:17 +00:00
}
2013-07-10 12:59:46 +00:00
/**
2013-07-12 00:35:54 +00:00
* @ override We override this parent function , since LDAP doesnt quote columns
2013-07-10 12:59:46 +00:00
*/
public function quote_column ( $column ) {
return $column ;
}
2013-07-12 00:35:54 +00:00
/** LDAP **/
2013-07-10 12:59:46 +00:00
/**
2013-07-12 00:35:54 +00:00
* Bind to the LDAP server with the creditials
*
* If we are successful , we return TRUE , if not FALSE
*
* @ return boolean TRUE | FALSE
2013-07-10 12:59:46 +00:00
*/
private function _bind ( $u , $p ) {
2014-07-18 02:35:27 +00:00
Log :: instance () -> add ( LOG_NOTICE , ':instance :method BINDing, Username :user, Pass :pass' , array ( ':instance' => $this -> _instance , ':method' => __METHOD__ , ':user' => $u , ':pass' => md5 ( $p )));
2013-07-10 12:59:46 +00:00
/*
// @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 )
2015-01-09 02:58:11 +00:00
$benchmark = Profiler :: start ( " Database LDAP ( { $this -> _instance } ) " , __METHOD__ );
2013-07-10 12:59:46 +00:00
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 ;
2014-07-18 02:35:27 +00:00
Log :: instance () -> add ( LOG_NOTICE , ':instance :method BOUND, Username :user, Pass :pass' , array ( ':instance' => $this -> _instance , ':method' => __METHOD__ , ':user' => $u , ':pass' => md5 ( $p )));
2013-07-10 12:59:46 +00:00
$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
*
2013-07-12 00:35:54 +00:00
* 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
2013-07-10 12:59:46 +00:00
*/
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 ();
2014-07-18 02:35:27 +00:00
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' ]));
2013-07-10 12:59:46 +00:00
// Do we need to do an search to find the DN
if ( ! empty ( $this -> _config [ 'login_attr' ]) AND strtoupper ( $this -> _config [ 'login_attr' ]) != 'DN' ) {
2014-07-18 02:35:27 +00:00
Log :: instance () -> add ( LOG_NOTICE , ':instance :method BINDing, Searching for DN' , array ( ':instance' => $this -> _instance , ':method' => __METHOD__ ));
2013-07-10 12:59:46 +00:00
// 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' );
2013-07-12 00:35:54 +00:00
$config = Arr :: merge ( $this -> _config , array (
2013-07-10 12:59:46 +00:00
'login_attr' => 'DN' ,
'connection' => array (
'hostname' => $hostname ,
'port' => $port ,
),
2013-07-12 00:35:54 +00:00
));
2013-07-10 12:59:46 +00:00
try {
2014-07-18 02:35:27 +00:00
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 )));
2013-07-12 00:35:54 +00:00
$x = LDAP :: factory ( 'auth' , NULL , $config );
2013-07-10 12:59:46 +00:00
// 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 )
2014-07-18 02:35:27 +00:00
-> execute ( NULL , 'Model_LDAP' );
2013-07-10 12:59:46 +00:00
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 ();
}
2013-08-16 02:21:17 +00:00
foreach ( $u as $dn => $leaf )
if ( $this -> _bind ( $dn , $pass ))
2015-01-09 02:58:11 +00:00
return ORM :: factory ( 'LDAP' , $dn );
2013-07-10 12:59:46 +00:00
// 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' => '' ,
));
2014-07-18 02:35:27 +00:00
Log :: instance () -> add ( LOG_NOTICE , ':instance :method CONNECT:- host :hostname, port :port' , array ( ':instance' => $this -> _instance , ':method' => __METHOD__ , ':hostname' => $hostname , ':port' => $port ));
2013-07-10 12:59:46 +00:00
/*
// @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 )
2015-01-09 02:58:11 +00:00
$benchmark = Profiler :: start ( " Database LDAP ( { $this -> _instance } ) " , __METHOD__ );
2013-07-10 12:59:46 +00:00
$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 );
2014-07-18 02:35:27 +00:00
Log :: instance () -> add ( LOG_NOTICE , ':instance :method connectED' , array ( ':instance' => $this -> _instance , ':method' => __METHOD__ ));
2013-07-10 12:59:46 +00:00
$this -> _connection = $r ;
}
2013-07-12 00:35:54 +00:00
public function disconnect () {
try {
// Database is assumed disconnected
$status = TRUE ;
2013-07-10 12:59:46 +00:00
2013-07-12 00:35:54 +00:00
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 );
2013-07-10 12:59:46 +00:00
}
2013-07-12 00:35:54 +00:00
return $status ;
2013-07-10 12:59:46 +00:00
}
2014-02-13 05:07:03 +00:00
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' ;
2015-01-09 02:58:11 +00:00
$x = LDAP :: factory ( 'schema' , NULL , $this -> _config );
2014-02-13 05:07:03 +00:00
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' )
2015-01-09 02:58:11 +00:00
// @todo: This should be a config item
-> cached ( 86400 )
2014-02-13 05:07:03 +00:00
-> 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 ();
}
}
2013-07-10 12:59:46 +00:00
public function search ( $base = array ()) {
2013-07-12 00:35:54 +00:00
return new Database_LDAP_Search ( $this , $base );
2013-07-10 12:59:46 +00:00
}
}
?>