├── LICENSE ├── README.md └── plugins └── auth_ldap ├── ChangeLog └── init.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Ben Tyger 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tiny Tiny RSS Contributed files 2 | =============================== 3 | 4 | This repository contains files which had been removed from trunk for 5 | whatever reason (usually it's because I can't properly test their functionality). 6 | 7 | 8 | Usage instructions 9 | ================= 10 | 11 | First of app, make sure you have `php-ldap` installed. 12 | For Debian/Ubuntu users, just do 13 | 14 | `sudo apt-get install php-ldap` 15 | 16 | 17 | Now, open the `config.php` file in your TT-RSS directory. 18 | 19 | First, add the plugin to the list of enabled plugins: 20 | 21 | ```php 22 | /// append auth_ldap to the list 23 | define('PLUGINS', 'auth_ldap, auth_internal, note'); 24 | ``` 25 | 26 | Second, add the following lines to the file and fill in the details of your ldap installation: 27 | 28 | ```php 29 | // Required parameters: 30 | define('LDAP_AUTH_SERVER_URI', 'ldap://localhost:389/'); 31 | define('LDAP_AUTH_USETLS', FALSE); // Enable StartTLS Support for ldap:// 32 | define('LDAP_AUTH_ALLOW_UNTRUSTED_CERT', TRUE); // Allows untrusted certificate 33 | define('LDAP_AUTH_BASEDN', 'dc=example,dc=com'); 34 | define('LDAP_AUTH_ANONYMOUSBEFOREBIND', FALSE); 35 | // ??? will be replaced with the entered username(escaped) at login 36 | define('LDAP_AUTH_SEARCHFILTER', '(&(objectClass=person)(uid=???))'); 37 | 38 | // Optional configuration 39 | define('LDAP_AUTH_BINDDN', 'cn=serviceaccount,dc=example,dc=com'); 40 | define('LDAP_AUTH_BINDPW', 'ServiceAccountsPassword'); 41 | define('LDAP_AUTH_LOGIN_ATTRIB', 'uid'); 42 | define('LDAP_AUTH_LOG_ATTEMPTS', FALSE); 43 | 44 | // Enable Debug Logging 45 | define('LDAP_AUTH_DEBUG', FALSE); 46 | ``` 47 | -------------------------------------------------------------------------------- /plugins/auth_ldap/ChangeLog: -------------------------------------------------------------------------------- 1 | 2014-08-25 hydrian 2 | 3 | * init.php: Version 0.5rc2 4 | Security Issue: #10 5 | Bug Fix: #11 6 | 7 | 8 | 2013-11-22 hydrian 9 | 10 | * init.php: Version 0.5rc1 11 | Bug fixes #5 #6 12 | Enhancement #7 #8 13 | Major restructure of cache 14 | Major restructure of variables 15 | 16 | 2013-04-11 hydrian 17 | 18 | * init.php: version 0.03 19 | Fixed Schema file location when sys_get_temp_dir() is not availiable. 20 | 21 | 2013-04-08 hydrian 22 | 23 | * init.php: version 0.02 24 | Fixed LDAP over SSL 25 | Fixed authentication with anonymous 26 | Added LDAP schema cache for performance 27 | Added loggging for authentication attempts 28 | -------------------------------------------------------------------------------- /plugins/auth_ldap/init.php: -------------------------------------------------------------------------------- 1 | link = $host->get_link(); 76 | $this->host = $host; 77 | //$this->base = new Auth_Base($this->link); 78 | 79 | $host->add_hook($host::HOOK_AUTH_USER, $this); 80 | } 81 | 82 | private function _log($msg, $level = E_USER_NOTICE, $file = '', $line = 0, $context = '') { 83 | Logger::log_error($level, $msg, $file, $line, $context); 84 | } 85 | 86 | /** 87 | * Logs login attempts 88 | * @param string $username Given username that attempts to log in to TTRSS 89 | * @param string $result "Logging message for type of result. (Success / Fail)" 90 | * @return boolean 91 | * @deprecated 92 | * 93 | * Now that _log support syslog and log levels and graceful fallback user. 94 | */ 95 | private function _logAttempt($username, $result) { 96 | 97 | 98 | return trigger_error('TT-RSS Login Attempt: user ' . (string) $username . 99 | ' attempted to login (' . (string) $result . ') from ' . (string) $ip, E_USER_NOTICE 100 | ); 101 | } 102 | 103 | /** 104 | * @param string $subject The subject string 105 | * @param string $ignore Set of characters to leave untouched 106 | * @param int $flags Any combination of LDAP_ESCAPE_* flags to indicate the 107 | * set(s) of characters to escape. 108 | * @return string 109 | **/ 110 | function ldap_escape($subject, $ignore = '', $flags = 0) 111 | { 112 | if (!function_exists('ldap_escape')) { 113 | define('LDAP_ESCAPE_FILTER', 0x01); 114 | define('LDAP_ESCAPE_DN', 0x02); 115 | 116 | static $charMaps = array( 117 | LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"), 118 | LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#'), 119 | ); 120 | 121 | // Pre-process the char maps on first call 122 | if (!isset($charMaps[0])) { 123 | $charMaps[0] = array(); 124 | for ($i = 0; $i < 256; $i++) { 125 | $charMaps[0][chr($i)] = sprintf('\\%02x', $i);; 126 | } 127 | 128 | for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_FILTER]); $i < $l; $i++) { 129 | $chr = $charMaps[LDAP_ESCAPE_FILTER][$i]; 130 | unset($charMaps[LDAP_ESCAPE_FILTER][$i]); 131 | $charMaps[LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr]; 132 | } 133 | 134 | for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_DN]); $i < $l; $i++) { 135 | $chr = $charMaps[LDAP_ESCAPE_DN][$i]; 136 | unset($charMaps[LDAP_ESCAPE_DN][$i]); 137 | $charMaps[LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr]; 138 | } 139 | } 140 | 141 | // Create the base char map to escape 142 | $flags = (int)$flags; 143 | $charMap = array(); 144 | if ($flags & LDAP_ESCAPE_FILTER) { 145 | $charMap += $charMaps[LDAP_ESCAPE_FILTER]; 146 | } 147 | if ($flags & LDAP_ESCAPE_DN) { 148 | $charMap += $charMaps[LDAP_ESCAPE_DN]; 149 | } 150 | if (!$charMap) { 151 | $charMap = $charMaps[0]; 152 | } 153 | 154 | // Remove any chars to ignore from the list 155 | $ignore = (string)$ignore; 156 | for ($i = 0, $l = strlen($ignore); $i < $l; $i++) { 157 | unset($charMap[$ignore[$i]]); 158 | } 159 | 160 | // Do the main replacement 161 | $result = strtr($subject, $charMap); 162 | 163 | // Encode leading/trailing spaces if LDAP_ESCAPE_DN is passed 164 | if ($flags & LDAP_ESCAPE_DN) { 165 | if ($result[0] === ' ') { 166 | $result = '\\20' . substr($result, 1); 167 | } 168 | if ($result[strlen($result) - 1] === ' ') { 169 | $result = substr($result, 0, -1) . '\\20'; 170 | } 171 | } 172 | 173 | return $result; 174 | }else{ 175 | return ldap_escape($subject, $ignore, $flags); 176 | } 177 | } 178 | 179 | /** 180 | * Finds client's IP address 181 | * @return string 182 | */ 183 | private function _getClientIP() { 184 | if (!empty($_SERVER['HTTP_CLIENT_IP'])) { 185 | //check ip from share internet 186 | 187 | $ip = $_SERVER['HTTP_CLIENT_IP']; 188 | } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { 189 | //to check ip is pass from proxy 190 | $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; 191 | } else { 192 | $ip = $_SERVER['REMOTE_ADDR']; 193 | } 194 | 195 | return $ip; 196 | } 197 | 198 | private function _getBindDNWord() { 199 | return (strlen($this->_serviceBindDN) > 0 ) ? $this->_serviceBindDN : 'anonymous DN'; 200 | } 201 | 202 | private function _getTempDir() { 203 | if (!sys_get_temp_dir()) { 204 | $tmpFile = tempnam(); 205 | $tmpDir = dirname($tmpFile); 206 | unlink($tmpFile); 207 | unset($tmpFile); 208 | return $tmpDir; 209 | } else { 210 | return sys_get_temp_dir(); 211 | } 212 | } 213 | 214 | /** 215 | * Main Authentication method 216 | * Required for plugin interface 217 | * @param string $login User's username 218 | * @param string $password User's password 219 | * @param string $service Service to authenticate for 220 | * @return boolean 221 | */ 222 | function authenticate($login, $password, $service = "") { 223 | if ($login && $password) { 224 | 225 | if (!function_exists('ldap_connect')) { 226 | trigger_error('auth_ldap requires PHP\'s PECL LDAP package installed.'); 227 | return FALSE; 228 | } 229 | 230 | //Loading configuration 231 | $this->_debugMode = defined('LDAP_AUTH_DEBUG') ? 232 | LDAP_AUTH_DEBUG : FALSE; 233 | 234 | $this->_anonBeforeBind = defined('LDAP_AUTH_ANONYMOUSBEFOREBIND') ? 235 | LDAP_AUTH_ANONYMOUSBEFOREBIND : FALSE; 236 | 237 | $this->_serviceBindDN = defined('LDAP_AUTH_BINDDN') ? LDAP_AUTH_BINDDN : null; 238 | $this->_serviceBindPass = defined('LDAP_AUTH_BINDPW') ? LDAP_AUTH_BINDPW : null; 239 | $this->_baseDN = defined('LDAP_AUTH_BASEDN') ? LDAP_AUTH_BASEDN : null; 240 | if (!defined('LDAP_AUTH_BASEDN')) { 241 | $this->_log('LDAP_AUTH_BASEDN is required and not defined.', E_USER_ERROR); 242 | return FALSE; 243 | } else { 244 | $this->_baseDN = LDAP_AUTH_BASEDN; 245 | } 246 | 247 | $parsedURI = parse_url(LDAP_AUTH_SERVER_URI); 248 | $this->_uri = LDAP_AUTH_SERVER_URI; 249 | if ($parsedURI === FALSE) { 250 | $this->_log('Could not parse LDAP_AUTH_SERVER_URI in config.php', E_USER_ERROR); 251 | return FALSE; 252 | } 253 | $this->_host = $parsedURI['host']; 254 | $this->_scheme = $parsedURI['scheme']; 255 | 256 | if (is_int($parsedURI['port'])) { 257 | $this->_port = $parsedURI['port']; 258 | } else { 259 | $this->_port = ($this->_scheme === 'ldaps') ? 636 : 389; 260 | } 261 | 262 | $this->_useTLS = defined('LDAP_AUTH_USETLS') ? LDAP_AUTH_USETLS : FALSE; 263 | 264 | $this->_logAttempts = defined('LDAP_AUTH_LOG_ATTEMPTS') ? 265 | LDAP_AUTH_LOG_ATTEMPTS : FALSE; 266 | 267 | $this->_ldapLoginAttrib = defined('LDAP_AUTH_LOGIN_ATTRIB') ? 268 | LDAP_AUTH_LOGIN_ATTRIB : null; 269 | 270 | 271 | /** 272 | Building LDAP connection 273 | * */ 274 | $ldapConnParams = array( 275 | 'host' => $this->_host, 276 | 'basedn' => $this->_baseDN, 277 | 'port' => $this->_port, 278 | 'starttls' => $this->_useTLS 279 | ); 280 | 281 | if ($this->_debugMode) 282 | $this->_log(print_r($ldapConnParams, TRUE), E_USER_NOTICE); 283 | $ldapConn = @ldap_connect($this->_uri); 284 | if ($ldapConn === FALSE) { 285 | $this->_log('Could not connect to LDAP Server: \'' . $this->_host . '\'', E_USER_ERROR); 286 | return false; 287 | } 288 | 289 | /* Enable LDAP protocol version 3. */ 290 | if (!@ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3)) { 291 | $this->_log('Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', E_USER_ERROR); 292 | return false; 293 | } 294 | 295 | /* Set referral option */ 296 | if (!@ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, FALSE)) { 297 | $this->_log('Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to TRUE', E_USER_ERROR); 298 | return false; 299 | } 300 | 301 | if (stripos($this->_scheme, "ldaps") === FALSE and $this->_useTLS) { 302 | if (!@ldap_start_tls($ldapConn)) { 303 | $this->_log('Unable to force TLS', E_USER_ERROR); 304 | return false; 305 | } 306 | } 307 | $error = @ldap_bind($ldapConn, $this->_serviceBindDN, $this->_serviceBindPass); 308 | if ($error === FALSE) { 309 | $this->_log( 310 | 'LDAP bind(): Bind failed (' . $error . ')with DN ' . $this->_serviceBindDN, E_USER_ERROR 311 | ); 312 | return FALSE; 313 | } else { 314 | $this->_log( 315 | 'Connected to LDAP Server: ' . LDAP_AUTH_SERVER_URI . ' with ' . $this->_getBindDNWord()); 316 | } 317 | 318 | // Bind with service account if orignal connexion was anonymous 319 | /* if (($this->_anonBeforeBind) && (strlen($this->_bindDN > 0))) { 320 | $binding=$this->ldapObj->bind($this->_serviceBindDN, $this->_serviceBindPass); 321 | if (get_class($binding) !== 'Net_LDAP2') { 322 | $this->_log( 323 | 'Cound not bind service account: '.$binding->getMessage(),E_USER_ERROR); 324 | return FALSE; 325 | } else { 326 | $this->_log('Bind with '.$this->_serviceBindDN.' successful.',E_USER_NOTICE); 327 | } 328 | } */ 329 | 330 | //Searching for user 331 | $filterObj = str_replace('???', $this->ldap_escape($login), LDAP_AUTH_SEARCHFILTER); 332 | $searchResults = @ldap_search($ldapConn, $this->_baseDN, $filterObj, array('displayName', 'title', 'sAMAccountName', $this->_ldapLoginAttrib), 0, 0, 0); 333 | if ($searchResults === FALSE) { 334 | $this->_log('LDAP Search Failed on base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_ERROR); 335 | return FALSE; 336 | } 337 | $count = @ldap_count_entries($ldapConn, $searchResults); 338 | if ($count === FALSE) { 339 | 340 | } elseif ($count > 1) { 341 | $this->_log('Multiple DNs found for username ' . (string) $login, E_USER_WARNING); 342 | return FALSE; 343 | } elseif ($count === 0) { 344 | $this->_log('Unknown User ' . (string) $login, E_USER_NOTICE); 345 | return FALSE; 346 | } 347 | 348 | //Getting user's DN from search 349 | $userEntry = @ldap_first_entry($ldapConn, $searchResults); 350 | if ($userEntry === FALSE) { 351 | $this->_log('LDAP search(): Unable to retrieve result after searching base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_WARNING); 352 | return false; 353 | } 354 | $userAttributes = @ldap_get_attributes($ldapConn, $userEntry); 355 | $userDN = @ldap_get_dn($ldapConn, $userEntry); 356 | if ($userDN == FALSE) { 357 | $this->_log('LDAP search(): Unable to get DN after searching base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_WARNING); 358 | return false; 359 | } 360 | //Binding with user's DN. 361 | if ($this->_debugMode) 362 | $this->_log('Try to bind with user\'s DN: ' . $userDN); 363 | $loginAttempt = @ldap_bind($ldapConn, $userDN, $password); 364 | if ($loginAttempt === TRUE) { 365 | $this->_log('User: ' . (string) $login . ' authentication successful'); 366 | if (strlen($this->_ldapLoginAttrib) > 0) { 367 | if ($this->_debugMode) 368 | $this->_log('Looking up TT-RSS username attribute in ' . $this->_ldapLoginAttrib); 369 | $ttrssUsername = $userAttributes[$this->_ldapLoginAttrib][0]; 370 | ; 371 | @ldap_close($ldapConn); 372 | if (!is_string($ttrssUsername)) { 373 | $this->_log('Could not find user name attribute ' . $this->_ldapLoginAttrib . ' in LDAP entry', E_USER_WARNING); 374 | return FALSE; 375 | } 376 | return $this->auto_create_user($ttrssUsername); 377 | } else { 378 | @ldap_close($ldapConn); 379 | return $this->auto_create_user($login); 380 | } 381 | } else { 382 | @ldap_close($ldapConn); 383 | $this->_log('User: ' . (string) $login . ' authentication failed'); 384 | return FALSE; 385 | } 386 | } 387 | return false; 388 | } 389 | 390 | /** 391 | * Returns plugin API version 392 | * Required for plugin interface 393 | * @return number 394 | */ 395 | function api_version() { 396 | return 2; 397 | } 398 | 399 | } 400 | 401 | ?> 402 | --------------------------------------------------------------------------------