====== RADIUS Authentication Backend ======
Radius is a authentication and accounting protocol for large departments, somehow similar to LDAP. If your department uses RADIUS to manage accounts, you can reuse that accounts for your dokuwiki.
===== Requirements =====
* a radius server: host, port, sharedsecret
* perl (depends on distribution, could be part of the php-pear package)
* phpize (depends on distribution, could be part of the php-devel package)
* php radius extension. To install use
# pear install pecl/radius
===== Code =====
Save this under .../dokuwiki/inc/auth/radius.class.php:
*/
define('DOKU_AUTH', dirname(__FILE__));
require_once(DOKU_AUTH.'/plain.class.php');
// FIXME: needed?
// we only accept page ids for auth_plain
//if(isset($_REQUEST['u']))
// $_REQUEST['u'] = cleanID($_REQUEST['u']);
//if(isset($_REQUEST['acl_user']))
// $_REQUEST['acl_user'] = cleanID($_REQUEST['acl_user']);
//// the same goes for password reset requests
//if(isset($_POST['login'])){
// $_POST['login'] = cleanID($_POST['login']);
//}
// FIXME: better way to do this? must we support windows?
//load radius extension if required
if(!extension_loaded('radius')) {
if (preg_match('/windows/i', getenv('OS'))) {
dl('php_radius.dll');
} else {
dl('radius.so');
}
}
class auth_radius extends auth_plain {
/**
* handle to the radius server
*/
var $radius = null;
/**
* radius config
*/
var $cnf = null;
/**
* Constructor
*
* Carry out sanity checks to ensure the object is
* able to operate. Sets no capabilities because we only auhtenticate via radius
*
* @author Sebastian Menge
*/
function auth_radius() {
parent::auth_plain();
global $conf;
$this->cnf = $conf['auth']['radius'];
// radius extension is needed
if(!function_exists('radius_add_server')) {
if ($this->cnf['debug'])
msg("Radius err: PHP radius extension not found.",-1,__LINE__,__FILE__);
$this->success = false;
return;
}
// FIXME: defaults like this: (e.g. localhost:1812)
// if(empty($this->cnf['groupkey'])) $this->cnf['groupkey'] = 'cn';
// get parameters from config
$host = $this->cnf['host'];
$port = $this->cnf['port'];
$secret = $this->cnf['secret'];
$timeout = $this->cnf['timeout'];
$tries = $this->cnf['tries'];
//create handle and add server
$this->radius = radius_auth_open();
//try to connect
if (!radius_add_server($this->radius,$host,$port,$secret,$timeout,$tries)){
msg("Radius err: ". radius_strerror($this->radius),-1,__LINE__,__FILE__);
$this->success = false;
return;
}
// set additional capabilities
$this->cando['modLogin'] = false;
$this->cando['modPass'] = false;
// $this->cando['external'] = true; // does the module do external auth checking?
// $this->cando['logoff'] = false; // has the module some special logoff method?
return;
}
/**
* Check user+password [required auth function]
*
* Checks if the given user exists and the given
* plaintext password is correct
*
* @author Sebastian Menge
* @return bool
*/
function checkPass($user,$pass){
if (! radius_create_request($this->radius,RADIUS_ACCESS_REQUEST)) {
msg("Radius err: ". radius_strerror($this->radius),-1,__LINE__,__FILE__);
}
radius_put_attr($this->radius,RADIUS_USER_NAME,$user);
radius_put_attr($this->radius,RADIUS_USER_PASSWORD,$pass);
//send the actual request and return result
switch (radius_send_request($this->radius)) {
case RADIUS_ACCESS_ACCEPT:
$data = $this->getUserData($user);
return true;
break;
case RADIUS_ACCESS_REJECT:
return false;
break;
case RADIUS_ACCESS_CHALLENGE:
//msg("Radius: CHAP not supported by auth_radius.",-1);
return false;
break;
default:
msg('Radius Error: ('.$user.') ' . radius_strerror($this->radius),-1,__LINE__,__FILE__);
}
return false;
}
/**
* Return user info
*
* Returns info about the given user needs to contain
* at least these fields:
*
* name string full name of the user, for now just $user
* mail string email address of the user, for now $user@$mailhost.
* $mailhost is set in the config
* grps array list of groups the user is in, for now empty.
* Could be calculated from the username regexes in the config.
*
* @author Sebastian Menge
*/
function getUserData($user) {
if($this->users === null) parent::_loadUserData();
$plain_data = isset($this->users[$user]) ? $this->users[$user] : false;
if ($plain_data) {
$data['grps'] = $plain_data['grps'];
$data['mail'] = $plain_data['mail'];
$data['name'] = $plain_data['name'];
} else {
$data['grps'] = array();
$data['mail'] = $user.'@'.$this->cnf['mailhost'];
$data['name'] = $user." (".implode(", ",$data['grps']).")";
}
//if no email address is set, use login@mailhost
if (count ($data['mail']) == 0) {
$data['mail'] = $user.'@'.$this->cnf['mailhost'];
}
//if no groups are found place the user in 'defaultgroup'
if (count ($data['grps']) == 0) {
$data['grps'][] = $this->cnf['defaultgroup'];
}
return $data;
}
/**
* Return local user info
*
* Returns info about the given user from the local db.
* 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
*
* @author Andreas Gohr
*/
function getLocalUserData($user){
if($this->users === null) $this->_loadUserData();
return isset($this->users[$user]) ? $this->users[$user] : false;
}
/**
* Create a new User
*
* Returns false if the user already exists, null when an error
* occured and true if everything went well.
*
* The new user will be added to the default group by this
* function if grps are not specified (default behaviour).
*
* @author Andreas Gohr
* @author Chris Smith
*/
function createUser($user,$pwd,$name,$mail,$grps=null){
global $conf;
// user mustn't already exist
if ($this->getLocalUserData($user) !== false) return false;
$pass = "pass_set_by_radius";
// set default group if no groups specified
if (!is_array($grps)) $grps = array($conf['defaultgroup']);
// prepare user line
$groups = join(',',$grps);
$userline = join(':',array($user,$pass,$name,$mail,$groups))."\n";
if (io_saveFile(AUTH_USERFILE,$userline,true)) {
$this->users[$user] = compact('pass','name','mail','grps');
return $pwd;
}
msg('The '.AUTH_USERFILE.' file is not writable. Please inform the Wiki-Admin',-1);
return null;
}
}
//Setup VIM: ex: et ts=2 enc=utf-8 :
===== Configuration =====
//enable radius auth
$conf['authtype'] = 'radius';
//radius Config
$conf['auth']['radius']['host'] = 'radius.mycompany.com';
$conf['auth']['radius']['port'] = '0'; //0 reuses data from /etc/services
$conf['auth']['radius']['secret'] = 'radiussecret';
$conf['auth']['radius']['timeout'] = '3';
$conf['auth']['radius']['tries'] = '3';
$conf['auth']['radius']['mailhost'] = 'mail.mycompany.com';
Put the following in .../dokuwiki/lib/plugins/config/settings/config.metadata.php to edit the main settings it via the config plugin.
//radius-settings
$meta['auth____radius____host'] = array('string');
$meta['auth____radius____port'] = array('string');
$meta['auth____radius____timeout'] = array('string');
$meta['auth____radius____tries'] = array('string');
$meta['auth____radius____secret'] = array('password');
$meta['auth____radius____mailhost'] = array('string');
$meta['auth____radius____defaultgroup'] = array('string');
Users can be managed via the user-manager plugin now. The password of users.auth.php is ignored. Just put a dummy value in there.
Users have to change the lines
$this->cando['modLogin'] = false;
$this->cando['modPass'] = false;
such that the right capabilities are set. (set modLogin to true.)