1f4476bd9SJan Schumann<?php 293497020SAndreas Gohr 38553d24dSAndreas Gohruse dokuwiki\Extension\AuthPlugin; 493497020SAndreas Gohruse dokuwiki\Logger; 50489c64bSMoisés Braga Ribeirouse dokuwiki\Utf8\Sort; 6f4476bd9SJan Schumann 7f4476bd9SJan Schumann/** 8f4476bd9SJan Schumann * Plaintext authentication backend 9f4476bd9SJan Schumann * 10f4476bd9SJan Schumann * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 11f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 12f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 13f4476bd9SJan Schumann * @author Jan Schumann <js@schumann-it.com> 14f4476bd9SJan Schumann */ 158553d24dSAndreas Gohrclass auth_plugin_authplain extends AuthPlugin 165aa905e9SAndreas Gohr{ 17311f4603SAndreas Gohr /** @var array user cache */ 18ab9790caSAndreas Gohr protected $users; 19311f4603SAndreas Gohr 20311f4603SAndreas Gohr /** @var array filter pattern */ 21ab9790caSAndreas Gohr protected $pattern = []; 22f4476bd9SJan Schumann 236c8c1f46SChristopher Smith /** @var bool safe version of preg_split */ 245aa905e9SAndreas Gohr protected $pregsplit_safe = false; 256c8c1f46SChristopher Smith 26f4476bd9SJan Schumann /** 27f4476bd9SJan Schumann * Constructor 28f4476bd9SJan Schumann * 29f4476bd9SJan Schumann * Carry out sanity checks to ensure the object is 30f4476bd9SJan Schumann * able to operate. Set capabilities. 31f4476bd9SJan Schumann * 32f4476bd9SJan Schumann * @author Christopher Smith <chris@jalakai.co.uk> 33f4476bd9SJan Schumann */ 345aa905e9SAndreas Gohr public function __construct() 355aa905e9SAndreas Gohr { 36454d868bSAndreas Gohr parent::__construct(); 37f4476bd9SJan Schumann global $config_cascade; 38f4476bd9SJan Schumann 39f4476bd9SJan Schumann if (!@is_readable($config_cascade['plainauth.users']['default'])) { 40f4476bd9SJan Schumann $this->success = false; 41f4476bd9SJan Schumann } else { 42f4476bd9SJan Schumann if (@is_writable($config_cascade['plainauth.users']['default'])) { 43f4476bd9SJan Schumann $this->cando['addUser'] = true; 44f4476bd9SJan Schumann $this->cando['delUser'] = true; 45f4476bd9SJan Schumann $this->cando['modLogin'] = true; 46f4476bd9SJan Schumann $this->cando['modPass'] = true; 47f4476bd9SJan Schumann $this->cando['modName'] = true; 48f4476bd9SJan Schumann $this->cando['modMail'] = true; 49f4476bd9SJan Schumann $this->cando['modGroups'] = true; 50f4476bd9SJan Schumann } 51f4476bd9SJan Schumann $this->cando['getUsers'] = true; 52f4476bd9SJan Schumann $this->cando['getUserCount'] = true; 53b2fcc742SAnna Dabrowska $this->cando['getGroups'] = true; 54f4476bd9SJan Schumann } 55f4476bd9SJan Schumann } 56f4476bd9SJan Schumann 57f4476bd9SJan Schumann /** 58311f4603SAndreas Gohr * Check user+password 59f4476bd9SJan Schumann * 60f4476bd9SJan Schumann * Checks if the given user exists and the given 61f4476bd9SJan Schumann * plaintext password is correct 62f4476bd9SJan Schumann * 63f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 64311f4603SAndreas Gohr * @param string $user 65311f4603SAndreas Gohr * @param string $pass 66f4476bd9SJan Schumann * @return bool 67f4476bd9SJan Schumann */ 685aa905e9SAndreas Gohr public function checkPass($user, $pass) 695aa905e9SAndreas Gohr { 70f4476bd9SJan Schumann $userinfo = $this->getUserData($user); 71*9c952d3bSAndreas Gohr if ($userinfo === false) { 72*9c952d3bSAndreas Gohr auth_cryptPassword('dummy'); // run a crypt op to prevent timing attacks 73*9c952d3bSAndreas Gohr return false; 74*9c952d3bSAndreas Gohr } 75f4476bd9SJan Schumann 76f4476bd9SJan Schumann return auth_verifyPassword($pass, $this->users[$user]['pass']); 77f4476bd9SJan Schumann } 78f4476bd9SJan Schumann 79f4476bd9SJan Schumann /** 80f4476bd9SJan Schumann * Return user info 81f4476bd9SJan Schumann * 82f4476bd9SJan Schumann * Returns info about the given user needs to contain 83f4476bd9SJan Schumann * at least these fields: 84f4476bd9SJan Schumann * 85f4476bd9SJan Schumann * name string full name of the user 86f4476bd9SJan Schumann * mail string email addres of the user 87f4476bd9SJan Schumann * grps array list of groups the user is in 88f4476bd9SJan Schumann * 89f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 90311f4603SAndreas Gohr * @param string $user 912046a654SChristopher Smith * @param bool $requireGroups (optional) ignored by this plugin, grps info always supplied 92253d4b48SGerrit Uitslag * @return array|false 93f4476bd9SJan Schumann */ 945aa905e9SAndreas Gohr public function getUserData($user, $requireGroups = true) 955aa905e9SAndreas Gohr { 965aa905e9SAndreas Gohr if ($this->users === null) $this->loadUserData(); 97ab9790caSAndreas Gohr return $this->users[$user] ?? false; 98f4476bd9SJan Schumann } 99f4476bd9SJan Schumann 100f4476bd9SJan Schumann /** 101f95ecbbfSAngus Gratton * Creates a string suitable for saving as a line 102f95ecbbfSAngus Gratton * in the file database 103f95ecbbfSAngus Gratton * (delimiters escaped, etc.) 104f95ecbbfSAngus Gratton * 105f95ecbbfSAngus Gratton * @param string $user 106f95ecbbfSAngus Gratton * @param string $pass 107f95ecbbfSAngus Gratton * @param string $name 108f95ecbbfSAngus Gratton * @param string $mail 109f95ecbbfSAngus Gratton * @param array $grps list of groups the user is in 110f95ecbbfSAngus Gratton * @return string 111f95ecbbfSAngus Gratton */ 1125aa905e9SAndreas Gohr protected function createUserLine($user, $pass, $name, $mail, $grps) 1135aa905e9SAndreas Gohr { 114ab9790caSAndreas Gohr $groups = implode(',', $grps); 115ab9790caSAndreas Gohr $userline = [$user, $pass, $name, $mail, $groups]; 116f95ecbbfSAngus Gratton $userline = str_replace('\\', '\\\\', $userline); // escape \ as \\ 117f95ecbbfSAngus Gratton $userline = str_replace(':', '\\:', $userline); // escape : as \: 118b346670eSAndreas Gohr $userline = str_replace('#', '\\#', $userline); // escape # as \ 119ab9790caSAndreas Gohr $userline = implode(':', $userline) . "\n"; 120f95ecbbfSAngus Gratton return $userline; 121f95ecbbfSAngus Gratton } 122f95ecbbfSAngus Gratton 123f95ecbbfSAngus Gratton /** 124f4476bd9SJan Schumann * Create a new User 125f4476bd9SJan Schumann * 126f4476bd9SJan Schumann * Returns false if the user already exists, null when an error 127f4476bd9SJan Schumann * occurred and true if everything went well. 128f4476bd9SJan Schumann * 129f4476bd9SJan Schumann * The new user will be added to the default group by this 130f4476bd9SJan Schumann * function if grps are not specified (default behaviour). 131f4476bd9SJan Schumann * 132f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 133f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 134311f4603SAndreas Gohr * 135311f4603SAndreas Gohr * @param string $user 136311f4603SAndreas Gohr * @param string $pwd 137311f4603SAndreas Gohr * @param string $name 138311f4603SAndreas Gohr * @param string $mail 139311f4603SAndreas Gohr * @param array $grps 140311f4603SAndreas Gohr * @return bool|null|string 141f4476bd9SJan Schumann */ 1425aa905e9SAndreas Gohr public function createUser($user, $pwd, $name, $mail, $grps = null) 1435aa905e9SAndreas Gohr { 144f4476bd9SJan Schumann global $conf; 145f4476bd9SJan Schumann global $config_cascade; 146f4476bd9SJan Schumann 147f4476bd9SJan Schumann // user mustn't already exist 148db9faf02SPatrick Brown if ($this->getUserData($user) !== false) { 149db9faf02SPatrick Brown msg($this->getLang('userexists'), -1); 150db9faf02SPatrick Brown return false; 151db9faf02SPatrick Brown } 152f4476bd9SJan Schumann 153f4476bd9SJan Schumann $pass = auth_cryptPassword($pwd); 154f4476bd9SJan Schumann 155f4476bd9SJan Schumann // set default group if no groups specified 156ab9790caSAndreas Gohr if (!is_array($grps)) $grps = [$conf['defaultgroup']]; 157f4476bd9SJan Schumann 158f4476bd9SJan Schumann // prepare user line 1595aa905e9SAndreas Gohr $userline = $this->createUserLine($user, $pass, $name, $mail, $grps); 160f4476bd9SJan Schumann 161db9faf02SPatrick Brown if (!io_saveFile($config_cascade['plainauth.users']['default'], $userline, true)) { 162db9faf02SPatrick Brown msg($this->getLang('writefail'), -1); 163db9faf02SPatrick Brown return null; 164f4476bd9SJan Schumann } 165f4476bd9SJan Schumann 166ab9790caSAndreas Gohr $this->users[$user] = [ 167ab9790caSAndreas Gohr 'pass' => $pass, 168ab9790caSAndreas Gohr 'name' => $name, 169ab9790caSAndreas Gohr 'mail' => $mail, 170ab9790caSAndreas Gohr 'grps' => $grps 171ab9790caSAndreas Gohr ]; 172db9faf02SPatrick Brown return $pwd; 173f4476bd9SJan Schumann } 174f4476bd9SJan Schumann 175f4476bd9SJan Schumann /** 176f4476bd9SJan Schumann * Modify user data 177f4476bd9SJan Schumann * 178f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 179311f4603SAndreas Gohr * @param string $user nick of the user to be changed 180311f4603SAndreas Gohr * @param array $changes array of field/value pairs to be changed (password will be clear text) 181f4476bd9SJan Schumann * @return bool 182f4476bd9SJan Schumann */ 1835aa905e9SAndreas Gohr public function modifyUser($user, $changes) 1845aa905e9SAndreas Gohr { 185f4476bd9SJan Schumann global $ACT; 186f4476bd9SJan Schumann global $config_cascade; 187f4476bd9SJan Schumann 188f4476bd9SJan Schumann // sanity checks, user must already exist and there must be something to change 189db9faf02SPatrick Brown if (($userinfo = $this->getUserData($user)) === false) { 190db9faf02SPatrick Brown msg($this->getLang('usernotexists'), -1); 191db9faf02SPatrick Brown return false; 192db9faf02SPatrick Brown } 19342cbd322SAndreas Gohr 19442cbd322SAndreas Gohr // don't modify protected users 19542cbd322SAndreas Gohr if (!empty($userinfo['protected'])) { 19642cbd322SAndreas Gohr msg(sprintf($this->getLang('protected'), hsc($user)), -1); 19742cbd322SAndreas Gohr return false; 19842cbd322SAndreas Gohr } 19942cbd322SAndreas Gohr 200ab9790caSAndreas Gohr if (!is_array($changes) || $changes === []) return true; 201f4476bd9SJan Schumann 202f4476bd9SJan Schumann // update userinfo with new data, remembering to encrypt any password 203f4476bd9SJan Schumann $newuser = $user; 204f4476bd9SJan Schumann foreach ($changes as $field => $value) { 205f4476bd9SJan Schumann if ($field == 'user') { 206f4476bd9SJan Schumann $newuser = $value; 207f4476bd9SJan Schumann continue; 208f4476bd9SJan Schumann } 209f4476bd9SJan Schumann if ($field == 'pass') $value = auth_cryptPassword($value); 210f4476bd9SJan Schumann $userinfo[$field] = $value; 211f4476bd9SJan Schumann } 212f4476bd9SJan Schumann 2135aa905e9SAndreas Gohr $userline = $this->createUserLine( 21464159a61SAndreas Gohr $newuser, 21564159a61SAndreas Gohr $userinfo['pass'], 21664159a61SAndreas Gohr $userinfo['name'], 21764159a61SAndreas Gohr $userinfo['mail'], 21864159a61SAndreas Gohr $userinfo['grps'] 21964159a61SAndreas Gohr ); 220f4476bd9SJan Schumann 221699e3c49SPatrick Brown if (!io_replaceInFile($config_cascade['plainauth.users']['default'], '/^' . $user . ':/', $userline, true)) { 222699e3c49SPatrick Brown msg('There was an error modifying your user data. You may need to register again.', -1); 223699e3c49SPatrick Brown // FIXME, io functions should be fail-safe so existing data isn't lost 224311f4603SAndreas Gohr $ACT = 'register'; 225f4476bd9SJan Schumann return false; 226f4476bd9SJan Schumann } 227f4476bd9SJan Schumann 22859440086SAndreas Gohr if (isset($this->users[$user])) unset($this->users[$user]); 229f4476bd9SJan Schumann $this->users[$newuser] = $userinfo; 230f4476bd9SJan Schumann return true; 231f4476bd9SJan Schumann } 232f4476bd9SJan Schumann 233f4476bd9SJan Schumann /** 234f4476bd9SJan Schumann * Remove one or more users from the list of registered users 235f4476bd9SJan Schumann * 236f4476bd9SJan Schumann * @author Christopher Smith <chris@jalakai.co.uk> 237f4476bd9SJan Schumann * @param array $users array of users to be deleted 238f4476bd9SJan Schumann * @return int the number of users deleted 239f4476bd9SJan Schumann */ 2405aa905e9SAndreas Gohr public function deleteUsers($users) 2415aa905e9SAndreas Gohr { 242f4476bd9SJan Schumann global $config_cascade; 243f4476bd9SJan Schumann 244ab9790caSAndreas Gohr if (!is_array($users) || $users === []) return 0; 245f4476bd9SJan Schumann 2465aa905e9SAndreas Gohr if ($this->users === null) $this->loadUserData(); 247f4476bd9SJan Schumann 248ab9790caSAndreas Gohr $deleted = []; 249f4476bd9SJan Schumann foreach ($users as $user) { 25042cbd322SAndreas Gohr // don't delete protected users 25142cbd322SAndreas Gohr if (!empty($this->users[$user]['protected'])) { 25242cbd322SAndreas Gohr msg(sprintf($this->getLang('protected'), hsc($user)), -1); 25342cbd322SAndreas Gohr continue; 25442cbd322SAndreas Gohr } 255f4476bd9SJan Schumann if (isset($this->users[$user])) $deleted[] = preg_quote($user, '/'); 256f4476bd9SJan Schumann } 257f4476bd9SJan Schumann 258ab9790caSAndreas Gohr if ($deleted === []) return 0; 259f4476bd9SJan Schumann 260ab9790caSAndreas Gohr $pattern = '/^(' . implode('|', $deleted) . '):/'; 261db9faf02SPatrick Brown if (!io_deleteFromFile($config_cascade['plainauth.users']['default'], $pattern, true)) { 262db9faf02SPatrick Brown msg($this->getLang('writefail'), -1); 263db9faf02SPatrick Brown return 0; 264db9faf02SPatrick Brown } 265f4476bd9SJan Schumann 2669d24536dSAndreas Gohr // reload the user list and count the difference 267f4476bd9SJan Schumann $count = count($this->users); 2685aa905e9SAndreas Gohr $this->loadUserData(); 269f4476bd9SJan Schumann $count -= count($this->users); 270f4476bd9SJan Schumann return $count; 271f4476bd9SJan Schumann } 272f4476bd9SJan Schumann 273f4476bd9SJan Schumann /** 274f4476bd9SJan Schumann * Return a count of the number of user which meet $filter criteria 275f4476bd9SJan Schumann * 276f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 277311f4603SAndreas Gohr * 278311f4603SAndreas Gohr * @param array $filter 279311f4603SAndreas Gohr * @return int 280f4476bd9SJan Schumann */ 281ab9790caSAndreas Gohr public function getUserCount($filter = []) 2825aa905e9SAndreas Gohr { 283f4476bd9SJan Schumann 2845aa905e9SAndreas Gohr if ($this->users === null) $this->loadUserData(); 285f4476bd9SJan Schumann 286ab9790caSAndreas Gohr if ($filter === []) return count($this->users); 287f4476bd9SJan Schumann 288f4476bd9SJan Schumann $count = 0; 2895aa905e9SAndreas Gohr $this->constructPattern($filter); 290f4476bd9SJan Schumann 291f4476bd9SJan Schumann foreach ($this->users as $user => $info) { 2925aa905e9SAndreas Gohr $count += $this->filter($user, $info); 293f4476bd9SJan Schumann } 294f4476bd9SJan Schumann 295f4476bd9SJan Schumann return $count; 296f4476bd9SJan Schumann } 297f4476bd9SJan Schumann 298f4476bd9SJan Schumann /** 299f4476bd9SJan Schumann * Bulk retrieval of user data 300f4476bd9SJan Schumann * 301f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 302311f4603SAndreas Gohr * 303311f4603SAndreas Gohr * @param int $start index of first user to be returned 304311f4603SAndreas Gohr * @param int $limit max number of users to be returned 305311f4603SAndreas Gohr * @param array $filter array of field/pattern pairs 306311f4603SAndreas Gohr * @return array userinfo (refer getUserData for internal userinfo details) 307f4476bd9SJan Schumann */ 308ab9790caSAndreas Gohr public function retrieveUsers($start = 0, $limit = 0, $filter = []) 3095aa905e9SAndreas Gohr { 310f4476bd9SJan Schumann 3115aa905e9SAndreas Gohr if ($this->users === null) $this->loadUserData(); 312f4476bd9SJan Schumann 3130489c64bSMoisés Braga Ribeiro Sort::ksort($this->users); 314f4476bd9SJan Schumann 315f4476bd9SJan Schumann $i = 0; 316f4476bd9SJan Schumann $count = 0; 317ab9790caSAndreas Gohr $out = []; 3185aa905e9SAndreas Gohr $this->constructPattern($filter); 319f4476bd9SJan Schumann 320f4476bd9SJan Schumann foreach ($this->users as $user => $info) { 3215aa905e9SAndreas Gohr if ($this->filter($user, $info)) { 322f4476bd9SJan Schumann if ($i >= $start) { 323f4476bd9SJan Schumann $out[$user] = $info; 324f4476bd9SJan Schumann $count++; 325f4476bd9SJan Schumann if (($limit > 0) && ($count >= $limit)) break; 326f4476bd9SJan Schumann } 327f4476bd9SJan Schumann $i++; 328f4476bd9SJan Schumann } 329f4476bd9SJan Schumann } 330f4476bd9SJan Schumann 331f4476bd9SJan Schumann return $out; 332f4476bd9SJan Schumann } 333f4476bd9SJan Schumann 334f4476bd9SJan Schumann /** 335b2fcc742SAnna Dabrowska * Retrieves groups. 336b2fcc742SAnna Dabrowska * Loads complete user data into memory before searching for groups. 337b2fcc742SAnna Dabrowska * 338b2fcc742SAnna Dabrowska * @param int $start index of first group to be returned 339b2fcc742SAnna Dabrowska * @param int $limit max number of groups to be returned 340b2fcc742SAnna Dabrowska * @return array 341b2fcc742SAnna Dabrowska */ 342b2fcc742SAnna Dabrowska public function retrieveGroups($start = 0, $limit = 0) 343b2fcc742SAnna Dabrowska { 344b2fcc742SAnna Dabrowska $groups = []; 345b2fcc742SAnna Dabrowska 34642c62e55SAndreas Gohr if ($this->users === null) $this->loadUserData(); 347ab9790caSAndreas Gohr foreach ($this->users as $info) { 348b2fcc742SAnna Dabrowska $groups = array_merge($groups, array_diff($info['grps'], $groups)); 349b2fcc742SAnna Dabrowska } 3500489c64bSMoisés Braga Ribeiro Sort::ksort($groups); 351b2fcc742SAnna Dabrowska 352b2fcc742SAnna Dabrowska if ($limit > 0) { 353b2fcc742SAnna Dabrowska return array_splice($groups, $start, $limit); 354b2fcc742SAnna Dabrowska } 355b2fcc742SAnna Dabrowska return array_splice($groups, $start); 356b2fcc742SAnna Dabrowska } 357b2fcc742SAnna Dabrowska 358b2fcc742SAnna Dabrowska /** 359f4476bd9SJan Schumann * Only valid pageid's (no namespaces) for usernames 360311f4603SAndreas Gohr * 361311f4603SAndreas Gohr * @param string $user 362311f4603SAndreas Gohr * @return string 363f4476bd9SJan Schumann */ 3645aa905e9SAndreas Gohr public function cleanUser($user) 3655aa905e9SAndreas Gohr { 366f4476bd9SJan Schumann global $conf; 3675f18fdf3SAndreas Gohr 3685f18fdf3SAndreas Gohr return cleanID(str_replace([':', '/', ';'], $conf['sepchar'], $user)); 369f4476bd9SJan Schumann } 370f4476bd9SJan Schumann 371f4476bd9SJan Schumann /** 372f4476bd9SJan Schumann * Only valid pageid's (no namespaces) for groupnames 373311f4603SAndreas Gohr * 374311f4603SAndreas Gohr * @param string $group 375311f4603SAndreas Gohr * @return string 376f4476bd9SJan Schumann */ 3775aa905e9SAndreas Gohr public function cleanGroup($group) 3785aa905e9SAndreas Gohr { 379f4476bd9SJan Schumann global $conf; 3805f18fdf3SAndreas Gohr 3815f18fdf3SAndreas Gohr return cleanID(str_replace([':', '/', ';'], $conf['sepchar'], $group)); 382f4476bd9SJan Schumann } 383f4476bd9SJan Schumann 384f4476bd9SJan Schumann /** 385f4476bd9SJan Schumann * Load all user data 386f4476bd9SJan Schumann * 387f4476bd9SJan Schumann * loads the user file into a datastructure 388f4476bd9SJan Schumann * 389f4476bd9SJan Schumann * @author Andreas Gohr <andi@splitbrain.org> 390f4476bd9SJan Schumann */ 3915aa905e9SAndreas Gohr protected function loadUserData() 3925aa905e9SAndreas Gohr { 393f4476bd9SJan Schumann global $config_cascade; 394f4476bd9SJan Schumann 3955aa905e9SAndreas Gohr $this->users = $this->readUserFile($config_cascade['plainauth.users']['default']); 396f4476bd9SJan Schumann 39742cbd322SAndreas Gohr // support protected users 39842cbd322SAndreas Gohr if (!empty($config_cascade['plainauth.users']['protected'])) { 3995aa905e9SAndreas Gohr $protected = $this->readUserFile($config_cascade['plainauth.users']['protected']); 40042cbd322SAndreas Gohr foreach (array_keys($protected) as $key) { 40142cbd322SAndreas Gohr $protected[$key]['protected'] = true; 40242cbd322SAndreas Gohr } 40342cbd322SAndreas Gohr $this->users = array_merge($this->users, $protected); 40442cbd322SAndreas Gohr } 40542cbd322SAndreas Gohr } 406f4476bd9SJan Schumann 40742cbd322SAndreas Gohr /** 40842cbd322SAndreas Gohr * Read user data from given file 40942cbd322SAndreas Gohr * 41042cbd322SAndreas Gohr * ignores non existing files 41142cbd322SAndreas Gohr * 41242cbd322SAndreas Gohr * @param string $file the file to load data from 41342cbd322SAndreas Gohr * @return array 41442cbd322SAndreas Gohr */ 4155aa905e9SAndreas Gohr protected function readUserFile($file) 4165aa905e9SAndreas Gohr { 417ab9790caSAndreas Gohr $users = []; 41842cbd322SAndreas Gohr if (!file_exists($file)) return $users; 41942cbd322SAndreas Gohr 42042cbd322SAndreas Gohr $lines = file($file); 421f4476bd9SJan Schumann foreach ($lines as $line) { 422b346670eSAndreas Gohr $line = preg_replace('/(?<!\\\\)#.*$/', '', $line); //ignore comments (unless escaped) 423f4476bd9SJan Schumann $line = trim($line); 424f4476bd9SJan Schumann if (empty($line)) continue; 425f4476bd9SJan Schumann 4265aa905e9SAndreas Gohr $row = $this->splitUserData($line); 427f95ecbbfSAngus Gratton $row = str_replace('\\:', ':', $row); 428f95ecbbfSAngus Gratton $row = str_replace('\\\\', '\\', $row); 429b346670eSAndreas Gohr $row = str_replace('\\#', '#', $row); 430f95ecbbfSAngus Gratton 431f4476bd9SJan Schumann $groups = array_values(array_filter(explode(",", $row[4]))); 432f4476bd9SJan Schumann 43342cbd322SAndreas Gohr $users[$row[0]]['pass'] = $row[1]; 43442cbd322SAndreas Gohr $users[$row[0]]['name'] = urldecode($row[2]); 43542cbd322SAndreas Gohr $users[$row[0]]['mail'] = $row[3]; 43642cbd322SAndreas Gohr $users[$row[0]]['grps'] = $groups; 437f4476bd9SJan Schumann } 43842cbd322SAndreas Gohr return $users; 439f4476bd9SJan Schumann } 440f4476bd9SJan Schumann 4415aa905e9SAndreas Gohr /** 4425aa905e9SAndreas Gohr * Get the user line split into it's parts 4435aa905e9SAndreas Gohr * 4445aa905e9SAndreas Gohr * @param string $line 4455aa905e9SAndreas Gohr * @return string[] 4465aa905e9SAndreas Gohr */ 4475aa905e9SAndreas Gohr protected function splitUserData($line) 4485aa905e9SAndreas Gohr { 44993497020SAndreas Gohr $data = preg_split('/(?<![^\\\\]\\\\)\:/', $line, 5); // allow for : escaped as \: 45093497020SAndreas Gohr if (count($data) < 5) { 45193497020SAndreas Gohr $data = array_pad($data, 5, ''); 45293497020SAndreas Gohr Logger::error('User line with less than 5 fields. Possibly corruption in your user file', $data); 4536c8c1f46SChristopher Smith } 45493497020SAndreas Gohr return $data; 4556c8c1f46SChristopher Smith } 4566c8c1f46SChristopher Smith 457f4476bd9SJan Schumann /** 458311f4603SAndreas Gohr * return true if $user + $info match $filter criteria, false otherwise 459f4476bd9SJan Schumann * 460f4476bd9SJan Schumann * @author Chris Smith <chris@jalakai.co.uk> 461311f4603SAndreas Gohr * 462311f4603SAndreas Gohr * @param string $user User login 463311f4603SAndreas Gohr * @param array $info User's userinfo array 464311f4603SAndreas Gohr * @return bool 465f4476bd9SJan Schumann */ 4665aa905e9SAndreas Gohr protected function filter($user, $info) 4675aa905e9SAndreas Gohr { 4685aa905e9SAndreas Gohr foreach ($this->pattern as $item => $pattern) { 469f4476bd9SJan Schumann if ($item == 'user') { 470311f4603SAndreas Gohr if (!preg_match($pattern, $user)) return false; 471f4476bd9SJan Schumann } elseif ($item == 'grps') { 472311f4603SAndreas Gohr if (!count(preg_grep($pattern, $info['grps']))) return false; 473ab9790caSAndreas Gohr } elseif (!preg_match($pattern, $info[$item])) { 474ab9790caSAndreas Gohr return false; 475f4476bd9SJan Schumann } 476f4476bd9SJan Schumann } 477311f4603SAndreas Gohr return true; 478f4476bd9SJan Schumann } 479f4476bd9SJan Schumann 480311f4603SAndreas Gohr /** 481311f4603SAndreas Gohr * construct a filter pattern 482311f4603SAndreas Gohr * 483311f4603SAndreas Gohr * @param array $filter 484311f4603SAndreas Gohr */ 4855aa905e9SAndreas Gohr protected function constructPattern($filter) 4865aa905e9SAndreas Gohr { 487ab9790caSAndreas Gohr $this->pattern = []; 488f4476bd9SJan Schumann foreach ($filter as $item => $pattern) { 4895aa905e9SAndreas Gohr $this->pattern[$item] = '/' . str_replace('/', '\/', $pattern) . '/i'; // allow regex characters 490f4476bd9SJan Schumann } 491f4476bd9SJan Schumann } 492f4476bd9SJan Schumann} 493