Newer
Older
<?php
/**
* Active Directory authentication backend for DokuWiki
*
* This makes authentication with a Active Directory server much easier
* than when using the normal LDAP backend by utilizing the adLDAP library
*
* Usage:
* Set DokuWiki's local.protected.php auth setting to read
*
* $conf['useacl'] = 1;
* $conf['disableactions'] = 'register';
* $conf['autopasswd'] = 0;
* $conf['authtype'] = 'ad';
* $conf['passcrypt'] = 'ssha';
*
* $conf['auth']['ad']['account_suffix'] = '@my.domain.org';
* $conf['auth']['ad']['base_dn'] = 'DC=my,DC=domain,DC=org';
* $conf['auth']['ad']['domain_controllers'] = 'srv1.domain.org,srv2.domain.org';
*
* //optional:
* $conf['auth']['ad']['sso'] = 1;
* $conf['auth']['ad']['ad_username'] = 'root';
* $conf['auth']['ad']['ad_password'] = 'pass';
* $conf['auth']['ad']['real_primarygroup'] = 1;
* $conf['auth']['ad']['use_ssl'] = 1;
* $conf['auth']['ad']['debug'] = 1;
*
* // get additional informations to the userinfo array
* // add a list of comma separated ldap contact fields.
* $conf['auth']['ad']['additional'] = 'field1,field2';
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author James Van Lommel <jamesvl@gmail.com>
* @link http://www.nosq.com/blog/2005/08/ldap-activedirectory-and-dokuwiki/
* @author Andreas Gohr <andi@splitbrain.org>
*/
require_once(DOKU_INC.'inc/adLDAP.php');
class auth_ad extends auth_basic {
var $cnf = null;
/**
* Constructor
*/
function auth_ad() {
global $conf;
$this->cnf = $conf['auth']['ad'];
// additional information fields
if (isset($this->cnf['additional'])) {
$this->cnf['additional'] = str_replace(' ', '', $this->cnf['additional']);
$this->cnf['additional'] = explode(',', $this->cnf['additional']);
} else $this->cnf['additional'] = array();
// ldap extension is needed
if (!function_exists('ldap_connect')) {
if ($this->cnf['debug'])
msg("LDAP err: PHP LDAP extension not found.",-1);
$this->success = false;
return;
}
// Prepare SSO
if($_SERVER['REMOTE_USER'] && $this->cnf['sso']){
// remove possible NTLM domain
list($dom,$usr) = explode('\\',$_SERVER['REMOTE_USER'],2);
if(!$usr) $usr = $dom;
// remove possible Kerberos domain
list($usr,$dom) = explode('@',$usr);
$dom = strtolower($dom);
$_SERVER['REMOTE_USER'] = $usr;
// we need to simulate a login
if(empty($_COOKIE[DOKU_COOKIE])){
$_REQUEST['u'] = $_SERVER['REMOTE_USER'];
$_REQUEST['p'] = 'sso_only';
}
}
// prepare adLDAP standard configuration
// add possible domain specific configuration
if($dom && is_array($this->cnf[$dom])) foreach($this->cnf[$dom] as $key => $val){
$this->opts['domain_controllers'] = explode(',',$this->opts['domain_controllers']);
$this->opts['domain_controllers'] = array_map('trim',$this->opts['domain_controllers']);
$this->opts['domain_controllers'] = array_filter($this->opts['domain_controllers']);
// we currently just handle authentication, so no capabilities are set
}
/**
* Check user+password [required auth function]
*
* Checks if the given user exists and the given
* plaintext password is correct by trying to bind
* to the LDAP server
*
* @author James Van Lommel <james@nosq.com>
* @return bool
*/
function checkPass($user, $pass){
if($_SERVER['REMOTE_USER'] &&
$_SERVER['REMOTE_USER'] == $user &&
$this->cnf['sso']) return true;
if(!$this->_init()) return false;
return $this->adldap->authenticate($user, $pass);
}
/**
* Return user info [required auth function]
*
* Returns info about the given user needs to contain
* at least these fields:
*
* name string full name of the user
* mail string email addres of the user
* grps array list of groups the user is in
*
* This LDAP specific function returns the following
* addional fields:
*
* dn string distinguished name (DN)
* uid string Posix User ID
*
* @author James Van Lommel <james@nosq.com>
*/
function getUserData($user){
global $conf;
if(!$this->_init()) return false;
$fields = array('mail','displayname','samaccountname');
// add additional fields to read
$fields = array_merge($fields, $this->cnf['additional']);
$fields = array_unique($fields);
//get info for given user
$result = $this->adldap->user_info($user, $fields);
//general user info
$info['name'] = $result[0]['displayname'][0];
$info['mail'] = $result[0]['mail'][0];
$info['uid'] = $result[0]['samaccountname'][0];
$info['dn'] = $result[0]['dn'];
// additional informations
foreach ($this->cnf['additional'] as $field) {
if (isset($result[0][strtolower($field)])) {
$info[$field] = $result[0][strtolower($field)][0];
}
}
// handle ActiveDirectory memberOf
$info['grps'] = $this->adldap->user_groups($user,(bool) $this->opts['recursive_groups']);
if (is_array($info['grps'])) {
foreach ($info['grps'] as $ndx => $group) {
$info['grps'][$ndx] = $this->cleanGroup($group);
}
}
// always add the default group to the list of groups
if(!is_array($info['grps']) || !in_array($conf['defaultgroup'],$info['grps'])){
$info['grps'][] = $conf['defaultgroup'];
}
return $info;
}
/**
* Make AD group names usable by DokuWiki.
*
* Removes backslashes ('\'), pound signs ('#'), and converts spaces to underscores.
*
* @author James Van Lommel (jamesvl@gmail.com)
$sName = str_replace('\\', '', $name);
$sName = str_replace('#', '', $sName);
$sName = preg_replace('[\s]', '_', $sName);
return $sName;
}
/**
* Sanitize user names
*/
function cleanUser($name) {
return $this->cleanGroup($name);
}
/**
* Most values in LDAP are case-insensitive
*/
function isCaseSensitive(){
return false;
}
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/**
* Bulk retrieval of user data
*
* @author Dominik Eckelmann <dokuwiki@cosmocode.de>
* @param start index of first user to be returned
* @param limit max number of users to be returned
* @param filter array of field/pattern pairs, null for no filter
* @return array of userinfo (refer getUserData for internal userinfo details)
*/
function retrieveUsers($start=0,$limit=-1,$filter=array()) {
if(!$this->_init()) return false;
if ($this->users === null) {
//get info for given user
$result = $this->adldap->all_users();
if (!$result) return array();
$this->users = array_fill_keys($result, false);
}
$i = 0;
$count = 0;
$this->_constructPattern($filter);
$result = array();
foreach ($this->users as $user => &$info) {
if ($i++ < $start) {
continue;
}
if ($info === false) {
$info = $this->getUserData($user);
}
if ($this->_filter($user, $info)) {
$result[$user] = $info;
if (($limit >= 0) && (++$count >= $limit)) break;
}
}
return $result;
}
/**
* Initialize the AdLDAP library and connect to the server
*/
function _init(){
if(!is_null($this->adldap)) return true;
// connect
try {
if (isset($this->opts['ad_username']) && isset($this->opts['ad_password'])) {
$this->canDo['getUsers'] = true;
}
return true;
} catch (adLDAPException $e) {
if ($this->cnf['debug']) {
msg($e->getMessage(), -1);
}
$this->success = false;
$this->adldap = null;
}
return false;
}
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/**
* return 1 if $user + $info match $filter criteria, 0 otherwise
*
* @author Chris Smith <chris@jalakai.co.uk>
*/
function _filter($user, $info) {
foreach ($this->_pattern as $item => $pattern) {
if ($item == 'user') {
if (!preg_match($pattern, $user)) return 0;
} else if ($item == 'grps') {
if (!count(preg_grep($pattern, $info['grps']))) return 0;
} else {
if (!preg_match($pattern, $info[$item])) return 0;
}
}
return 1;
}
function _constructPattern($filter) {
$this->_pattern = array();
foreach ($filter as $item => $pattern) {
// $this->_pattern[$item] = '/'.preg_quote($pattern,"/").'/i'; // don't allow regex characters
$this->_pattern[$item] = '/'.str_replace('/','\/',$pattern).'/i'; // allow regex characters
}
}
}
//Setup VIM: ex: et ts=4 enc=utf-8 :