Automated build for v0.01
This commit is contained in:
403
plugins/auth_ldap/init.php
Normal file
403
plugins/auth_ldap/init.php
Normal file
@ -0,0 +1,403 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Tiny Tiny RSS plugin for LDAP authentication
|
||||
* @author tsmgeek (tsmgeek@gmail.com)
|
||||
* @author hydrian (ben.tyger@tygerclan.net)
|
||||
* @copyright GPL2
|
||||
* Requires php-ldap
|
||||
* @version 2.00
|
||||
*/
|
||||
/**
|
||||
* Configuration
|
||||
* Put the following options in config.php and customize them for your environment
|
||||
*
|
||||
* define('LDAP_AUTH_SERVER_URI', 'ldaps://LDAPServerHostname:port/');
|
||||
* define('LDAP_AUTH_USETLS', FALSE); // Enable TLS Support for ldaps://
|
||||
* define('LDAP_AUTH_ALLOW_UNTRUSTED_CERT', TRUE); // Allows untrusted certificate
|
||||
* define('LDAP_AUTH_BASEDN', 'dc=example,dc=com');
|
||||
* define('LDAP_AUTH_ANONYMOUSBEFOREBIND', FALSE);
|
||||
* // ??? will be replaced with the entered username(escaped) at login
|
||||
* define('LDAP_AUTH_SEARCHFILTER', '(&(objectClass=person)(uid=???))');
|
||||
* // Optional configuration
|
||||
* define('LDAP_AUTH_BINDDN', 'cn=serviceaccount,dc=example,dc=com');
|
||||
* define('LDAP_AUTH_BINDPW', 'ServiceAccountsPassword');
|
||||
* define('LDAP_AUTH_LOGIN_ATTRIB', 'uid');
|
||||
* define('LDAP_AUTH_LOG_ATTEMPTS', FALSE);
|
||||
* Enable Debug Logging
|
||||
* define('LDAP_AUTH_DEBUG', FALSE);
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Notes -
|
||||
* LDAP search does not support follow ldap referals. Referals are disabled to
|
||||
* allow proper login. This is particular to Active Directory.
|
||||
*
|
||||
* Also group membership can be supported if the user object contains the
|
||||
* the group membership via attributes. The following LDAP servers can
|
||||
* support this.
|
||||
* * Active Directory
|
||||
* * OpenLDAP support with MemberOf Overlay
|
||||
*
|
||||
*/
|
||||
class Auth_Ldap extends Plugin implements IAuthModule {
|
||||
|
||||
private $link;
|
||||
private $host;
|
||||
private $base;
|
||||
private $logClass;
|
||||
private $ldapObj = NULL;
|
||||
private $_debugMode;
|
||||
private $_serviceBindDN;
|
||||
private $_serviceBindPass;
|
||||
private $_baseDN;
|
||||
private $_useTLS;
|
||||
private $_host;
|
||||
private $_port;
|
||||
private $_scheme;
|
||||
private $_schemaCacheEnabled;
|
||||
private $_anonBeforeBind;
|
||||
private $_allowUntrustedCerts;
|
||||
private $_ldapLoginAttrib;
|
||||
|
||||
function about() {
|
||||
return array(0.05,
|
||||
"Authenticates against an LDAP server (configured in config.php)",
|
||||
"hydrian",
|
||||
true);
|
||||
}
|
||||
|
||||
function init($host) {
|
||||
$this->link = $host->get_link();
|
||||
$this->host = $host;
|
||||
$this->base = new Auth_Base($this->link);
|
||||
|
||||
$host->add_hook($host::HOOK_AUTH_USER, $this);
|
||||
}
|
||||
|
||||
private function _log($msg, $level = E_USER_NOTICE, $file = '', $line = 0, $context = '') {
|
||||
$loggerFunction = Logger::get();
|
||||
if (is_object($loggerFunction)) {
|
||||
$loggerFunction->log_error($level, $msg, $file, $line, $context);
|
||||
} else {
|
||||
trigger_error($msg, $level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs login attempts
|
||||
* @param string $username Given username that attempts to log in to TTRSS
|
||||
* @param string $result "Logging message for type of result. (Success / Fail)"
|
||||
* @return boolean
|
||||
* @deprecated
|
||||
*
|
||||
* Now that _log support syslog and log levels and graceful fallback user.
|
||||
*/
|
||||
private function _logAttempt($username, $result) {
|
||||
|
||||
|
||||
return trigger_error('TT-RSS Login Attempt: user ' . (string) $username .
|
||||
' attempted to login (' . (string) $result . ') from ' . (string) $ip, E_USER_NOTICE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $subject The subject string
|
||||
* @param string $ignore Set of characters to leave untouched
|
||||
* @param int $flags Any combination of LDAP_ESCAPE_* flags to indicate the
|
||||
* set(s) of characters to escape.
|
||||
* @return string
|
||||
**/
|
||||
function ldap_escape($subject, $ignore = '', $flags = 0)
|
||||
{
|
||||
if (!function_exists('ldap_escape')) {
|
||||
define('LDAP_ESCAPE_FILTER', 0x01);
|
||||
define('LDAP_ESCAPE_DN', 0x02);
|
||||
|
||||
static $charMaps = array(
|
||||
LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"),
|
||||
LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#'),
|
||||
);
|
||||
|
||||
// Pre-process the char maps on first call
|
||||
if (!isset($charMaps[0])) {
|
||||
$charMaps[0] = array();
|
||||
for ($i = 0; $i < 256; $i++) {
|
||||
$charMaps[0][chr($i)] = sprintf('\\%02x', $i);;
|
||||
}
|
||||
|
||||
for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_FILTER]); $i < $l; $i++) {
|
||||
$chr = $charMaps[LDAP_ESCAPE_FILTER][$i];
|
||||
unset($charMaps[LDAP_ESCAPE_FILTER][$i]);
|
||||
$charMaps[LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr];
|
||||
}
|
||||
|
||||
for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_DN]); $i < $l; $i++) {
|
||||
$chr = $charMaps[LDAP_ESCAPE_DN][$i];
|
||||
unset($charMaps[LDAP_ESCAPE_DN][$i]);
|
||||
$charMaps[LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr];
|
||||
}
|
||||
}
|
||||
|
||||
// Create the base char map to escape
|
||||
$flags = (int)$flags;
|
||||
$charMap = array();
|
||||
if ($flags & LDAP_ESCAPE_FILTER) {
|
||||
$charMap += $charMaps[LDAP_ESCAPE_FILTER];
|
||||
}
|
||||
if ($flags & LDAP_ESCAPE_DN) {
|
||||
$charMap += $charMaps[LDAP_ESCAPE_DN];
|
||||
}
|
||||
if (!$charMap) {
|
||||
$charMap = $charMaps[0];
|
||||
}
|
||||
|
||||
// Remove any chars to ignore from the list
|
||||
$ignore = (string)$ignore;
|
||||
for ($i = 0, $l = strlen($ignore); $i < $l; $i++) {
|
||||
unset($charMap[$ignore[$i]]);
|
||||
}
|
||||
|
||||
// Do the main replacement
|
||||
$result = strtr($subject, $charMap);
|
||||
|
||||
// Encode leading/trailing spaces if LDAP_ESCAPE_DN is passed
|
||||
if ($flags & LDAP_ESCAPE_DN) {
|
||||
if ($result[0] === ' ') {
|
||||
$result = '\\20' . substr($result, 1);
|
||||
}
|
||||
if ($result[strlen($result) - 1] === ' ') {
|
||||
$result = substr($result, 0, -1) . '\\20';
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}else{
|
||||
return ldap_escape($subject, $ignore, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds client's IP address
|
||||
* @return string
|
||||
*/
|
||||
private function _getClientIP() {
|
||||
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
//check ip from share internet
|
||||
|
||||
$ip = $_SERVER['HTTP_CLIENT_IP'];
|
||||
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
//to check ip is pass from proxy
|
||||
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
} else {
|
||||
$ip = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
||||
private function _getBindDNWord() {
|
||||
return (strlen($this->_serviceBindDN) > 0 ) ? $this->_serviceBindDN : 'anonymous DN';
|
||||
}
|
||||
|
||||
private function _getTempDir() {
|
||||
if (!sys_get_temp_dir()) {
|
||||
$tmpFile = tempnam();
|
||||
$tmpDir = dirname($tmpFile);
|
||||
unlink($tmpFile);
|
||||
unset($tmpFile);
|
||||
return $tmpDir;
|
||||
} else {
|
||||
return sys_get_temp_dir();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Authentication method
|
||||
* Required for plugin interface
|
||||
* @param unknown $login User's username
|
||||
* @param unknown $password User's password
|
||||
* @return boolean
|
||||
*/
|
||||
function authenticate($login, $password) {
|
||||
if ($login && $password) {
|
||||
|
||||
if (!function_exists('ldap_connect')) {
|
||||
trigger_error('auth_ldap requires PHP\'s PECL LDAP package installed.');
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//Loading configuration
|
||||
$this->_debugMode = defined('LDAP_AUTH_DEBUG') ?
|
||||
LDAP_AUTH_DEBUG : FALSE;
|
||||
|
||||
$this->_anonBeforeBind = defined('LDAP_AUTH_ANONYMOUSBEFOREBIND') ?
|
||||
LDAP_AUTH_ANONYMOUSBEFOREBIND : FALSE;
|
||||
|
||||
$this->_serviceBindDN = defined('LDAP_AUTH_BINDDN') ? LDAP_AUTH_BINDDN : null;
|
||||
$this->_serviceBindPass = defined('LDAP_AUTH_BINDPW') ? LDAP_AUTH_BINDPW : null;
|
||||
$this->_baseDN = defined('LDAP_AUTH_BASEDN') ? LDAP_AUTH_BASEDN : null;
|
||||
if (!defined('LDAP_AUTH_BASEDN')) {
|
||||
$this->_log('LDAP_AUTH_BASEDN is required and not defined.', E_USER_ERROR);
|
||||
return FALSE;
|
||||
} else {
|
||||
$this->_baseDN = LDAP_AUTH_BASEDN;
|
||||
}
|
||||
|
||||
$parsedURI = parse_url(LDAP_AUTH_SERVER_URI);
|
||||
if ($parsedURI === FALSE) {
|
||||
$this->_log('Could not parse LDAP_AUTH_SERVER_URI in config.php', E_USER_ERROR);
|
||||
return FALSE;
|
||||
}
|
||||
$this->_host = $parsedURI['host'];
|
||||
$this->_scheme = $parsedURI['scheme'];
|
||||
|
||||
if (is_int($parsedURI['port'])) {
|
||||
$this->_port = $parsedURI['port'];
|
||||
} else {
|
||||
$this->_port = ($this->_scheme === 'ldaps') ? 636 : 389;
|
||||
}
|
||||
|
||||
$this->_useTLS = defined('LDAP_AUTH_USETLS') ? LDAP_AUTH_USETLS : FALSE;
|
||||
|
||||
$this->_logAttempts = defined('LDAP_AUTH_LOG_ATTEMPTS') ?
|
||||
LDAP_AUTH_LOG_ATTEMPTS : FALSE;
|
||||
|
||||
$this->_ldapLoginAttrib = defined('LDAP_AUTH_LOGIN_ATTRIB') ?
|
||||
LDAP_AUTH_LOGIN_ATTRIB : null;
|
||||
|
||||
|
||||
/**
|
||||
Building LDAP connection
|
||||
* */
|
||||
$ldapConnParams = array(
|
||||
'host' => $this->_host,
|
||||
'basedn' => $this->_baseDN,
|
||||
'port' => $this->_port,
|
||||
'starttls' => $this->_useTLS
|
||||
);
|
||||
|
||||
if ($this->_debugMode)
|
||||
$this->_log(print_r($ldapConnParams, TRUE), E_USER_NOTICE);
|
||||
$ldapConn = @ldap_connect($this->_host, $this->_port);
|
||||
if ($ldapConn === FALSE) {
|
||||
$this->_log('Could not connect to LDAP Server: \'' . $this->_host . '\'', E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Enable LDAP protocol version 3. */
|
||||
if (!@ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3)) {
|
||||
$this->_log('Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set referral option */
|
||||
if (!@ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, FALSE)) {
|
||||
$this->_log('Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to TRUE', E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stripos($this->_host, "ldaps:") === FALSE and $this->_useTLS) {
|
||||
if (!@ldap_start_tls($ldapConn)) {
|
||||
$this->_log('Unable to force TLS', E_USER_ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$error = @ldap_bind($ldapConn, $this->_serviceBindDN, $this->_serviceBindPass);
|
||||
if ($error === FALSE) {
|
||||
$this->_log(
|
||||
'LDAP bind(): Bind failed (' . $error . ')with DN ' . $this->_serviceBindDN, E_USER_ERROR
|
||||
);
|
||||
return FALSE;
|
||||
} else {
|
||||
$this->_log(
|
||||
'Connected to LDAP Server: ' . LDAP_AUTH_SERVER_URI . ' with ' . $this->_getBindDNWord());
|
||||
}
|
||||
|
||||
// Bind with service account if orignal connexion was anonymous
|
||||
/* if (($this->_anonBeforeBind) && (strlen($this->_bindDN > 0))) {
|
||||
$binding=$this->ldapObj->bind($this->_serviceBindDN, $this->_serviceBindPass);
|
||||
if (get_class($binding) !== 'Net_LDAP2') {
|
||||
$this->_log(
|
||||
'Cound not bind service account: '.$binding->getMessage(),E_USER_ERROR);
|
||||
return FALSE;
|
||||
} else {
|
||||
$this->_log('Bind with '.$this->_serviceBindDN.' successful.',E_USER_NOTICE);
|
||||
}
|
||||
} */
|
||||
|
||||
//Searching for user
|
||||
$filterObj = str_replace('???', $this->ldap_escape($login), LDAP_AUTH_SEARCHFILTER);
|
||||
$searchResults = @ldap_search($ldapConn, $this->_baseDN, $filterObj, array('displayName', 'title', 'sAMAccountName', $this->_ldapLoginAttrib), 0, 0, 0);
|
||||
if ($searchResults === FALSE) {
|
||||
$this->_log('LDAP Search Failed on base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_ERROR);
|
||||
return FALSE;
|
||||
}
|
||||
$count = @ldap_count_entries($ldapConn, $searchResults);
|
||||
if ($count === FALSE) {
|
||||
|
||||
} elseif ($count > 1) {
|
||||
$this->_log('Multiple DNs found for username ' . (string) $login, E_USER_WARNING);
|
||||
return FALSE;
|
||||
} elseif ($count === 0) {
|
||||
$this->_log((string) $login, 'Unknown User', E_USER_NOTICE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//Getting user's DN from search
|
||||
$userEntry = @ldap_first_entry($ldapConn, $searchResults);
|
||||
if ($userEntry === FALSE) {
|
||||
$this->_log('LDAP search(): Unable to retrieve result after searching base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
$userAttributes = @ldap_get_attributes($ldapConn, $userEntry);
|
||||
$userDN = @ldap_get_dn($ldapConn, $userEntry);
|
||||
if ($userDN == FALSE) {
|
||||
$this->_log('LDAP search(): Unable to get DN after searching base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_WARNING);
|
||||
return false;
|
||||
}
|
||||
//Binding with user's DN.
|
||||
if ($this->_debugMode)
|
||||
$this->_log('Try to bind with user\'s DN: ' . $userDN);
|
||||
$loginAttempt = @ldap_bind($ldapConn, $userDN, $password);
|
||||
if ($loginAttempt === TRUE) {
|
||||
$this->_log('User: ' . (string) $login . ' authentication successful');
|
||||
if (strlen($this->_ldapLoginAttrib) > 0) {
|
||||
if ($this->_debugMode)
|
||||
$this->_log('Looking up TT-RSS username attribute in ' . $this->_ldapLoginAttrib);
|
||||
$ttrssUsername = $userAttributes[$this->_ldapLoginAttrib][0];
|
||||
;
|
||||
@ldap_close($ldapConn);
|
||||
if (!is_string($ttrssUsername)) {
|
||||
$this->_log('Could not find user name attribute ' . $this->_ldapLoginAttrib . ' in LDAP entry', E_USER_WARNING);
|
||||
return FALSE;
|
||||
}
|
||||
return $this->base->auto_create_user($ttrssUsername);
|
||||
} else {
|
||||
@ldap_close($ldapConn);
|
||||
return $this->base->auto_create_user($login);
|
||||
}
|
||||
} else {
|
||||
@ldap_close($ldapConn);
|
||||
$this->_log('User: ' . (string) $login . ' authentication failed');
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns plugin API version
|
||||
* Required for plugin interface
|
||||
* @return number
|
||||
*/
|
||||
function api_version() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
Reference in New Issue
Block a user