Skip to content

Instantly share code, notes, and snippets.

@grom358
Last active December 4, 2015 02:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grom358/4ef0f1db6bf807144816 to your computer and use it in GitHub Desktop.
Save grom358/4ef0f1db6bf807144816 to your computer and use it in GitHub Desktop.
<?php
class LDAP_Connection {
protected $ds;
protected $baseDN;
protected $hostname;
protected $port;
protected $binaryFields;
private $isConnected;
public function __construct($config = null) {
global $LDAP_DEFAULT_CONFIG;
if (!isset($config)) {
$config = $LDAP_DEFAULT_CONFIG;
}
$this->hostname = $config['hostname'];
if (strlen($this->hostname) > 8 && substr($this->hostname, 0, 8) == 'ldaps://') {
$defaultPort = 636;
} else {
$defaultPort = 389;
}
$this->port = isset($config['port']) ? $config['port'] : $defaultPort;
$this->ds = ldap_connect($this->hostname, $this->port);
if (!$this->ds) {
// Connections to fedora directory don't fail till binding
$this->ds = null;
throw new LDAP_Exception("Could not connect to " . $this->hostname . ':' . $this->port, -1);
}
$this->setOptions();
// Bind as anonymous to test connection
if (!@ldap_bind($this->ds)) {
$this->ds = null;
throw new LDAP_Exception("Could not connect to " . $this->hostname . ':' . $this->port, -1);
}
$this->baseDN = $config['baseDN'];
$this->binaryFields = isset($config['binaryFields']) ? $config['binaryFields'] : array();
$this->isConnected = true;
}
protected function setOptions() {
ldap_set_option($this->ds, LDAP_OPT_PROTOCOL_VERSION, 3);
}
public function __destruct() {
$this->disconnect();
}
protected function checkForError() {
LDAP_Exception::checkForLdapError($this->ds);
}
public function bind($user, $password) {
$this->bindDN('uid=' . $user . ',' . $this->baseDN, $password);
}
public function bindAsDirectoryManager() {
$this->bindDN(LDAP_DIRECTORY_MANAGER_DN, LDAP_DIRECTORY_MANAGER_PASSWORD);
}
public function bindDN($bindDN, $password) {
if (!@ldap_bind($this->ds, $bindDN, $password)) {
$errorCode = ldap_errno($this->ds);
if ($errorCode == 32) {
throw new LDAP_Exception("Unable to bind as " . $bindDN, $errorCode);
} elseif ($errorCode == -1) {
// It appears a connection to fedora directory doesn't occur till you attempt to bind
throw new LDAP_Exception("Could not connect to " . $this->hostname . ':' . $this->port, $errorCode);
} else {
$this->checkForError();
}
}
}
public function search($filter, $attributes = array(), $searchRDN = null) {
if ($filter instanceof LDAP_Filter) {
$filter = $filter->filter;
}
$searchDN = $this->baseDN;
if ($searchRDN) {
$searchDN = $searchRDN . ',' . $this->baseDN;
}
$sr = @ldap_search($this->ds, $searchDN, $filter, $attributes);
$this->checkForError();
return new LDAP_SearchResults($this->ds, $this->baseDN, $sr, $this->binaryFields);
}
/**
*
* Returns null if there is no such record
*/
public function read($rdn, $attributes = array()) {
$dn = $rdn . ',' . $this->baseDN;
$data = LDAP_Utils::getEntry($this->ds, $dn, $attributes, $this->binaryFields);
if ($data == null) {
return null;
}
if (array_key_exists('nsRole', $data)) {
$data['roles'] = LDAP_Utils::getRoles($this->ds, $data);
}
return new LDAP_Entry($this->ds, $rdn, $dn, $data);
}
public function add($rdn, $data) {
$dn = $rdn . ',' . $this->baseDN;
@ldap_add($this->ds, $dn, $data);
$this->checkForError();
}
public function delete($rdn) {
$dn = $rdn . ',' . $this->baseDN;
@ldap_delete($this->ds, $dn);
$this->checkForError();
}
public function rename($oldRDN, $newRDN, $newParent = null, $deleteOldDN = false) {
$oldDN = $oldRDN . ',' . $this->baseDN;
if ($newParent == null) {
$newParent = LDAP_Utils::getParent($oldDN);
} else {
$newParent .= ',' . $this->baseDN;
}
@ldap_rename($this->ds, $oldDN, $newRDN, $newParent, $deleteOldDN);
$this->checkForError();
}
public function move($oldRDN, $newRDN) {
$oldDN = $oldRDN . ',' . $this->baseDN;
$newDN = $newRDN . ',' . $this->baseDN;
$parent = LDAP_Utils::getParent($oldDN);
$topRDN = LDAP_Utils::getRDN($newDN, $parent);
@ldap_rename($this->ds, $oldDN, $topRDN, $parent, false);
$this->checkForError();
}
/**
* Shortcut for adding attributes
*/
public function addAttributes($rdn, $data) {
$dn = $rdn . ',' . $this->baseDN;
@ldap_mod_add($this->ds, $dn, $data);
$this->checkForError();
}
/**
* Shortcut for replacing attributes
*/
public function replaceAttributes($rdn, $data) {
$dn = $rdn . ',' . $this->baseDN;
@ldap_mod_replace($this->ds, $dn, $data);
$this->checkForError();
}
/**
* Shortcut for deleting attributes
*/
public function deleteAttributes($rdn, $data) {
$dn = $rdn . ',' . $this->baseDN;
@ldap_mod_del($this->ds, $dn, $data);
$this->checkForError();
}
public function disconnect() {
if ($this->isConnected) {
ldap_close($this->ds);
}
$this->isConnected = false;
}
}
class LDAP_Filter {
public $filter;
static public function create($filter = 'objectclass=*') {
$builder = new LDAP_Filter('(' . $filter . ')');
return $builder;
}
private function __construct($filter) {
$this->filter = $filter;
}
public function and_($filter) {
if (is_a($filter, 'LDAP_Filter')) {
$this->filter = '(&' . $this->filter . $filter->filter . ')';
} else {
$this->filter = '(&' . $this->filter . '(' . $filter . '))';
}
return $this;
}
public function or_($filter) {
if (is_a($filter, 'LDAP_Filter')) {
$this->filter = '(|' . $this->filter . $filter->filter . ')';
} else {
$this->filter = '(|' . $this->filter . '(' . $filter . '))';
}
return $this;
}
static private function _not($filter) {
return '!(' . $filter . ')';
}
static public function not($filter) {
return new LDAP_Filter('(' . self::_not($filter) . ')');
}
public function andNot($filter) {
return $this->and_(self::_not($filter));
}
public function orNot($filter) {
return $this->or_(self::_not($filter));
}
}
class LDAP_SearchResults {
protected $ds;
protected $baseDN;
protected $sr;
protected $binaryFields;
protected $entryID = null;
public function __construct($ds, $baseDN, $sr, $binaryFields) {
$this->ds = $ds;
$this->baseDN = $baseDN;
$this->sr = $sr;
$this->binaryFields = $binaryFields;
}
public function next() {
if ($this->entryID === null) {
$this->entryID = ldap_first_entry($this->ds, $this->sr);
} else {
$this->entryID = ldap_next_entry($this->ds, $this->entryID);
}
if (!$this->entryID) {
return null;
}
$data = LDAP_Utils::readEntry($this->ds, $this->entryID, $this->binaryFields);
if (array_key_exists('nsRole', $data)) {
$data['roles'] = LDAP_Utils::getRoles($this->ds, $data);
}
$dn = ldap_get_dn($this->ds, $this->entryID);
$rdn = LDAP_Utils::getRDN($dn, $this->baseDN);
return new LDAP_Entry($this->ds, $rdn, $dn, $data);
}
public function count() {
return ldap_count_entries($this->ds, $this->sr);
}
public function sort($sortBy) {
if (is_array($sortBy)) {
$sortAttributes = array_reverse($sortBy);
foreach ($sortAttributes as $sortAttr) {
ldap_sort($this->ds, $this->sr, $sortAttr);
}
} else {
ldap_sort($this->ds, $this->sr, $sortBy);
}
}
public function getAll($dataOnly = false, $includeDN = true) {
$results = array();
while ($entry = $this->next()) {
if ($dataOnly) {
if ($includeDN) {
$results[] = array_merge(array('rdn' => $entry->rdn, 'dn' => $entry->dn), $entry->data);
} else {
$results[] = $entry->data;
}
} else {
$results[] = $entry;
}
}
return $results;
}
public function getCol($columnName = null) {
$results = array();
while ($entry = $this->next()) {
if ($columnName == null) {
$columnName = key($entry->data);
}
$results[] = $entry->data[$columnName];
}
return $results;
}
public function getAssoc($idColumnName = null, $valueColumnName = null) {
$results = array();
while ($entry = $this->next()) {
if ($idColumnName == null) {
$idColumnName = key($entry->data);
}
if ($valueColumnName == null) {
$keys = array_keys($entry->data);
$valueColumnName = $keys[1];
}
$results[$entry->data[$idColumnName]] = $entry->data[$valueColumnName];
}
return $results;
}
}
class LDAP_Entry {
protected $ds;
public $rdn;
public $dn;
public $dnAttribute;
public $data;
protected $oldData;
public function __construct($ds, $rdn, $dn, $data) {
$this->ds = $ds;
$this->rdn = $rdn;
$this->dn = $dn;
$this->data = $data;
$this->oldData = $data;
$exploded_dn = ldap_explode_dn($dn, 0);
$top_dn = explode('=', $exploded_dn[0]);
$this->dnAttribute = strtolower($top_dn[0]);
}
protected function checkForError() {
LDAP_Exception::checkForLdapError($this->ds);
}
public function deleteAttribute($attributeName) {
@ldap_mod_del($this->ds, $this->dn, array($attributeName => array()));
$this->checkForError();
unset($this->oldData[$attributeName]);
unset($this->data[$attributeName]);
}
public function update() {
// Update LDAP entry
$add = array(); $replace = array(); $delete = array();
$renameTo = null;
foreach ($this->data as $key => $val) {
if ($key == $this->dnAttribute && $val !== $this->oldData[$key]) {
$renameTo = $val;
} elseif (array_key_exists($key, $this->oldData)) {
if ($val == null) {
$delete[$key] = array();
} elseif ($val !== $this->oldData[$key]) {
$replace[$key] = $val;
}
} elseif ($val != null) {
$add[$key] = $val;
}
}
if (count($add) > 0) {
@ldap_mod_add($this->ds, $this->dn, $add);
$this->checkForError();
}
if (count($replace) > 0) {
@ldap_mod_replace($this->ds, $this->dn, $replace);
$this->checkForError();
}
if (count($delete) > 0) {
@ldap_mod_del($this->ds, $this->dn, $delete);
$this->checkForError();
}
if ($renameTo) {
$topRDN = $this->dnAttribute . '=' . $renameTo;
$parent = LDAP_Utils::getParent($this->dn);
@ldap_rename($this->ds, $this->dn, $topRDN, $parent, true);
$this->checkForError();
$this->rdn = $topRDN . ',' . LDAP_Utils::getParent($this->rdn);
$this->dn = $topRDN . ',' . $parent;
}
}
}
class LDAP_Utils {
/**
* Take an LDAP entry and make an associative array from it.
*
* This function takes an LDAP entry in the ldap_get_entries() style and
* converts it to an associative array like ldap_add() needs.
*
* @param array $entry is the entry that should be converted.
*
* @return array is the converted entry.
*/
static public function cleanUpEntry( &$entry ) {
$retEntry = array();
for ( $i = 0; $i < $entry['count']; $i++ ) {
$attribute = $entry[$i];
if ( $entry[$attribute]['count'] == 1 ) {
$retEntry[$attribute] = $entry[$attribute][0];
} else {
for ( $j = 0; $j < $entry[$attribute]['count']; $j++ ) {
$retEntry[$attribute][] = $entry[$attribute][$j];
}
}
}
return $retEntry;
}
static public function readEntry($ds, $entryId, $binaryFields = array()) {
$data = array();
for ($attribute = ldap_first_attribute($ds, $entryId, $attributeId); $attribute !== false; $attribute = ldap_next_attribute($ds, $entryId, $attributeId)) {
$fieldValues = ldap_get_values($ds, $entryId, $attribute);
if (in_array($attribute, $binaryFields)) {
$fieldValues = ldap_get_values_len($ds, $entryId, $attribute);
}
if ($fieldValues['count'] == 1) {
$data[$attribute] = $fieldValues[0];
} else {
for ($i = 0; $i < $fieldValues['count']; $i++) {
$data[$attribute][$i] = $fieldValues[$i];
}
}
}
return $data;
}
static public function getEntry($ds, $dn, $attributes = array(), $binaryFields = array()) {
$sr = @ldap_read($ds, $dn, '(objectclass=*)', $attributes);
if (!$sr) {
return null;
}
$entryID = ldap_first_entry($ds, $sr);
$entry = self::readEntry($ds, $entryID, $binaryFields);
return $entry;
}
static public function getParent($dn) {
$exploded_dn = ldap_explode_dn($dn, 0);
$path = array();
for ($i = 1; $i < $exploded_dn['count']; $i++) {
$path[] = $exploded_dn[$i];
}
return implode(',', $path);
}
/**
* Test wether object class exists in objectclass attribute
*/
static public function hasClass($class, $ldapClasses) {
if (in_array($class, $ldapClasses) || in_array(strtolower($class), $ldapClasses)) {
return true;
}
return false;
}
/**
* Extract out the roles from LDAP entry
*/
static public function getRoles( &$ds, &$ldap_entry ) {
$roles = array();
if (!$ldap_entry['nsRole'] ) {
return $roles;
}
if (!is_array($ldap_entry['nsRole'])) {
$ldap_entry['nsRole'] = array($ldap_entry['nsRole']);
}
foreach ($ldap_entry['nsRole'] as $dn) {
$sr = ldap_read($ds, $dn, '(objectClass=*)', array('cn'));
$entryId = ldap_first_entry($ds, $sr);
$entry = self::cleanUpEntry(ldap_get_attributes($ds, $entryId));
$roles[] = $entry['cn'];
}
return $roles;
}
static public function getRDN($dn, $baseDN) {
return substr($dn, 0, strlen($dn) - strlen($baseDN) - 1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment