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