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