'localhost', 'port' => 389, 'version' => 3 ); var $__descriptions = array(); // Lifecycle -------------------------------------------------------------- /** * Constructor */ function __construct($config = null) { $this->debug = Configure :: read() > 0; $this->fullDebug = Configure :: read() > 1; parent::__construct($config); return $this->connect(); } /** * Destructor. Closes connection to the database. * */ function __destruct() { $this->close(); parent :: __destruct(); } // I know this looks funny, and for other data sources this is necessary but for LDAP, we just return the name of the field we're passed as an argument function name( $field ) { return $field; } // Connection -------------------------------------------------------------- function connect() { $config = $this->config; $this->connected = false; $bindDN = (empty($this->myDN)) ? $config['login'] : $this->myDN; $bindPasswd = (empty($this->myPasswd)) ? $config['password'] : $this->myPasswd; if(preg_match('/^ldaps:.*/i', $config['host'])){ $this->database = ldap_connect($config['host']) or $this->log("Failed to connect to LDAP SSL Server:".$config['host'].':'.$config['port'],'ldap.error'); }else{ $this->database = ldap_connect($config['host'], $config['port']) or $this->log("Failed to connect to LDAP Non-SSL Server:".$config['host'].':'.$config['port'],'ldap.error'); } ldap_set_option($this->database, LDAP_OPT_PROTOCOL_VERSION, $config['version']); // From Filipee, to allow the user to specify in the db config to use TLS // 'tls'=> true in config/database.php if ($config['tls']) { if (!ldap_start_tls($this->connection)) { $this->log("Ldap_start_tls failed", 'ldap.error'); fatal_error("Ldap_start_tls failed"); } } if (ldap_bind($this->database, $bindDN, $bindPasswd)) $this->connected = true; return $this->connected; } /** * Test if the dn/passwd combo is valid */ function auth( $dn, $passwd ){ $config = $this->config; if(preg_match('/^ldaps:.*/i', $config['host'])){ $this->database = ldap_connect($config['host']) or $this->log("Failed to connect for Auth to LDAP SSL Server:".$config['host'].':'.$config['port'],'ldap.error'); }else{ $this->database = ldap_connect($config['host'], $config['port']) or $this->log("Failed to connect for Auth to LDAP Non-SSL Server:".$config['host'].':'.$config['port'],'ldap.error'); } ldap_set_option($this->database, LDAP_OPT_PROTOCOL_VERSION, $config['version']); if (@ldap_bind($this->database, $dn, $passwd)){ return true; }else{ $this->log("Auth Error: for '$dn', '$passwd'.".$this->lastError(),'ldap.error'); return $this->lastError(); } } /** * Disconnects database, kills the connection and says the connection is closed, * and if DEBUG is turned on, the log for this object is shown. * */ function close() { if ($this->fullDebug && Configure :: read() > 1) { $this->showLog(); } $this->disconnect(); } function disconnect() { @ldap_free_result($this->results); $this->connected = !@ldap_unbind($this->database); return !$this->connected; } /** * Checks if it's connected to the database * * @return boolean True if the database is connected, else false */ function isConnected() { return $this->connected; } /** * Reconnects to database server with optional new settings * * @param array $config An array defining the new configuration settings * @return boolean True on success, false on failure */ function reconnect($config = null) { $this->disconnect(); if ($config != null) { $this->config = am($this->_baseConfig, $this->config, $config); } return $this->connect(); } // CRUD -------------------------------------------------------------- /** * The "C" in CRUD * * @param Model $model * @param array $fields containing the field names * @param array $values containing the fields' values * @return true on success, false on error */ function create( &$model, $fields = null, $values = null ) { $basedn = $this->config['basedn']; $key = $model->primaryKey; $table = $model->useTable; $fieldsData = array(); $id = null; $objectclasses = null; if ($fields == null) { unset($fields, $values); $fields = array_keys($model->data); $values = array_values($model->data); } $count = count($fields); for ($i = 0; $i < $count; $i++) { if ($fields[$i] == $key) { $id = $values[$i]; }elseif($fields[$i] == 'cn'){ $cn = $values[$i]; } $fieldsData[$fields[$i]] = $values[$i]; } //Lets make our DN, this is made from the useTable & basedn + primary key. Logically this corelate to LDAP if(isset($table) && preg_match('/=/', $table)){ $table = $table.', '; }else{ $table = ''; } if(isset($key) && !empty($key)){ $key = "$key=$id, "; }else{ //Almost everything has a cn, this is a good fall back. $key = "cn=$cn, "; } $dn = $key.$table.$basedn; $res = @ ldap_add( $this->database, $dn, $fieldsData ); // Add the entry if( $res ){ $model->setInsertID($id); $model->id = $id; return true; } else { $this->log("Failed to add ldap entry: dn:$dn\nData:".print_r($fieldsData,true)."\n".ldap_error($this->database),'ldap.error'); $model->onError(); return false; } } /** * Returns the query * */ function query($find, $query = null, $model){ if(isset($query[0]) && is_array($query[0])){ $query = $query[0]; } if(isset($find)){ switch($find){ case 'auth': return $this->auth($query['dn'], $query['password']); case 'findSchema': $query = $this->__getLDAPschema(); //$this->findSchema($query); break; case 'findConfig': return $this->config; break; default: $query = $this->read($model, $query); break; } } return $query; } /** * The "R" in CRUD * * @param Model $model * @param array $queryData * @param integer $recursive Number of levels of association * @return unknown */ function read( &$model, $queryData = array(), $recursive = null ) { $this->model = $model; $this->__scrubQueryData($queryData); if (!is_null($recursive)) { $_recursive = $model->recursive; $model->recursive = $recursive; } // Check if we are doing a 'count' .. this is kinda ugly but i couldn't find a better way to do this, yet if ( is_string( $queryData['fields'] ) && $queryData['fields'] == 'COUNT(*) AS ' . $this->name( 'count' ) ) { $queryData['fields'] = array(); } // Prepare query data ------------------------ $queryData['conditions'] = $this->_conditions( $queryData['conditions'], $model); if(empty($queryData['targetDn'])){ $queryData['targetDn'] = $model->useTable; } $queryData['type'] = 'search'; if (empty($queryData['order'])) $queryData['order'] = array($model->primaryKey); // Associations links -------------------------- foreach ($model->__associations as $type) { foreach ($model->{$type} as $assoc => $assocData) { if ($model->recursive > -1) { $linkModel = & $model->{$assoc}; $linkedModels[] = $type . '/' . $assoc; } } } // Execute search query ------------------------ $res = $this->_executeQuery($queryData ); if ($this->lastNumRows()==0) return false; // Format results ----------------------------- ldap_sort($this->database, $res, $queryData['order'][0]); $resultSet = ldap_get_entries($this->database, $res); $resultSet = $this->_ldapFormat($model, $resultSet); // Query on linked models ---------------------- if ($model->recursive > 0) { foreach ($model->__associations as $type) { foreach ($model->{$type} as $assoc => $assocData) { $db = null; $linkModel = & $model->{$assoc}; if ($model->useDbConfig == $linkModel->useDbConfig) { $db = & $this; } else { $db = & ConnectionManager :: getDataSource($linkModel->useDbConfig); } if (isset ($db) && $db != null) { $stack = array ($assoc); $array = array (); $db->queryAssociation($model, $linkModel, $type, $assoc, $assocData, $array, true, $resultSet, $model->recursive - 1, $stack); unset ($db); } } } } if (!is_null($recursive)) { $model->recursive = $_recursive; } // Add the count field to the resultSet (needed by find() to work out how many entries we got back .. used when $model->exists() is called) $resultSet[0][0]['count'] = $this->lastNumRows(); return $resultSet; } /** * The "U" in CRUD */ function update( &$model, $fields = null, $values = null ) { $fieldsData = array(); if ($fields == null) { unset($fields, $values); $fields = array_keys( $model->data ); $values = array_values( $model->data ); } for ($i = 0; $i < count( $fields ); $i++) { $fieldsData[$fields[$i]] = $values[$i]; } //set our scope $queryData['scope'] = 'base'; if($model->primaryKey == 'dn'){ $queryData['targetDn'] = $model->id; }elseif(isset($model->useTable) && !empty($model->useTable)){ $queryData['targetDn'] = $model->primaryKey.'='.$model->id.', '.$model->useTable; } // fetch the record // Find the user we will update as we need their dn $resultSet = $this->read( $model, $queryData, $model->recursive ); //now we need to find out what's different about the old entry and the new one and only changes those parts $current = $resultSet[0][$model->alias]; $update = $model->data[$model->alias]; foreach( $update as $attr => $value){ if(isset($update[$attr]) && !empty($update[$attr])){ $entry[$attr] = $update[$attr]; }elseif(!empty($current[$attr]) && (isset($update[$attr]) && empty($update[$attr])) ){ $entry[$attr] = array(); } } //if this isn't a password reset, then remove the password field to avoid constraint violations... if(!$this->in_arrayi('userpassword', $update)){ unset($entry['userpassword']); } unset($entry['count']); unset($entry['dn']); if( $resultSet) { $_dn = $resultSet[0][$model->alias]['dn']; if( @ldap_modify( $this->database, $_dn, $entry ) ) { return true; }else{ $this->log("Error updating $_dn: ".ldap_error($this->database)."\nHere is what I sent: ".print_r($entry,true), 'ldap.error'); return false; } } // If we get this far, something went horribly wrong .. $model->onError(); return false; } /** * The "D" in CRUD */ function delete( &$model ) { // Boolean to determine if we want to recursively delete or not //$recursive = true; $recursive = false; if(preg_match('/dn/i', $model->primaryKey)){ $dn = $model->id; }else{ // Find the user we will update as we need their dn if( $model->defaultObjectClass ) { $options['conditions'] = sprintf( '(&(objectclass=%s)(%s=%s))', $model->defaultObjectClass, $model->primaryKey, $model->id ); } else { $options['conditions'] = sprintf( '%s=%s', $model->primaryKey, $model->id ); } $options['targetDn'] = $model->useTable; $options['scope'] = 'sub'; $entry = $this->read( $model, $options, $model->recursive ); $dn = $entry[0][$model->name]['dn']; } if( $dn ) { if( $recursive === true ) { // Recursively delete LDAP entries if( $this->__deleteRecursively( $dn ) ) { return true; } } else { // Single entry delete if( @ldap_delete( $this->database, $dn ) ) { return true; } } } $model->onError(); $errMsg = ldap_error($this->database); $this->log("Failed Trying to delete: $dn \nLdap Erro:$errMsg",'ldap.error'); return false; } /* Courtesy of gabriel at hrz dot uni-marburg dot de @ http://ar.php.net/ldap_delete */ function __deleteRecursively( $_dn ) { // Search for sub entries $subentries = ldap_list( $this->database, $_dn, "objectClass=*", array() ); $info = ldap_get_entries( $this->database, $subentries ); for( $i = 0; $i < $info['count']; $i++ ) { // deleting recursively sub entries $result = $this->__deleteRecursively( $info[$i]['dn'] ); if( !$result ) { return false; } } return( @ldap_delete( $this->database, $_dn ) ); } // Public -------------------------------------------------------------- function generateAssociationQuery(& $model, & $linkModel, $type, $association = null, $assocData = array (), & $queryData, $external = false, & $resultSet) { $this->__scrubQueryData($queryData); switch ($type) { case 'hasOne' : $id = $resultSet[$model->name][$model->primaryKey]; $queryData['conditions'] = trim($assocData['foreignKey']) . '=' . trim($id); $queryData['targetDn'] = $linkModel->useTable; $queryData['type'] = 'search'; $queryData['limit'] = 1; return $queryData; case 'belongsTo' : $id = $resultSet[$model->name][$assocData['foreignKey']]; $queryData['conditions'] = trim($linkModel->primaryKey).'='.trim($id); $queryData['targetDn'] = $linkModel->useTable; $queryData['type'] = 'search'; $queryData['limit'] = 1; return $queryData; case 'hasMany' : $id = $resultSet[$model->name][$model->primaryKey]; $queryData['conditions'] = trim($assocData['foreignKey']) . '=' . trim($id); $queryData['targetDn'] = $linkModel->useTable; $queryData['type'] = 'search'; $queryData['limit'] = $assocData['limit']; return $queryData; case 'hasAndBelongsToMany' : return null; } return null; } function queryAssociation(& $model, & $linkModel, $type, $association, $assocData, & $queryData, $external = false, & $resultSet, $recursive, $stack) { if (!isset ($resultSet) || !is_array($resultSet)) { if (Configure :: read() > 0) { e('
SQL Error in model ' . $model->name . ': '); if (isset ($this->error) && $this->error != null) { e($this->error); } e('
'); } return null; } $count = count($resultSet); for ($i = 0; $i < $count; $i++) { $row = & $resultSet[$i]; $queryData = $this->generateAssociationQuery($model, $linkModel, $type, $association, $assocData, $queryData, $external, $row); $fetch = $this->_executeQuery($queryData); $fetch = ldap_get_entries($this->database, $fetch); $fetch = $this->_ldapFormat($linkModel,$fetch); if (!empty ($fetch) && is_array($fetch)) { if ($recursive > 0) { foreach ($linkModel->__associations as $type1) { foreach ($linkModel-> {$type1 } as $assoc1 => $assocData1) { $deepModel = & $linkModel->{$assocData1['className']}; if ($deepModel->alias != $model->name) { $tmpStack = $stack; $tmpStack[] = $assoc1; if ($linkModel->useDbConfig == $deepModel->useDbConfig) { $db = & $this; } else { $db = & ConnectionManager :: getDataSource($deepModel->useDbConfig); } $queryData = array(); $db->queryAssociation($linkModel, $deepModel, $type1, $assoc1, $assocData1, $queryData, true, $fetch, $recursive -1, $tmpStack); } } } } $this->__mergeAssociation($resultSet[$i], $fetch, $association, $type); } else { $tempArray[0][$association] = false; $this->__mergeAssociation($resultSet[$i], $tempArray, $association, $type); } } } /** * Returns a formatted error message from previous database operation. * * @return string Error message with error number */ function lastError() { if (ldap_errno($this->database)) { return ldap_errno($this->database) . ': ' . ldap_error($this->database); } return null; } /** * Returns number of rows in previous resultset. If no previous resultset exists, * this returns false. * * @return int Number of rows in resultset */ function lastNumRows() { if ($this->_result and is_resource($this->_result)) { return @ ldap_count_entries($this->database, $this->_result); } return null; } // Usefull public (static) functions-------------------------------------------- /** * Convert Active Directory timestamps to unix ones * * @param integer $ad_timestamp Active directory timestamp * @return integer Unix timestamp */ function convertTimestamp_ADToUnix($ad_timestamp) { $epoch_diff = 11644473600; // difference 1601<>1970 in seconds. see reference URL $date_timestamp = $ad_timestamp * 0.0000001; $unix_timestamp = $date_timestamp - $epoch_diff; return $unix_timestamp; }// convertTimestamp_ADToUnix /* The following was kindly "borrowed" from the excellent phpldapadmin project */ function __getLDAPschema() { $schemaTypes = array( 'objectclasses', 'attributetypes' ); $check = @ldap_read($this->database, 'cn=Schema', 'objectClass=*'); if(ldap_count_entries($this->database, $check) > 0){ $schemaDN = 'cn=Schema'; }else{ $schemaDN = 'cn=SubSchema'; } foreach (array('(objectClass=*)','(objectClass=subschema)') as $schema_filter) { $this->results = @ldap_read($this->database, $schemaDN, $schema_filter, $schemaTypes,0,0,0,LDAP_DEREF_ALWAYS); if( is_null( $this->results ) ) { $this->log( "LDAP schema filter $schema_filter is invalid!", 'ldap.error'); continue; } $schema_entries = @ldap_get_entries( $this->database, $this->results ); if ( is_array( $schema_entries ) && isset( $schema_entries['count'] ) ) { break; } unset( $schema_entries ); $schema_search = null; } if( $schema_entries ) { $return = array(); foreach( $schemaTypes as $n ) { $schemaTypeEntries = $schema_entries[0][$n]; for( $x = 0; $x < $schemaTypeEntries['count']; $x++ ) { $entry = array(); $strings = preg_split('/[\s,]+/', $schemaTypeEntries[$x], -1, PREG_SPLIT_DELIM_CAPTURE); $str_count = count( $strings ); for ( $i=0; $i < $str_count; $i++ ) { switch ($strings[$i]) { case '(': break; case 'NAME': if ( $strings[$i+1] != '(' ) { do { $i++; if( !isset( $entry['name'] ) || strlen( $entry['name'] ) == 0 ) $entry['name'] = $strings[$i]; else $entry['name'] .= ' '.$strings[$i]; } while ( !preg_match('/\'$/s', $strings[$i])); } else { $i++; do { $i++; if( !isset( $entry['name'] ) || strlen( $entry['name'] ) == 0) $entry['name'] = $strings[$i]; else $entry['name'] .= ' ' . $strings[$i]; } while ( !preg_match( '/\'$/s', $strings[$i] ) ); do { $i++; } while ( !preg_match( '/\)+\)?/', $strings[$i] ) ); } $entry['name'] = preg_replace('/^\'/', '', $entry['name'] ); $entry['name'] = preg_replace('/\'$/', '', $entry['name'] ); break; case 'DESC': do { $i++; if ( !isset( $entry['description'] ) || strlen( $entry['description'] ) == 0 ) $entry['description'] = $strings[$i]; else $entry['description'] .= ' ' . $strings[$i]; } while ( !preg_match( '/\'$/s', $strings[$i] ) ); break; case 'OBSOLETE': $entry['is_obsolete'] = TRUE; break; case 'SUP': $entry['sup_classes'] = array(); if ( $strings[$i+1] != '(' ) { $i++; array_push( $entry['sup_classes'], preg_replace( "/'/", '', $strings[$i] ) ); } else { $i++; do { $i++; if ( $strings[$i] != '$' ) array_push( $entry['sup_classes'], preg_replace( "/'/", '', $strings[$i] ) ); } while (! preg_match('/\)+\)?/',$strings[$i+1])); } break; case 'ABSTRACT': $entry['type'] = 'abstract'; break; case 'STRUCTURAL': $entry['type'] = 'structural'; break; case 'SINGLE-VALUE': $entry['multiValue'] = 'false'; break; case 'AUXILIARY': $entry['type'] = 'auxiliary'; break; case 'MUST': $entry['must'] = array(); $i = $this->_parse_list(++$i, $strings, $entry['must']); break; case 'MAY': $entry['may'] = array(); $i = $this->_parse_list(++$i, $strings, $entry['may']); break; default: if( preg_match( '/[\d\.]+/i', $strings[$i]) && $i == 1 ) { $entry['oid'] = $strings[$i]; } break; } } if( !isset( $return[$n] ) || !is_array( $return[$n] ) ) { $return[$n] = array(); } //make lowercase for consistency $return[strtolower($n)][strtolower($entry['name'])] = $entry; //array_push( $return[$n][$entry['name']], $entry ); } } } return $return; } function _parse_list( $i, $strings, &$attrs ) { /** ** A list starts with a ( followed by a list of attributes separated by $ terminated by ) ** The first token can therefore be a ( or a (NAME or a (NAME) ** The last token can therefore be a ) or NAME) ** The last token may be terminate by more than one bracket */ $string = $strings[$i]; if (!preg_match('/^\(/',$string)) { // A bareword only - can be terminated by a ) if the last item if (preg_match('/\)+$/',$string)) $string = preg_replace('/\)+$/','',$string); array_push($attrs, $string); } elseif (preg_match('/^\(.*\)$/',$string)) { $string = preg_replace('/^\(/','',$string); $string = preg_replace('/\)+$/','',$string); array_push($attrs, $string); } else { // Handle the opening cases first if ($string == '(') { $i++; } elseif (preg_match('/^\(./',$string)) { $string = preg_replace('/^\(/','',$string); array_push ($attrs, $string); $i++; } // Token is either a name, a $ or a ')' // NAME can be terminated by one or more ')' while (! preg_match('/\)+$/',$strings[$i])) { $string = $strings[$i]; if ($string == '$') { $i++; continue; } if (preg_match('/\)$/',$string)) { $string = preg_replace('/\)+$/','',$string); } else { $i++; } array_push ($attrs, $string); } } sort($attrs); return $i; } /** * Function not supported */ function execute($query) { return null; } /** * Function not supported */ function fetchAll($query, $cache = true) { return array(); } // Logs -------------------------------------------------------------- /** * Log given LDAP query. * * @param string $query LDAP statement * @todo: Add hook to log errors instead of returning false */ function logQuery($query) { $this->_queriesCnt++; $this->_queriesTime += $this->took; $this->_queriesLog[] = array ( 'query' => $query, 'error' => $this->error, 'affected' => $this->affected, 'numRows' => $this->numRows, 'took' => $this->took ); if (count($this->_queriesLog) > $this->_queriesLogMax) { array_pop($this->_queriesLog); } if ($this->error) { return false; } } /** * Outputs the contents of the queries log. * * @param boolean $sorted */ function showLog($sorted = false) { if ($sorted) { $log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC); } else { $log = $this->_queriesLog; } if ($this->_queriesCnt > 1) { $text = 'queries'; } else { $text = 'query'; } if (php_sapi_name() != 'cli') { print ("\n\n"); print ("\n\n\n\n"); foreach ($log as $k => $i) { print ("\n"); } print ("
{$this->_queriesCnt} {$text} took {$this->_queriesTime} ms
NrQueryErrorAffectedNum. rowsTook (ms)
" . ($k +1) . "{$i['query']}{$i['error']}{$i['affected']}{$i['numRows']}{$i['took']}
\n"); } else { foreach ($log as $k => $i) { print (($k +1) . ". {$i['query']} {$i['error']}\n"); } } } /** * Output information about a LDAP query. The query, number of rows in resultset, * and execution time in microseconds. If the query fails, an error is output instead. * * @param string $query Query to show information on. */ function showQuery($query) { $error = $this->error; if (strlen($query) > 200 && !$this->fullDebug) { $query = substr($query, 0, 200) . '[...]'; } if ($this->debug || $error) { print ("

