1*f4476bd9SJan Schumann<?php 2*f4476bd9SJan Schumann/** 3*f4476bd9SJan Schumann * Plugin auth provider 4*f4476bd9SJan Schumann * 5*f4476bd9SJan Schumann * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6*f4476bd9SJan Schumann * @author Jan Schumann <js@schumann-it.com> 7*f4476bd9SJan Schumann */ 8*f4476bd9SJan Schumann// must be run within Dokuwiki 9*f4476bd9SJan Schumannif(!defined('DOKU_INC')) die(); 10*f4476bd9SJan Schumann 11*f4476bd9SJan Schumann/** 12*f4476bd9SJan Schumann * Plaintext authentication backend 13*f4476bd9SJan Schumann * 14*f4476bd9SJan Schumann * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 15*f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 16*f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 17*f4476bd9SJan Schumann * @author Jan Schumann <js@schumann-it.com> 18*f4476bd9SJan Schumann */ 19*f4476bd9SJan Schumannclass auth_plugin_authplain extends DokuWiki_Auth_Plugin 20*f4476bd9SJan Schumann{ 21*f4476bd9SJan Schumann var $users = null; 22*f4476bd9SJan Schumann var $_pattern = array(); 23*f4476bd9SJan Schumann 24*f4476bd9SJan Schumann /** 25*f4476bd9SJan Schumann * Constructor 26*f4476bd9SJan Schumann * 27*f4476bd9SJan Schumann * Carry out sanity checks to ensure the object is 28*f4476bd9SJan Schumann * able to operate. Set capabilities. 29*f4476bd9SJan Schumann * 30*f4476bd9SJan Schumann * @author Christopher Smith <chris@jalakai.co.uk> 31*f4476bd9SJan Schumann */ 32*f4476bd9SJan Schumann function auth_plugin_authplain() { 33*f4476bd9SJan Schumann global $config_cascade; 34*f4476bd9SJan Schumann 35*f4476bd9SJan Schumann if (!@is_readable($config_cascade['plainauth.users']['default'])){ 36*f4476bd9SJan Schumann $this->success = false; 37*f4476bd9SJan Schumann }else{ 38*f4476bd9SJan Schumann if(@is_writable($config_cascade['plainauth.users']['default'])){ 39*f4476bd9SJan Schumann $this->cando['addUser'] = true; 40*f4476bd9SJan Schumann $this->cando['delUser'] = true; 41*f4476bd9SJan Schumann $this->cando['modLogin'] = true; 42*f4476bd9SJan Schumann $this->cando['modPass'] = true; 43*f4476bd9SJan Schumann $this->cando['modName'] = true; 44*f4476bd9SJan Schumann $this->cando['modMail'] = true; 45*f4476bd9SJan Schumann $this->cando['modGroups'] = true; 46*f4476bd9SJan Schumann } 47*f4476bd9SJan Schumann $this->cando['getUsers'] = true; 48*f4476bd9SJan Schumann $this->cando['getUserCount'] = true; 49*f4476bd9SJan Schumann } 50*f4476bd9SJan Schumann } 51*f4476bd9SJan Schumann 52*f4476bd9SJan Schumann /** 53*f4476bd9SJan Schumann * Check user+password [required auth function] 54*f4476bd9SJan Schumann * 55*f4476bd9SJan Schumann * Checks if the given user exists and the given 56*f4476bd9SJan Schumann * plaintext password is correct 57*f4476bd9SJan Schumann * 58*f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 59*f4476bd9SJan Schumann * @return bool 60*f4476bd9SJan Schumann */ 61*f4476bd9SJan Schumann function checkPass($user,$pass){ 62*f4476bd9SJan Schumann 63*f4476bd9SJan Schumann $userinfo = $this->getUserData($user); 64*f4476bd9SJan Schumann if ($userinfo === false) return false; 65*f4476bd9SJan Schumann 66*f4476bd9SJan Schumann return auth_verifyPassword($pass,$this->users[$user]['pass']); 67*f4476bd9SJan Schumann } 68*f4476bd9SJan Schumann 69*f4476bd9SJan Schumann /** 70*f4476bd9SJan Schumann * Return user info 71*f4476bd9SJan Schumann * 72*f4476bd9SJan Schumann * Returns info about the given user needs to contain 73*f4476bd9SJan Schumann * at least these fields: 74*f4476bd9SJan Schumann * 75*f4476bd9SJan Schumann * name string full name of the user 76*f4476bd9SJan Schumann * mail string email addres of the user 77*f4476bd9SJan Schumann * grps array list of groups the user is in 78*f4476bd9SJan Schumann * 79*f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 80*f4476bd9SJan Schumann */ 81*f4476bd9SJan Schumann function getUserData($user){ 82*f4476bd9SJan Schumann 83*f4476bd9SJan Schumann if($this->users === null) $this->_loadUserData(); 84*f4476bd9SJan Schumann return isset($this->users[$user]) ? $this->users[$user] : false; 85*f4476bd9SJan Schumann } 86*f4476bd9SJan Schumann 87*f4476bd9SJan Schumann /** 88*f4476bd9SJan Schumann * Create a new User 89*f4476bd9SJan Schumann * 90*f4476bd9SJan Schumann * Returns false if the user already exists, null when an error 91*f4476bd9SJan Schumann * occurred and true if everything went well. 92*f4476bd9SJan Schumann * 93*f4476bd9SJan Schumann * The new user will be added to the default group by this 94*f4476bd9SJan Schumann * function if grps are not specified (default behaviour). 95*f4476bd9SJan Schumann * 96*f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 97*f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 98*f4476bd9SJan Schumann */ 99*f4476bd9SJan Schumann function createUser($user,$pwd,$name,$mail,$grps=null){ 100*f4476bd9SJan Schumann global $conf; 101*f4476bd9SJan Schumann global $config_cascade; 102*f4476bd9SJan Schumann 103*f4476bd9SJan Schumann // user mustn't already exist 104*f4476bd9SJan Schumann if ($this->getUserData($user) !== false) return false; 105*f4476bd9SJan Schumann 106*f4476bd9SJan Schumann $pass = auth_cryptPassword($pwd); 107*f4476bd9SJan Schumann 108*f4476bd9SJan Schumann // set default group if no groups specified 109*f4476bd9SJan Schumann if (!is_array($grps)) $grps = array($conf['defaultgroup']); 110*f4476bd9SJan Schumann 111*f4476bd9SJan Schumann // prepare user line 112*f4476bd9SJan Schumann $groups = join(',',$grps); 113*f4476bd9SJan Schumann $userline = join(':',array($user,$pass,$name,$mail,$groups))."\n"; 114*f4476bd9SJan Schumann 115*f4476bd9SJan Schumann if (io_saveFile($config_cascade['plainauth.users']['default'],$userline,true)) { 116*f4476bd9SJan Schumann $this->users[$user] = compact('pass','name','mail','grps'); 117*f4476bd9SJan Schumann return $pwd; 118*f4476bd9SJan Schumann } 119*f4476bd9SJan Schumann 120*f4476bd9SJan Schumann msg('The '.$config_cascade['plainauth.users']['default']. 121*f4476bd9SJan Schumann ' file is not writable. Please inform the Wiki-Admin',-1); 122*f4476bd9SJan Schumann return null; 123*f4476bd9SJan Schumann } 124*f4476bd9SJan Schumann 125*f4476bd9SJan Schumann /** 126*f4476bd9SJan Schumann * Modify user data 127*f4476bd9SJan Schumann * 128*f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 129*f4476bd9SJan Schumann * @param $user nick of the user to be changed 130*f4476bd9SJan Schumann * @param $changes array of field/value pairs to be changed (password will be clear text) 131*f4476bd9SJan Schumann * @return bool 132*f4476bd9SJan Schumann */ 133*f4476bd9SJan Schumann function modifyUser($user, $changes) { 134*f4476bd9SJan Schumann global $conf; 135*f4476bd9SJan Schumann global $ACT; 136*f4476bd9SJan Schumann global $INFO; 137*f4476bd9SJan Schumann global $config_cascade; 138*f4476bd9SJan Schumann 139*f4476bd9SJan Schumann // sanity checks, user must already exist and there must be something to change 140*f4476bd9SJan Schumann if (($userinfo = $this->getUserData($user)) === false) return false; 141*f4476bd9SJan Schumann if (!is_array($changes) || !count($changes)) return true; 142*f4476bd9SJan Schumann 143*f4476bd9SJan Schumann // update userinfo with new data, remembering to encrypt any password 144*f4476bd9SJan Schumann $newuser = $user; 145*f4476bd9SJan Schumann foreach ($changes as $field => $value) { 146*f4476bd9SJan Schumann if ($field == 'user') { 147*f4476bd9SJan Schumann $newuser = $value; 148*f4476bd9SJan Schumann continue; 149*f4476bd9SJan Schumann } 150*f4476bd9SJan Schumann if ($field == 'pass') $value = auth_cryptPassword($value); 151*f4476bd9SJan Schumann $userinfo[$field] = $value; 152*f4476bd9SJan Schumann } 153*f4476bd9SJan Schumann 154*f4476bd9SJan Schumann $groups = join(',',$userinfo['grps']); 155*f4476bd9SJan Schumann $userline = join(':',array($newuser, $userinfo['pass'], $userinfo['name'], $userinfo['mail'], $groups))."\n"; 156*f4476bd9SJan Schumann 157*f4476bd9SJan Schumann if (!$this->deleteUsers(array($user))) { 158*f4476bd9SJan Schumann msg('Unable to modify user data. Please inform the Wiki-Admin',-1); 159*f4476bd9SJan Schumann return false; 160*f4476bd9SJan Schumann } 161*f4476bd9SJan Schumann 162*f4476bd9SJan Schumann if (!io_saveFile($config_cascade['plainauth.users']['default'],$userline,true)) { 163*f4476bd9SJan Schumann msg('There was an error modifying your user data. You should register again.',-1); 164*f4476bd9SJan Schumann // FIXME, user has been deleted but not recreated, should force a logout and redirect to login page 165*f4476bd9SJan Schumann $ACT == 'register'; 166*f4476bd9SJan Schumann return false; 167*f4476bd9SJan Schumann } 168*f4476bd9SJan Schumann 169*f4476bd9SJan Schumann $this->users[$newuser] = $userinfo; 170*f4476bd9SJan Schumann return true; 171*f4476bd9SJan Schumann } 172*f4476bd9SJan Schumann 173*f4476bd9SJan Schumann /** 174*f4476bd9SJan Schumann * Remove one or more users from the list of registered users 175*f4476bd9SJan Schumann * 176*f4476bd9SJan Schumann * @author Christopher Smith <chris@jalakai.co.uk> 177*f4476bd9SJan Schumann * @param array $users array of users to be deleted 178*f4476bd9SJan Schumann * @return int the number of users deleted 179*f4476bd9SJan Schumann */ 180*f4476bd9SJan Schumann function deleteUsers($users) { 181*f4476bd9SJan Schumann global $config_cascade; 182*f4476bd9SJan Schumann 183*f4476bd9SJan Schumann if (!is_array($users) || empty($users)) return 0; 184*f4476bd9SJan Schumann 185*f4476bd9SJan Schumann if ($this->users === null) $this->_loadUserData(); 186*f4476bd9SJan Schumann 187*f4476bd9SJan Schumann $deleted = array(); 188*f4476bd9SJan Schumann foreach ($users as $user) { 189*f4476bd9SJan Schumann if (isset($this->users[$user])) $deleted[] = preg_quote($user,'/'); 190*f4476bd9SJan Schumann } 191*f4476bd9SJan Schumann 192*f4476bd9SJan Schumann if (empty($deleted)) return 0; 193*f4476bd9SJan Schumann 194*f4476bd9SJan Schumann $pattern = '/^('.join('|',$deleted).'):/'; 195*f4476bd9SJan Schumann 196*f4476bd9SJan Schumann if (io_deleteFromFile($config_cascade['plainauth.users']['default'],$pattern,true)) { 197*f4476bd9SJan Schumann foreach ($deleted as $user) unset($this->users[$user]); 198*f4476bd9SJan Schumann return count($deleted); 199*f4476bd9SJan Schumann } 200*f4476bd9SJan Schumann 201*f4476bd9SJan Schumann // problem deleting, reload the user list and count the difference 202*f4476bd9SJan Schumann $count = count($this->users); 203*f4476bd9SJan Schumann $this->_loadUserData(); 204*f4476bd9SJan Schumann $count -= count($this->users); 205*f4476bd9SJan Schumann return $count; 206*f4476bd9SJan Schumann } 207*f4476bd9SJan Schumann 208*f4476bd9SJan Schumann /** 209*f4476bd9SJan Schumann * Return a count of the number of user which meet $filter criteria 210*f4476bd9SJan Schumann * 211*f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 212*f4476bd9SJan Schumann */ 213*f4476bd9SJan Schumann function getUserCount($filter=array()) { 214*f4476bd9SJan Schumann 215*f4476bd9SJan Schumann if($this->users === null) $this->_loadUserData(); 216*f4476bd9SJan Schumann 217*f4476bd9SJan Schumann if (!count($filter)) return count($this->users); 218*f4476bd9SJan Schumann 219*f4476bd9SJan Schumann $count = 0; 220*f4476bd9SJan Schumann $this->_constructPattern($filter); 221*f4476bd9SJan Schumann 222*f4476bd9SJan Schumann foreach ($this->users as $user => $info) { 223*f4476bd9SJan Schumann $count += $this->_filter($user, $info); 224*f4476bd9SJan Schumann } 225*f4476bd9SJan Schumann 226*f4476bd9SJan Schumann return $count; 227*f4476bd9SJan Schumann } 228*f4476bd9SJan Schumann 229*f4476bd9SJan Schumann /** 230*f4476bd9SJan Schumann * Bulk retrieval of user data 231*f4476bd9SJan Schumann * 232*f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 233*f4476bd9SJan Schumann * @param start index of first user to be returned 234*f4476bd9SJan Schumann * @param limit max number of users to be returned 235*f4476bd9SJan Schumann * @param filter array of field/pattern pairs 236*f4476bd9SJan Schumann * @return array of userinfo (refer getUserData for internal userinfo details) 237*f4476bd9SJan Schumann */ 238*f4476bd9SJan Schumann function retrieveUsers($start=0,$limit=0,$filter=array()) { 239*f4476bd9SJan Schumann 240*f4476bd9SJan Schumann if ($this->users === null) $this->_loadUserData(); 241*f4476bd9SJan Schumann 242*f4476bd9SJan Schumann ksort($this->users); 243*f4476bd9SJan Schumann 244*f4476bd9SJan Schumann $i = 0; 245*f4476bd9SJan Schumann $count = 0; 246*f4476bd9SJan Schumann $out = array(); 247*f4476bd9SJan Schumann $this->_constructPattern($filter); 248*f4476bd9SJan Schumann 249*f4476bd9SJan Schumann foreach ($this->users as $user => $info) { 250*f4476bd9SJan Schumann if ($this->_filter($user, $info)) { 251*f4476bd9SJan Schumann if ($i >= $start) { 252*f4476bd9SJan Schumann $out[$user] = $info; 253*f4476bd9SJan Schumann $count++; 254*f4476bd9SJan Schumann if (($limit > 0) && ($count >= $limit)) break; 255*f4476bd9SJan Schumann } 256*f4476bd9SJan Schumann $i++; 257*f4476bd9SJan Schumann } 258*f4476bd9SJan Schumann } 259*f4476bd9SJan Schumann 260*f4476bd9SJan Schumann return $out; 261*f4476bd9SJan Schumann } 262*f4476bd9SJan Schumann 263*f4476bd9SJan Schumann /** 264*f4476bd9SJan Schumann * Only valid pageid's (no namespaces) for usernames 265*f4476bd9SJan Schumann */ 266*f4476bd9SJan Schumann function cleanUser($user){ 267*f4476bd9SJan Schumann global $conf; 268*f4476bd9SJan Schumann return cleanID(str_replace(':',$conf['sepchar'],$user)); 269*f4476bd9SJan Schumann } 270*f4476bd9SJan Schumann 271*f4476bd9SJan Schumann /** 272*f4476bd9SJan Schumann * Only valid pageid's (no namespaces) for groupnames 273*f4476bd9SJan Schumann */ 274*f4476bd9SJan Schumann function cleanGroup($group){ 275*f4476bd9SJan Schumann global $conf; 276*f4476bd9SJan Schumann return cleanID(str_replace(':',$conf['sepchar'],$group)); 277*f4476bd9SJan Schumann } 278*f4476bd9SJan Schumann 279*f4476bd9SJan Schumann /** 280*f4476bd9SJan Schumann * Load all user data 281*f4476bd9SJan Schumann * 282*f4476bd9SJan Schumann * loads the user file into a datastructure 283*f4476bd9SJan Schumann * 284*f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 285*f4476bd9SJan Schumann */ 286*f4476bd9SJan Schumann function _loadUserData(){ 287*f4476bd9SJan Schumann global $config_cascade; 288*f4476bd9SJan Schumann 289*f4476bd9SJan Schumann $this->users = array(); 290*f4476bd9SJan Schumann 291*f4476bd9SJan Schumann if(!@file_exists($config_cascade['plainauth.users']['default'])) return; 292*f4476bd9SJan Schumann 293*f4476bd9SJan Schumann $lines = file($config_cascade['plainauth.users']['default']); 294*f4476bd9SJan Schumann foreach($lines as $line){ 295*f4476bd9SJan Schumann $line = preg_replace('/#.*$/','',$line); //ignore comments 296*f4476bd9SJan Schumann $line = trim($line); 297*f4476bd9SJan Schumann if(empty($line)) continue; 298*f4476bd9SJan Schumann 299*f4476bd9SJan Schumann $row = explode(":",$line,5); 300*f4476bd9SJan Schumann $groups = array_values(array_filter(explode(",",$row[4]))); 301*f4476bd9SJan Schumann 302*f4476bd9SJan Schumann $this->users[$row[0]]['pass'] = $row[1]; 303*f4476bd9SJan Schumann $this->users[$row[0]]['name'] = urldecode($row[2]); 304*f4476bd9SJan Schumann $this->users[$row[0]]['mail'] = $row[3]; 305*f4476bd9SJan Schumann $this->users[$row[0]]['grps'] = $groups; 306*f4476bd9SJan Schumann } 307*f4476bd9SJan Schumann } 308*f4476bd9SJan Schumann 309*f4476bd9SJan Schumann /** 310*f4476bd9SJan Schumann * return 1 if $user + $info match $filter criteria, 0 otherwise 311*f4476bd9SJan Schumann * 312*f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 313*f4476bd9SJan Schumann */ 314*f4476bd9SJan Schumann function _filter($user, $info) { 315*f4476bd9SJan Schumann // FIXME 316*f4476bd9SJan Schumann foreach ($this->_pattern as $item => $pattern) { 317*f4476bd9SJan Schumann if ($item == 'user') { 318*f4476bd9SJan Schumann if (!preg_match($pattern, $user)) return 0; 319*f4476bd9SJan Schumann } else if ($item == 'grps') { 320*f4476bd9SJan Schumann if (!count(preg_grep($pattern, $info['grps']))) return 0; 321*f4476bd9SJan Schumann } else { 322*f4476bd9SJan Schumann if (!preg_match($pattern, $info[$item])) return 0; 323*f4476bd9SJan Schumann } 324*f4476bd9SJan Schumann } 325*f4476bd9SJan Schumann return 1; 326*f4476bd9SJan Schumann } 327*f4476bd9SJan Schumann 328*f4476bd9SJan Schumann function _constructPattern($filter) { 329*f4476bd9SJan Schumann $this->_pattern = array(); 330*f4476bd9SJan Schumann foreach ($filter as $item => $pattern) { 331*f4476bd9SJan Schumann// $this->_pattern[$item] = '/'.preg_quote($pattern,"/").'/i'; // don't allow regex characters 332*f4476bd9SJan Schumann $this->_pattern[$item] = '/'.str_replace('/','\/',$pattern).'/i'; // allow regex characters 333*f4476bd9SJan Schumann } 334*f4476bd9SJan Schumann } 335*f4476bd9SJan Schumann}