Add generic LDAP client library
This commit is contained in:
84
app/Core/Ldap/Client.php
Normal file
84
app/Core/Ldap/Client.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Ldap;
|
||||
|
||||
/**
|
||||
* LDAP Client
|
||||
*
|
||||
* @package ldap
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
/**
|
||||
* Get server connection
|
||||
*
|
||||
* @access public
|
||||
* @param string $server LDAP server hostname or IP
|
||||
* @param integer $port LDAP port
|
||||
* @param boolean $tls Start TLS
|
||||
* @param boolean $verify Skip SSL certificate verification
|
||||
* @return resource
|
||||
*/
|
||||
public function getConnection($server, $port = LDAP_PORT, $tls = LDAP_START_TLS, $verify = LDAP_SSL_VERIFY)
|
||||
{
|
||||
if (! function_exists('ldap_connect')) {
|
||||
throw new ClientException('LDAP: The PHP LDAP extension is required');
|
||||
}
|
||||
|
||||
if (! $verify) {
|
||||
putenv('LDAPTLS_REQCERT=never');
|
||||
}
|
||||
|
||||
$ldap = ldap_connect($server, $port);
|
||||
|
||||
if ($ldap === false) {
|
||||
throw new ClientException('LDAP: Unable to connect to the LDAP server');
|
||||
}
|
||||
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, 1);
|
||||
ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, 1);
|
||||
|
||||
if ($tls && ! @ldap_start_tls($ldap)) {
|
||||
throw new ClientException('LDAP: Unable to start TLS');
|
||||
}
|
||||
|
||||
return $ldap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymous authentication
|
||||
*
|
||||
* @access public
|
||||
* @param resource $ldap
|
||||
* @return boolean
|
||||
*/
|
||||
public function useAnonymousAuthentication($ldap)
|
||||
{
|
||||
if (! ldap_bind($ldap)) {
|
||||
throw new ClientException('Unable to perform anonymous binding');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication with username/password
|
||||
*
|
||||
* @access public
|
||||
* @param resource $ldap
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate($ldap, $username, $password)
|
||||
{
|
||||
if (! ldap_bind($ldap, $username, $password)) {
|
||||
throw new ClientException('Unable to perform anonymous binding');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
15
app/Core/Ldap/ClientException.php
Normal file
15
app/Core/Ldap/ClientException.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Ldap;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* LDAP Client Exception
|
||||
*
|
||||
* @package ldap
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ClientException extends Exception
|
||||
{
|
||||
}
|
||||
95
app/Core/Ldap/Query.php
Normal file
95
app/Core/Ldap/Query.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Ldap;
|
||||
|
||||
/**
|
||||
* LDAP Query
|
||||
*
|
||||
* @package ldap
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Query
|
||||
{
|
||||
/**
|
||||
* Query result
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $entries = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param array $entries
|
||||
*/
|
||||
public function __construct(array $entries = array())
|
||||
{
|
||||
$this->entries = $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute query
|
||||
*
|
||||
* @access public
|
||||
* @param resource $ldap
|
||||
* @param string $baseDn
|
||||
* @param string $filter
|
||||
* @param array $attributes
|
||||
* @return Query
|
||||
*/
|
||||
public function execute($ldap, $baseDn, $filter, array $attributes)
|
||||
{
|
||||
$sr = ldap_search($ldap, $baseDn, $filter, $attributes);
|
||||
if ($sr === false) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$entries = ldap_get_entries($ldap, $sr);
|
||||
if ($entries === false || count($entries) === 0 || $entries['count'] == 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->entries = $entries;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query returned a result
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasResult()
|
||||
{
|
||||
return ! empty($this->entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return subset of entries
|
||||
*
|
||||
* @access public
|
||||
* @param string $key
|
||||
* @param mixed $default
|
||||
* @return array
|
||||
*/
|
||||
public function getAttribute($key, $default = null)
|
||||
{
|
||||
return isset($this->entries[0][$key]) ? $this->entries[0][$key] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return one entry from a list of entries
|
||||
*
|
||||
* @access public
|
||||
* @param string $key Key
|
||||
* @param string $default Default value if key not set in entry
|
||||
* @return string
|
||||
*/
|
||||
public function getAttributeValue($key, $default = '')
|
||||
{
|
||||
return isset($this->entries[0][$key][0]) ? $this->entries[0][$key][0] : $default;
|
||||
}
|
||||
}
|
||||
178
app/Core/Ldap/User.php
Normal file
178
app/Core/Ldap/User.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Ldap;
|
||||
|
||||
/**
|
||||
* LDAP User
|
||||
*
|
||||
* @package ldap
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* Query
|
||||
*
|
||||
* @access private
|
||||
* @var Query
|
||||
*/
|
||||
private $query;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param Query $query
|
||||
*/
|
||||
public function __construct(Query $query = null)
|
||||
{
|
||||
$this->query = $query ?: new Query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user profile
|
||||
*
|
||||
* @access public
|
||||
* @param resource $ldap
|
||||
* @param string $baseDn
|
||||
* @param string $query
|
||||
* @return array
|
||||
*/
|
||||
public function getProfile($ldap, $baseDn, $query)
|
||||
{
|
||||
$this->query->execute($ldap, $baseDn, $query, $this->getAttributes());
|
||||
$profile = array();
|
||||
|
||||
if ($this->query->hasResult()) {
|
||||
$profile = $this->prepareProfile();
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build user profile
|
||||
*
|
||||
* @access private
|
||||
* @return boolean|array
|
||||
*/
|
||||
private function prepareProfile()
|
||||
{
|
||||
return array(
|
||||
'ldap_id' => $this->query->getAttribute('dn', ''),
|
||||
'username' => $this->query->getAttributeValue($this->getAttributeUsername()),
|
||||
'name' => $this->query->getAttributeValue($this->getAttributeName()),
|
||||
'email' => $this->query->getAttributeValue($this->getAttributeEmail()),
|
||||
'is_admin' => (int) $this->isMemberOf($this->query->getAttribute($this->getAttributeGroup(), array()), $this->getGroupAdminDn()),
|
||||
'is_project_admin' => (int) $this->isMemberOf($this->query->getAttribute($this->getAttributeGroup(), array()), $this->getGroupProjectAdminDn()),
|
||||
'is_ldap_user' => 1,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check group membership
|
||||
*
|
||||
* @access public
|
||||
* @param array $group_entries
|
||||
* @param string $group_dn
|
||||
* @return boolean
|
||||
*/
|
||||
public function isMemberOf(array $group_entries, $group_dn)
|
||||
{
|
||||
if (! isset($group_entries['count']) || empty($group_dn)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $group_entries['count']; $i++) {
|
||||
if ($group_entries[$i] === $group_dn) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ge the list of attributes to fetch when reading the LDAP user entry
|
||||
*
|
||||
* Must returns array with index that start at 0 otherwise ldap_search returns a warning "Array initialization wrong"
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return array_values(array_filter(array(
|
||||
$this->getAttributeUsername(),
|
||||
$this->getAttributeName(),
|
||||
$this->getAttributeEmail(),
|
||||
$this->getAttributeGroup(),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP account id attribute
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAttributeUsername()
|
||||
{
|
||||
return LDAP_ACCOUNT_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP account email attribute
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAttributeEmail()
|
||||
{
|
||||
return LDAP_ACCOUNT_EMAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP account name attribute
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAttributeName()
|
||||
{
|
||||
return LDAP_ACCOUNT_FULLNAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP account memberof attribute
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAttributeGroup()
|
||||
{
|
||||
return LDAP_ACCOUNT_MEMBEROF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP admin group DN
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupAdminDn()
|
||||
{
|
||||
return LDAP_GROUP_ADMIN_DN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get LDAP project admin group DN
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupProjectAdminDn()
|
||||
{
|
||||
return LDAP_GROUP_PROJECT_ADMIN_DN;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user