Query: {$query} [Aff:{$this->affected} Num:{$this->numRows} Took:{$this->took}ms]"); if ($error) { print ("
ERROR: {$this->error}"); } print ('

'); } } // _ private -------------------------------------------------------------- function _conditions($conditions, $model) { $res = ''; $key = $model->primaryKey; $name = $model->name; if(is_array($conditions) && count($conditions) == 1) { $sqlHack = "$name.$key"; $conditions = str_ireplace($sqlHack, $key, $conditions); foreach($conditions as $k => $v){ if($k == $name.'.dn'){ $res = substr($v, 0, strpos($v, ',')); }elseif(($k == $sqlHack) && ( (empty($v))||($v =='*') ) ){ $res = 'objectclass=*'; }elseif($k == $sqlHack){ $res = "$key=$v"; }else{ $res = "$k=$v"; } } $conditions = $res; } if (is_array($conditions)) { // Conditions expressed as an array if (empty($conditions)){ $res = 'objectclass=*'; } } if(empty($conditions) ) { $res = 'objectclass=*'; }else{ $res = $conditions; } return $res; } /** * Convert an array into a ldap condition string * * @param array $conditions condition * @return string */ function __conditionsArrayToString($conditions) { $ops_rec = array ( 'and' => array('prefix'=>'&'), 'or' => array('prefix'=>'|')); $ops_neg = array ( 'and not' => array() , 'or not' => array(), 'not equals' => array()); $ops_ter = array ( 'equals' => array('null'=>'*')); $ops = array_merge($ops_rec,$ops_neg, $ops_ter); if (is_array($conditions)) { $operand = array_keys($conditions); $operand = $operand[0]; if (!in_array($operand,array_keys($ops)) ){ $this->log("No operators defined in LDAP search conditions.",'ldap.error'); return null; } $children = $conditions[$operand]; if (in_array($operand, array_keys($ops_rec)) ) { if (!is_array($children)) return null; $tmp = '('.$ops_rec[$operand]['prefix']; foreach ($children as $key => $value) { $child = array ($key => $value); $tmp .= $this->__conditionsArrayToString($child); } return $tmp.')'; } else if (in_array($operand, array_keys($ops_neg)) ) { if (!is_array($children)) return null; $next_operand = trim(str_replace('not', '', $operand)); return '(!'.$this->__conditionsArrayToString(array ($next_operand => $children)).')'; } else if (in_array($operand, array_keys($ops_ter)) ){ $tmp = ''; foreach ($children as $key => $value) { if ( !is_array($value) ) $tmp .= '('.$key .'='.((is_null($value))?$ops_ter['equals']['null']:$value).')'; else foreach ($value as $subvalue) $tmp .= $this->__conditionsArrayToString(array('equals' => array($key => $subvalue))); } return $tmp; } } } function _executeQuery($queryData = array (), $cache = true){ $t = getMicrotime(); $pattern = '/,[ \t]+(\w+)=/'; $queryData['targetDn'] = preg_replace($pattern, ',$1=',$queryData['targetDn']); if(strpos($queryData['targetDn'],$this->config['basedn']) === false){ if($queryData['targetDn'] != null){ $seperator = (substr($queryData['targetDn'], -1) == ',') ? '' : ','; if( (strpos($queryData['targetDn'], '=') === false) && (isset($this->model) && !empty($this->model)) ){ //Fix TargetDN here $key = $this->model->primaryKey; $table = $this->model->useTable; $queryData['targetDn'] = $key.'='.$queryData['targetDn'].', '.$table.$seperator.$this->config['basedn']; }else{ $queryData['targetDn'] = $queryData['targetDn'].$seperator.$this->config['basedn']; } }else{ $queryData['targetDn'] = $this->config['basedn']; } } $query = $this->_queryToString($queryData); if ($cache && isset ($this->_queryCache[$query])) { if (strpos(trim(strtolower($query)), $queryData['type']) !== false) { $res = $this->_queryCache[$query]; } } else { switch ($queryData['type']) { case 'search': // TODO pb ldap_search & $queryData['limit'] //Handle LDAP Scope if(isset($queryData['scope']) && $queryData['scope'] == 'base'){ $res = @ ldap_read($this->database, $queryData['targetDn'], $queryData['conditions'], $queryData['fields']); }elseif(isset($queryData['scope']) && $queryData['scope'] == 'one'){ $res = @ ldap_list($this->database, $queryData['targetDn'], $queryData['conditions'], $queryData['fields']); }else{ if($queryData['fields'] == 1) $queryData['fields'] = array(); $res = @ ldap_search($this->database, $queryData['targetDn'], $queryData['conditions'], $queryData['fields'], 0, $queryData['limit']); } if(!$res){ $res = false; $errMsg = ldap_error($this->database); $this->log("Query Params Failed:".print_r($queryData,true).' Error: '.$errMsg,'ldap.error'); $this->count = 0; }else{ $this->count = ldap_count_entries($this->database, $res); } if ($cache) { if (strpos(trim(strtolower($query)), $queryData['type']) !== false) { $this->_queryCache[$query] = $res; } } break; case 'delete': $res = @ ldap_delete($this->database, $queryData['targetDn'] . ',' . $this->config['basedn']); break; default: $res = false; break; } } $this->_result = $res; $this->took = round((getMicrotime() - $t) * 1000, 0); $this->error = $this->lastError(); $this->numRows = $this->lastNumRows(); if ($this->fullDebug) { $this->logQuery($query); } return $this->_result; } function _queryToString($queryData) { $tmp = ''; if (!empty($queryData['scope'])) $tmp .= ' | scope: '.$queryData['scope'].' '; if (!empty($queryData['conditions'])) $tmp .= ' | cond: '.$queryData['conditions'].' '; if (!empty($queryData['targetDn'])) $tmp .= ' | targetDn: '.$queryData['targetDn'].' '; $fields = ''; if (!empty($queryData['fields']) && is_array( $queryData['fields'] ) ) { $fields = implode(', ', $queryData['fields']); $tmp .= ' |fields: '.$fields.' '; } if (!empty($queryData['order'])) $tmp .= ' | order: '.$queryData['order'][0].' '; if (!empty($queryData['limit'])) $tmp .= ' | limit: '.$queryData['limit']; return $queryData['type'] . $tmp; } function _ldapFormat(& $model, $data) { $res = array (); foreach ($data as $key => $row){ if ($key === 'count') continue; foreach ($row as $key1 => $param){ if ($key1 === 'dn') { $res[$key][$model->name][$key1] = $param; continue; } if (!is_numeric($key1)) continue; if ($row[$param]['count'] === 1) $res[$key][$model->name][$param] = $row[$param][0]; else { foreach ($row[$param] as $key2 => $item) { if ($key2 === 'count') continue; $res[$key][$model->name][$param][] = $item; } } } } return $res; } function _ldapQuote($str) { return str_replace( array( '\\', ' ', '*', '(', ')' ), array( '\\5c', '\\20', '\\2a', '\\28', '\\29' ), $str ); } // __ ----------------------------------------------------- function __mergeAssociation(& $data, $merge, $association, $type) { if (isset ($merge[0]) && !isset ($merge[0][$association])) { $association = Inflector :: pluralize($association); } if ($type == 'belongsTo' || $type == 'hasOne') { if (isset ($merge[$association])) { $data[$association] = $merge[$association][0]; } else { if (count($merge[0][$association]) > 1) { foreach ($merge[0] as $assoc => $data2) { if ($assoc != $association) { $merge[0][$association][$assoc] = $data2; } } } if (!isset ($data[$association])) { $data[$association] = $merge[0][$association]; } else { if (is_array($merge[0][$association])) { $data[$association] = array_merge($merge[0][$association], $data[$association]); } } } } else { if ($merge[0][$association] === false) { if (!isset ($data[$association])) { $data[$association] = array (); } } else { foreach ($merge as $i => $row) { if (count($row) == 1) { $data[$association][] = $row[$association]; } else { $tmp = array_merge($row[$association], $row); unset ($tmp[$association]); $data[$association][] = $tmp; } } } } } /** * Private helper method to remove query metadata in given data array. * * @param array $data */ function __scrubQueryData(& $data) { if (!isset ($data['type'])) $data['type'] = 'default'; if (!isset ($data['conditions'])) $data['conditions'] = array(); if (!isset ($data['targetDn'])) $data['targetDn'] = null; if (!isset ($data['fields']) && empty($data['fields'])) $data['fields'] = array (); if (!isset ($data['order']) && empty($data['order'])) $data['order'] = array (); if (!isset ($data['limit'])) $data['limit'] = null; } function __getObjectclasses() { $cache = null; if ($this->cacheSources !== false) { if (isset($this->__descriptions['ldap_objectclasses'])) { $cache = $this->__descriptions['ldap_objectclasses']; } else { $cache = $this->__cacheDescription('objectclasses'); } } if ($cache != null) { return $cache; } // If we get this far, then we haven't cached the attribute types, yet! $ldapschema = $this->__getLDAPschema(); $objectclasses = $ldapschema['objectclasses']; // Cache away $this->__cacheDescription( 'objectclasses', $objectclasses ); return $objectclasses; } function boolean() { return null; } /** * Returns the count of records * * @param model $model * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max' * @param array $params Function parameters (any values must be quoted manually) * @return string entry count * @access public */ function calculate(&$model, $func, $params = array()) { $params = (array)$params; switch (strtolower($func)) { case 'count': if(empty($params) && $model->id){ //quick search to make sure it exsits $queryData['targetDn'] = $model->id; $queryData['conditions'] = 'objectClass=*'; $queryData['scope'] = 'base'; $query = $this->read($model, $queryData); } return $this->count; break; case 'max': case 'min': break; } } function describe(&$model, $field = null){ $schemas = $this->__getLDAPschema(); $attrs = $schemas['attributetypes']; ksort($attrs); if(!empty($field)){ return($attrs[strtolower($field)]); }else{ return $attrs; } } function in_arrayi( $needle, $haystack ) { $found = false; foreach( $haystack as $attr => $value ) { if( strtolower( $attr ) == strtolower( $needle ) ) { $found = true; } elseif( strtolower( $value ) == strtolower( $needle ) ) { $found = true; } } return $found; } } // LdapSource ?>