Completed
Push — auth-external-fallback ( 81e999 )
by Henry
06:25 queued 03:39
created

auth.php ➔ auth_setup()   F

Complexity

Conditions 17
Paths 391

Size

Total Lines 86

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
nc 391
nop 0
dl 0
loc 86
rs 1.9021
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Authentication library
4
 *
5
 * Including this file will automatically try to login
6
 * a user by calling auth_login()
7
 *
8
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
9
 * @author     Andreas Gohr <[email protected]>
10
 */
11
12
// some ACL level defines
13
use dokuwiki\PassHash;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, PassHash.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
14
use dokuwiki\Subscriptions\RegistrationSubscriptionSender;
15
use dokuwiki\Extension\AuthPlugin;
16
use dokuwiki\Extension\PluginController;
17
use dokuwiki\Extension\Event;
18
19
define('AUTH_NONE', 0);
20
define('AUTH_READ', 1);
21
define('AUTH_EDIT', 2);
22
define('AUTH_CREATE', 4);
23
define('AUTH_UPLOAD', 8);
24
define('AUTH_DELETE', 16);
25
define('AUTH_ADMIN', 255);
26
27
/**
28
 * Initialize the auth system.
29
 *
30
 * This function is automatically called at the end of init.php
31
 *
32
 * This used to be the main() of the auth.php
33
 *
34
 * @todo backend loading maybe should be handled by the class autoloader
35
 * @todo maybe split into multiple functions at the XXX marked positions
36
 * @triggers AUTH_LOGIN_CHECK
37
 * @return bool
38
 */
39
function auth_setup() {
40
    global $conf;
41
    /* @var AuthPlugin $auth */
42
    global $auth;
43
    /* @var Input $INPUT */
44
    global $INPUT;
45
    global $AUTH_ACL;
46
    global $lang;
47
    /* @var PluginController $plugin_controller */
48
    global $plugin_controller;
49
    $AUTH_ACL = array();
50
51
    if(!$conf['useacl']) return false;
52
53
    // try to load auth backend from plugins
54
    foreach ($plugin_controller->getList('auth') as $plugin) {
55
        if ($conf['authtype'] === $plugin) {
56
            $auth = $plugin_controller->load('auth', $plugin);
57
            break;
58
        }
59
    }
60
61
    if(!isset($auth) || !$auth){
62
        msg($lang['authtempfail'], -1);
63
        return false;
64
    }
65
66
    if ($auth->success == false) {
0 ignored issues
show
Bug introduced by
Accessing success on the interface dokuwiki\Extension\PluginInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
67
        // degrade to unauthenticated user
68
        unset($auth);
69
        auth_logoff();
70
        msg($lang['authtempfail'], -1);
71
        return false;
72
    }
73
74
    // do the login either by cookie or provided credentials XXX
75
    $INPUT->set('http_credentials', false);
76
    if(!$conf['rememberme']) $INPUT->set('r', false);
77
78
    // handle renamed HTTP_AUTHORIZATION variable (can happen when a fix like
79
    // the one presented at
80
    // http://www.besthostratings.com/articles/http-auth-php-cgi.html is used
81
    // for enabling HTTP authentication with CGI/SuExec)
82
    if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
83
        $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
84
    // streamline HTTP auth credentials (IIS/rewrite -> mod_php)
85
    if(isset($_SERVER['HTTP_AUTHORIZATION'])) {
86
        list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
87
            explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
88
    }
89
90
    // if no credentials were given try to use HTTP auth (for SSO)
91
    if(!$INPUT->str('u') && empty($_COOKIE[DOKU_COOKIE]) && !empty($_SERVER['PHP_AUTH_USER'])) {
92
        $INPUT->set('u', $_SERVER['PHP_AUTH_USER']);
93
        $INPUT->set('p', $_SERVER['PHP_AUTH_PW']);
94
        $INPUT->set('http_credentials', true);
95
    }
96
97
    // apply cleaning (auth specific user names, remove control chars)
98
    if (true === $auth->success) {
0 ignored issues
show
Bug introduced by
Accessing success on the interface dokuwiki\Extension\PluginInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
99
        $INPUT->set('u', $auth->cleanUser(stripctl($INPUT->str('u'))));
100
        $INPUT->set('p', stripctl($INPUT->str('p')));
101
    }
102
103
    $ok = null;
104
    if (!is_null($auth) && $auth->canDo('external')) {
105
        $ok = $auth->trustExternal($INPUT->str('u'), $INPUT->str('p'), $INPUT->bool('r'));
106
    }
107
108
    if ($ok === null) {
109
        // external trust mechanism not in place, or returns no result,
110
        // then attempt auth_login
111
        $evdata = array(
112
            'user'     => $INPUT->str('u'),
113
            'password' => $INPUT->str('p'),
114
            'sticky'   => $INPUT->bool('r'),
115
            'silent'   => $INPUT->bool('http_credentials')
116
        );
117
        Event::createAndTrigger('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
118
    }
119
120
    //load ACL into a global array XXX
121
    $AUTH_ACL = auth_loadACL();
122
123
    return true;
124
}
125
126
/**
127
 * Loads the ACL setup and handle user wildcards
128
 *
129
 * @author Andreas Gohr <[email protected]>
130
 *
131
 * @return array
132
 */
133
function auth_loadACL() {
134
    global $config_cascade;
135
    global $USERINFO;
136
    /* @var Input $INPUT */
137
    global $INPUT;
138
139
    if(!is_readable($config_cascade['acl']['default'])) return array();
140
141
    $acl = file($config_cascade['acl']['default']);
142
143
    $out = array();
144
    foreach($acl as $line) {
145
        $line = trim($line);
146
        if(empty($line) || ($line[0] == '#')) continue; // skip blank lines & comments
147
        list($id,$rest) = preg_split('/[ \t]+/',$line,2);
148
149
        // substitute user wildcard first (its 1:1)
150
        if(strstr($line, '%USER%')){
151
            // if user is not logged in, this ACL line is meaningless - skip it
152
            if (!$INPUT->server->has('REMOTE_USER')) continue;
153
154
            $id   = str_replace('%USER%',cleanID($INPUT->server->str('REMOTE_USER')),$id);
155
            $rest = str_replace('%USER%',auth_nameencode($INPUT->server->str('REMOTE_USER')),$rest);
156
        }
157
158
        // substitute group wildcard (its 1:m)
159
        if(strstr($line, '%GROUP%')){
160
            // if user is not logged in, grps is empty, no output will be added (i.e. skipped)
161
            if(isset($USERINFO['grps'])){
162
                foreach((array) $USERINFO['grps'] as $grp){
163
                    $nid   = str_replace('%GROUP%',cleanID($grp),$id);
164
                    $nrest = str_replace('%GROUP%','@'.auth_nameencode($grp),$rest);
165
                    $out[] = "$nid\t$nrest";
166
                }
167
            }
168
        } else {
169
            $out[] = "$id\t$rest";
170
        }
171
    }
172
173
    return $out;
174
}
175
176
/**
177
 * Event hook callback for AUTH_LOGIN_CHECK
178
 *
179
 * @param array $evdata
180
 * @return bool
181
 */
182
function auth_login_wrapper($evdata) {
183
    return auth_login(
184
        $evdata['user'],
185
        $evdata['password'],
186
        $evdata['sticky'],
187
        $evdata['silent']
188
    );
189
}
190
191
/**
192
 * This tries to login the user based on the sent auth credentials
193
 *
194
 * The authentication works like this: if a username was given
195
 * a new login is assumed and user/password are checked. If they
196
 * are correct the password is encrypted with blowfish and stored
197
 * together with the username in a cookie - the same info is stored
198
 * in the session, too. Additonally a browserID is stored in the
199
 * session.
200
 *
201
 * If no username was given the cookie is checked: if the username,
202
 * crypted password and browserID match between session and cookie
203
 * no further testing is done and the user is accepted
204
 *
205
 * If a cookie was found but no session info was availabe the
206
 * blowfish encrypted password from the cookie is decrypted and
207
 * together with username rechecked by calling this function again.
208
 *
209
 * On a successful login $_SERVER[REMOTE_USER] and $USERINFO
210
 * are set.
211
 *
212
 * @author  Andreas Gohr <[email protected]>
213
 *
214
 * @param   string  $user    Username
215
 * @param   string  $pass    Cleartext Password
216
 * @param   bool    $sticky  Cookie should not expire
217
 * @param   bool    $silent  Don't show error on bad auth
218
 * @return  bool             true on successful auth
219
 */
220
function auth_login($user, $pass, $sticky = false, $silent = false) {
221
    global $USERINFO;
222
    global $conf;
223
    global $lang;
224
    /* @var AuthPlugin $auth */
225
    global $auth;
226
    /* @var Input $INPUT */
227
    global $INPUT;
228
229
    $sticky ? $sticky = true : $sticky = false; //sanity check
230
231
    if(!$auth) return false;
232
233
    if(!empty($user)) {
234
        //usual login
235
        if(!empty($pass) && $auth->checkPass($user, $pass)) {
236
            // make logininfo globally available
237
            $INPUT->server->set('REMOTE_USER', $user);
238
            $secret                 = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
239
            auth_setCookie($user, auth_encrypt($pass, $secret), $sticky);
240
            return true;
241
        } else {
242
            //invalid credentials - log off
243
            if(!$silent) {
244
                http_status(403, 'Login failed');
245
                msg($lang['badlogin'], -1);
246
            }
247
            auth_logoff();
248
            return false;
249
        }
250
    } else {
251
        // read cookie information
252
        list($user, $sticky, $pass) = auth_getCookie();
253
        if($user && $pass) {
254
            // we got a cookie - see if we can trust it
255
256
            // get session info
257
            $session = $_SESSION[DOKU_COOKIE]['auth'];
258
            if(isset($session) &&
259
                $auth->useSessionCache($user) &&
260
                ($session['time'] >= time() - $conf['auth_security_timeout']) &&
261
                ($session['user'] == $user) &&
262
                ($session['pass'] == sha1($pass)) && //still crypted
263
                ($session['buid'] == auth_browseruid())
264
            ) {
265
266
                // he has session, cookie and browser right - let him in
267
                $INPUT->server->set('REMOTE_USER', $user);
268
                $USERINFO               = $session['info']; //FIXME move all references to session
269
                return true;
270
            }
271
            // no we don't trust it yet - recheck pass but silent
272
            $secret = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
273
            $pass   = auth_decrypt($pass, $secret);
274
            return auth_login($user, $pass, $sticky, true);
275
        }
276
    }
277
    //just to be sure
278
    auth_logoff(true);
279
    return false;
280
}
281
282
/**
283
 * Builds a pseudo UID from browser and IP data
284
 *
285
 * This is neither unique nor unfakable - still it adds some
286
 * security. Using the first part of the IP makes sure
287
 * proxy farms like AOLs are still okay.
288
 *
289
 * @author  Andreas Gohr <[email protected]>
290
 *
291
 * @return  string  a MD5 sum of various browser headers
292
 */
293
function auth_browseruid() {
294
    /* @var Input $INPUT */
295
    global $INPUT;
296
297
    $ip  = clientIP(true);
298
    $uid = '';
299
    $uid .= $INPUT->server->str('HTTP_USER_AGENT');
300
    $uid .= $INPUT->server->str('HTTP_ACCEPT_CHARSET');
301
    $uid .= substr($ip, 0, strpos($ip, '.'));
302
    $uid = strtolower($uid);
303
    return md5($uid);
304
}
305
306
/**
307
 * Creates a random key to encrypt the password in cookies
308
 *
309
 * This function tries to read the password for encrypting
310
 * cookies from $conf['metadir'].'/_htcookiesalt'
311
 * if no such file is found a random key is created and
312
 * and stored in this file.
313
 *
314
 * @author  Andreas Gohr <[email protected]>
315
 *
316
 * @param   bool $addsession if true, the sessionid is added to the salt
317
 * @param   bool $secure     if security is more important than keeping the old value
318
 * @return  string
319
 */
320
function auth_cookiesalt($addsession = false, $secure = false) {
321
    if (defined('SIMPLE_TEST')) {
322
        return 'test';
323
    }
324
    global $conf;
325
    $file = $conf['metadir'].'/_htcookiesalt';
326
    if ($secure || !file_exists($file)) {
327
        $file = $conf['metadir'].'/_htcookiesalt2';
328
    }
329
    $salt = io_readFile($file);
330
    if(empty($salt)) {
331
        $salt = bin2hex(auth_randombytes(64));
332
        io_saveFile($file, $salt);
333
    }
334
    if($addsession) {
335
        $salt .= session_id();
336
    }
337
    return $salt;
338
}
339
340
/**
341
 * Return cryptographically secure random bytes.
342
 *
343
 * @author Niklas Keller <[email protected]>
344
 *
345
 * @param int $length number of bytes
346
 * @return string cryptographically secure random bytes
347
 */
348
function auth_randombytes($length) {
349
    return random_bytes($length);
350
}
351
352
/**
353
 * Cryptographically secure random number generator.
354
 *
355
 * @author Niklas Keller <[email protected]>
356
 *
357
 * @param int $min
358
 * @param int $max
359
 * @return int
360
 */
361
function auth_random($min, $max) {
362
    return random_int($min, $max);
363
}
364
365
/**
366
 * Encrypt data using the given secret using AES
367
 *
368
 * The mode is CBC with a random initialization vector, the key is derived
369
 * using pbkdf2.
370
 *
371
 * @param string $data   The data that shall be encrypted
372
 * @param string $secret The secret/password that shall be used
373
 * @return string The ciphertext
374
 */
375
function auth_encrypt($data, $secret) {
376
    $iv     = auth_randombytes(16);
377
    $cipher = new \phpseclib\Crypt\AES();
378
    $cipher->setPassword($secret);
379
380
    /*
381
    this uses the encrypted IV as IV as suggested in
382
    http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, Appendix C
383
    for unique but necessarily random IVs. The resulting ciphertext is
384
    compatible to ciphertext that was created using a "normal" IV.
385
    */
386
    return $cipher->encrypt($iv.$data);
387
}
388
389
/**
390
 * Decrypt the given AES ciphertext
391
 *
392
 * The mode is CBC, the key is derived using pbkdf2
393
 *
394
 * @param string $ciphertext The encrypted data
395
 * @param string $secret     The secret/password that shall be used
396
 * @return string The decrypted data
397
 */
398
function auth_decrypt($ciphertext, $secret) {
399
    $iv     = substr($ciphertext, 0, 16);
400
    $cipher = new \phpseclib\Crypt\AES();
401
    $cipher->setPassword($secret);
402
    $cipher->setIV($iv);
403
404
    return $cipher->decrypt(substr($ciphertext, 16));
405
}
406
407
/**
408
 * Log out the current user
409
 *
410
 * This clears all authentication data and thus log the user
411
 * off. It also clears session data.
412
 *
413
 * @author  Andreas Gohr <[email protected]>
414
 *
415
 * @param bool $keepbc - when true, the breadcrumb data is not cleared
416
 */
417
function auth_logoff($keepbc = false) {
418
    global $conf;
419
    global $USERINFO;
420
    /* @var AuthPlugin $auth */
421
    global $auth;
422
    /* @var Input $INPUT */
423
    global $INPUT;
424
425
    // make sure the session is writable (it usually is)
426
    @session_start();
427
428
    if(isset($_SESSION[DOKU_COOKIE]['auth']['user']))
429
        unset($_SESSION[DOKU_COOKIE]['auth']['user']);
430
    if(isset($_SESSION[DOKU_COOKIE]['auth']['pass']))
431
        unset($_SESSION[DOKU_COOKIE]['auth']['pass']);
432
    if(isset($_SESSION[DOKU_COOKIE]['auth']['info']))
433
        unset($_SESSION[DOKU_COOKIE]['auth']['info']);
434
    if(!$keepbc && isset($_SESSION[DOKU_COOKIE]['bc']))
435
        unset($_SESSION[DOKU_COOKIE]['bc']);
436
    $INPUT->server->remove('REMOTE_USER');
437
    $USERINFO = null; //FIXME
438
439
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
440
    setcookie(DOKU_COOKIE, '', time() - 600000, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
441
442
    if($auth) $auth->logOff();
443
}
444
445
/**
446
 * Check if a user is a manager
447
 *
448
 * Should usually be called without any parameters to check the current
449
 * user.
450
 *
451
 * The info is available through $INFO['ismanager'], too
452
 *
453
 * @author Andreas Gohr <[email protected]>
454
 * @see    auth_isadmin
455
 *
456
 * @param  string $user       Username
457
 * @param  array  $groups     List of groups the user is in
458
 * @param  bool   $adminonly  when true checks if user is admin
459
 * @return bool
460
 */
461
function auth_ismanager($user = null, $groups = null, $adminonly = false) {
462
    global $conf;
463
    global $USERINFO;
464
    /* @var AuthPlugin $auth */
465
    global $auth;
466
    /* @var Input $INPUT */
467
    global $INPUT;
468
469
470
    if(!$auth) return false;
471
    if(is_null($user)) {
472
        if(!$INPUT->server->has('REMOTE_USER')) {
473
            return false;
474
        } else {
475
            $user = $INPUT->server->str('REMOTE_USER');
476
        }
477
    }
478
    if(is_null($groups)) {
479
        $groups = $USERINFO ? (array) $USERINFO['grps'] : array();
480
    }
481
482
    // check superuser match
483
    if(auth_isMember($conf['superuser'], $user, $groups)) return true;
484
    if($adminonly) return false;
485
    // check managers
486
    if(auth_isMember($conf['manager'], $user, $groups)) return true;
487
488
    return false;
489
}
490
491
/**
492
 * Check if a user is admin
493
 *
494
 * Alias to auth_ismanager with adminonly=true
495
 *
496
 * The info is available through $INFO['isadmin'], too
497
 *
498
 * @author Andreas Gohr <[email protected]>
499
 * @see auth_ismanager()
500
 *
501
 * @param  string $user       Username
502
 * @param  array  $groups     List of groups the user is in
503
 * @return bool
504
 */
505
function auth_isadmin($user = null, $groups = null) {
506
    return auth_ismanager($user, $groups, true);
507
}
508
509
/**
510
 * Match a user and his groups against a comma separated list of
511
 * users and groups to determine membership status
512
 *
513
 * Note: all input should NOT be nameencoded.
514
 *
515
 * @param string $memberlist commaseparated list of allowed users and groups
516
 * @param string $user       user to match against
517
 * @param array  $groups     groups the user is member of
518
 * @return bool       true for membership acknowledged
519
 */
520
function auth_isMember($memberlist, $user, array $groups) {
521
    /* @var AuthPlugin $auth */
522
    global $auth;
523
    if(!$auth) return false;
524
525
    // clean user and groups
526
    if(!$auth->isCaseSensitive()) {
527
        $user   = \dokuwiki\Utf8\PhpString::strtolower($user);
528
        $groups = array_map('utf8_strtolower', $groups);
529
    }
530
    $user   = $auth->cleanUser($user);
531
    $groups = array_map(array($auth, 'cleanGroup'), $groups);
532
533
    // extract the memberlist
534
    $members = explode(',', $memberlist);
535
    $members = array_map('trim', $members);
536
    $members = array_unique($members);
537
    $members = array_filter($members);
538
539
    // compare cleaned values
540
    foreach($members as $member) {
541
        if($member == '@ALL' ) return true;
542
        if(!$auth->isCaseSensitive()) $member = \dokuwiki\Utf8\PhpString::strtolower($member);
543
        if($member[0] == '@') {
544
            $member = $auth->cleanGroup(substr($member, 1));
545
            if(in_array($member, $groups)) return true;
546
        } else {
547
            $member = $auth->cleanUser($member);
548
            if($member == $user) return true;
549
        }
550
    }
551
552
    // still here? not a member!
553
    return false;
554
}
555
556
/**
557
 * Convinience function for auth_aclcheck()
558
 *
559
 * This checks the permissions for the current user
560
 *
561
 * @author  Andreas Gohr <[email protected]>
562
 *
563
 * @param  string  $id  page ID (needs to be resolved and cleaned)
564
 * @return int          permission level
565
 */
566
function auth_quickaclcheck($id) {
567
    global $conf;
568
    global $USERINFO;
569
    /* @var Input $INPUT */
570
    global $INPUT;
571
    # if no ACL is used always return upload rights
572
    if(!$conf['useacl']) return AUTH_UPLOAD;
573
    return auth_aclcheck($id, $INPUT->server->str('REMOTE_USER'), is_array($USERINFO) ? $USERINFO['grps'] : array());
574
}
575
576
/**
577
 * Returns the maximum rights a user has for the given ID or its namespace
578
 *
579
 * @author  Andreas Gohr <[email protected]>
580
 *
581
 * @triggers AUTH_ACL_CHECK
582
 * @param  string       $id     page ID (needs to be resolved and cleaned)
583
 * @param  string       $user   Username
584
 * @param  array|null   $groups Array of groups the user is in
585
 * @return int             permission level
586
 */
587
function auth_aclcheck($id, $user, $groups) {
588
    $data = array(
589
        'id'     => $id,
590
        'user'   => $user,
591
        'groups' => $groups
592
    );
593
594
    return Event::createAndTrigger('AUTH_ACL_CHECK', $data, 'auth_aclcheck_cb');
595
}
596
597
/**
598
 * default ACL check method
599
 *
600
 * DO NOT CALL DIRECTLY, use auth_aclcheck() instead
601
 *
602
 * @author  Andreas Gohr <[email protected]>
603
 *
604
 * @param  array $data event data
605
 * @return int   permission level
606
 */
607
function auth_aclcheck_cb($data) {
608
    $id     =& $data['id'];
609
    $user   =& $data['user'];
610
    $groups =& $data['groups'];
611
612
    global $conf;
613
    global $AUTH_ACL;
614
    /* @var AuthPlugin $auth */
615
    global $auth;
616
617
    // if no ACL is used always return upload rights
618
    if(!$conf['useacl']) return AUTH_UPLOAD;
619
    if(!$auth) return AUTH_NONE;
620
621
    //make sure groups is an array
622
    if(!is_array($groups)) $groups = array();
623
624
    //if user is superuser or in superusergroup return 255 (acl_admin)
625
    if(auth_isadmin($user, $groups)) {
626
        return AUTH_ADMIN;
627
    }
628
629
    if(!$auth->isCaseSensitive()) {
630
        $user   = \dokuwiki\Utf8\PhpString::strtolower($user);
631
        $groups = array_map('utf8_strtolower', $groups);
632
    }
633
    $user   = auth_nameencode($auth->cleanUser($user));
634
    $groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
635
636
    //prepend groups with @ and nameencode
637
    foreach($groups as &$group) {
638
        $group = '@'.auth_nameencode($group);
639
    }
640
641
    $ns   = getNS($id);
642
    $perm = -1;
643
644
    //add ALL group
645
    $groups[] = '@ALL';
646
647
    //add User
648
    if($user) $groups[] = $user;
649
650
    //check exact match first
651
    $matches = preg_grep('/^'.preg_quote($id, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
652
    if(count($matches)) {
653
        foreach($matches as $match) {
654
            $match = preg_replace('/#.*$/', '', $match); //ignore comments
655
            $acl   = preg_split('/[ \t]+/', $match);
656
            if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
657
                $acl[1] = \dokuwiki\Utf8\PhpString::strtolower($acl[1]);
658
            }
659
            if(!in_array($acl[1], $groups)) {
660
                continue;
661
            }
662
            if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
663
            if($acl[2] > $perm) {
664
                $perm = $acl[2];
665
            }
666
        }
667
        if($perm > -1) {
668
            //we had a match - return it
669
            return (int) $perm;
670
        }
671
    }
672
673
    //still here? do the namespace checks
674
    if($ns) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ns of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
675
        $path = $ns.':*';
676
    } else {
677
        $path = '*'; //root document
678
    }
679
680
    do {
681
        $matches = preg_grep('/^'.preg_quote($path, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
682
        if(count($matches)) {
683
            foreach($matches as $match) {
684
                $match = preg_replace('/#.*$/', '', $match); //ignore comments
685
                $acl   = preg_split('/[ \t]+/', $match);
686
                if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
687
                    $acl[1] = \dokuwiki\Utf8\PhpString::strtolower($acl[1]);
688
                }
689
                if(!in_array($acl[1], $groups)) {
690
                    continue;
691
                }
692
                if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
693
                if($acl[2] > $perm) {
694
                    $perm = $acl[2];
695
                }
696
            }
697
            //we had a match - return it
698
            if($perm != -1) {
699
                return (int) $perm;
700
            }
701
        }
702
        //get next higher namespace
703
        $ns = getNS($ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($ns) on line 703 can also be of type false; however, getNS() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
704
705
        if($path != '*') {
706
            $path = $ns.':*';
707
            if($path == ':*') $path = '*';
708
        } else {
709
            //we did this already
710
            //looks like there is something wrong with the ACL
711
            //break here
712
            msg('No ACL setup yet! Denying access to everyone.');
713
            return AUTH_NONE;
714
        }
715
    } while(1); //this should never loop endless
716
    return AUTH_NONE;
717
}
718
719
/**
720
 * Encode ASCII special chars
721
 *
722
 * Some auth backends allow special chars in their user and groupnames
723
 * The special chars are encoded with this function. Only ASCII chars
724
 * are encoded UTF-8 multibyte are left as is (different from usual
725
 * urlencoding!).
726
 *
727
 * Decoding can be done with rawurldecode
728
 *
729
 * @author Andreas Gohr <[email protected]>
730
 * @see rawurldecode()
731
 *
732
 * @param string $name
733
 * @param bool $skip_group
734
 * @return string
735
 */
736
function auth_nameencode($name, $skip_group = false) {
737
    global $cache_authname;
738
    $cache =& $cache_authname;
739
    $name  = (string) $name;
740
741
    // never encode wildcard FS#1955
742
    if($name == '%USER%') return $name;
743
    if($name == '%GROUP%') return $name;
744
745
    if(!isset($cache[$name][$skip_group])) {
746
        if($skip_group && $name[0] == '@') {
747
            $cache[$name][$skip_group] = '@'.preg_replace_callback(
748
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
749
                'auth_nameencode_callback', substr($name, 1)
750
            );
751
        } else {
752
            $cache[$name][$skip_group] = preg_replace_callback(
753
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
754
                'auth_nameencode_callback', $name
755
            );
756
        }
757
    }
758
759
    return $cache[$name][$skip_group];
760
}
761
762
/**
763
 * callback encodes the matches
764
 *
765
 * @param array $matches first complete match, next matching subpatterms
766
 * @return string
767
 */
768
function auth_nameencode_callback($matches) {
769
    return '%'.dechex(ord(substr($matches[1],-1)));
770
}
771
772
/**
773
 * Create a pronouncable password
774
 *
775
 * The $foruser variable might be used by plugins to run additional password
776
 * policy checks, but is not used by the default implementation
777
 *
778
 * @author   Andreas Gohr <[email protected]>
779
 * @link     http://www.phpbuilder.com/annotate/message.php3?id=1014451
780
 * @triggers AUTH_PASSWORD_GENERATE
781
 *
782
 * @param  string $foruser username for which the password is generated
783
 * @return string  pronouncable password
784
 */
785
function auth_pwgen($foruser = '') {
786
    $data = array(
787
        'password' => '',
788
        'foruser'  => $foruser
789
    );
790
791
    $evt = new Event('AUTH_PASSWORD_GENERATE', $data);
792
    if($evt->advise_before(true)) {
793
        $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
794
        $v = 'aeiou'; //vowels
795
        $a = $c.$v; //both
796
        $s = '!$%&?+*~#-_:.;,'; // specials
797
798
        //use thre syllables...
799
        for($i = 0; $i < 3; $i++) {
800
            $data['password'] .= $c[auth_random(0, strlen($c) - 1)];
801
            $data['password'] .= $v[auth_random(0, strlen($v) - 1)];
802
            $data['password'] .= $a[auth_random(0, strlen($a) - 1)];
803
        }
804
        //... and add a nice number and special
805
        $data['password'] .= $s[auth_random(0, strlen($s) - 1)].auth_random(10, 99);
806
    }
807
    $evt->advise_after();
808
809
    return $data['password'];
810
}
811
812
/**
813
 * Sends a password to the given user
814
 *
815
 * @author  Andreas Gohr <[email protected]>
816
 *
817
 * @param string $user Login name of the user
818
 * @param string $password The new password in clear text
819
 * @return bool  true on success
820
 */
821
function auth_sendPassword($user, $password) {
822
    global $lang;
823
    /* @var AuthPlugin $auth */
824
    global $auth;
825
    if(!$auth) return false;
826
827
    $user     = $auth->cleanUser($user);
828
    $userinfo = $auth->getUserData($user, $requireGroups = false);
829
830
    if(!$userinfo['mail']) return false;
831
832
    $text = rawLocale('password');
833
    $trep = array(
834
        'FULLNAME' => $userinfo['name'],
835
        'LOGIN'    => $user,
836
        'PASSWORD' => $password
837
    );
838
839
    $mail = new Mailer();
840
    $mail->to($mail->getCleanName($userinfo['name']).' <'.$userinfo['mail'].'>');
841
    $mail->subject($lang['regpwmail']);
842
    $mail->setBody($text, $trep);
843
    return $mail->send();
844
}
845
846
/**
847
 * Register a new user
848
 *
849
 * This registers a new user - Data is read directly from $_POST
850
 *
851
 * @author  Andreas Gohr <[email protected]>
852
 *
853
 * @return bool  true on success, false on any error
854
 */
855
function register() {
856
    global $lang;
857
    global $conf;
858
    /* @var \dokuwiki\Extension\AuthPlugin $auth */
859
    global $auth;
860
    global $INPUT;
861
862
    if(!$INPUT->post->bool('save')) return false;
863
    if(!actionOK('register')) return false;
864
865
    // gather input
866
    $login    = trim($auth->cleanUser($INPUT->post->str('login')));
867
    $fullname = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('fullname')));
868
    $email    = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('email')));
869
    $pass     = $INPUT->post->str('pass');
870
    $passchk  = $INPUT->post->str('passchk');
871
872
    if(empty($login) || empty($fullname) || empty($email)) {
873
        msg($lang['regmissing'], -1);
874
        return false;
875
    }
876
877
    if($conf['autopasswd']) {
878
        $pass = auth_pwgen($login); // automatically generate password
879
    } elseif(empty($pass) || empty($passchk)) {
880
        msg($lang['regmissing'], -1); // complain about missing passwords
881
        return false;
882
    } elseif($pass != $passchk) {
883
        msg($lang['regbadpass'], -1); // complain about misspelled passwords
884
        return false;
885
    }
886
887
    //check mail
888
    if(!mail_isvalid($email)) {
889
        msg($lang['regbadmail'], -1);
890
        return false;
891
    }
892
893
    //okay try to create the user
894
    if(!$auth->triggerUserMod('create', array($login, $pass, $fullname, $email))) {
895
        msg($lang['regfail'], -1);
896
        return false;
897
    }
898
899
    // send notification about the new user
900
    $subscription = new RegistrationSubscriptionSender();
901
    $subscription->sendRegister($login, $fullname, $email);
902
903
    // are we done?
904
    if(!$conf['autopasswd']) {
905
        msg($lang['regsuccess2'], 1);
906
        return true;
907
    }
908
909
    // autogenerated password? then send password to user
910
    if(auth_sendPassword($login, $pass)) {
911
        msg($lang['regsuccess'], 1);
912
        return true;
913
    } else {
914
        msg($lang['regmailfail'], -1);
915
        return false;
916
    }
917
}
918
919
/**
920
 * Update user profile
921
 *
922
 * @author    Christopher Smith <[email protected]>
923
 */
924
function updateprofile() {
925
    global $conf;
926
    global $lang;
927
    /* @var AuthPlugin $auth */
928
    global $auth;
929
    /* @var Input $INPUT */
930
    global $INPUT;
931
932
    if(!$INPUT->post->bool('save')) return false;
933
    if(!checkSecurityToken()) return false;
934
935
    if(!actionOK('profile')) {
936
        msg($lang['profna'], -1);
937
        return false;
938
    }
939
940
    $changes         = array();
941
    $changes['pass'] = $INPUT->post->str('newpass');
942
    $changes['name'] = $INPUT->post->str('fullname');
943
    $changes['mail'] = $INPUT->post->str('email');
944
945
    // check misspelled passwords
946
    if($changes['pass'] != $INPUT->post->str('passchk')) {
947
        msg($lang['regbadpass'], -1);
948
        return false;
949
    }
950
951
    // clean fullname and email
952
    $changes['name'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['name']));
953
    $changes['mail'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['mail']));
954
955
    // no empty name and email (except the backend doesn't support them)
956
    if((empty($changes['name']) && $auth->canDo('modName')) ||
957
        (empty($changes['mail']) && $auth->canDo('modMail'))
958
    ) {
959
        msg($lang['profnoempty'], -1);
960
        return false;
961
    }
962
    if(!mail_isvalid($changes['mail']) && $auth->canDo('modMail')) {
963
        msg($lang['regbadmail'], -1);
964
        return false;
965
    }
966
967
    $changes = array_filter($changes);
968
969
    // check for unavailable capabilities
970
    if(!$auth->canDo('modName')) unset($changes['name']);
971
    if(!$auth->canDo('modMail')) unset($changes['mail']);
972
    if(!$auth->canDo('modPass')) unset($changes['pass']);
973
974
    // anything to do?
975
    if(!count($changes)) {
976
        msg($lang['profnochange'], -1);
977
        return false;
978
    }
979
980
    if($conf['profileconfirm']) {
981
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
982
            msg($lang['badpassconfirm'], -1);
983
            return false;
984
        }
985
    }
986
987
    if(!$auth->triggerUserMod('modify', array($INPUT->server->str('REMOTE_USER'), &$changes))) {
988
        msg($lang['proffail'], -1);
989
        return false;
990
    }
991
992
    if($changes['pass']) {
993
        // update cookie and session with the changed data
994
        list( /*user*/, $sticky, /*pass*/) = auth_getCookie();
995
        $pass = auth_encrypt($changes['pass'], auth_cookiesalt(!$sticky, true));
0 ignored issues
show
Bug introduced by
It seems like auth_cookiesalt(!$sticky, true) targeting auth_cookiesalt() can also be of type boolean; however, auth_encrypt() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
996
        auth_setCookie($INPUT->server->str('REMOTE_USER'), $pass, (bool) $sticky);
997
    } else {
998
        // make sure the session is writable
999
        @session_start();
1000
        // invalidate session cache
1001
        $_SESSION[DOKU_COOKIE]['auth']['time'] = 0;
1002
        session_write_close();
1003
    }
1004
1005
    return true;
1006
}
1007
1008
/**
1009
 * Delete the current logged-in user
1010
 *
1011
 * @return bool true on success, false on any error
1012
 */
1013
function auth_deleteprofile(){
1014
    global $conf;
1015
    global $lang;
1016
    /* @var \dokuwiki\Extension\AuthPlugin $auth */
1017
    global $auth;
1018
    /* @var Input $INPUT */
1019
    global $INPUT;
1020
1021
    if(!$INPUT->post->bool('delete')) return false;
1022
    if(!checkSecurityToken()) return false;
1023
1024
    // action prevented or auth module disallows
1025
    if(!actionOK('profile_delete') || !$auth->canDo('delUser')) {
1026
        msg($lang['profnodelete'], -1);
1027
        return false;
1028
    }
1029
1030
    if(!$INPUT->post->bool('confirm_delete')){
1031
        msg($lang['profconfdeletemissing'], -1);
1032
        return false;
1033
    }
1034
1035
    if($conf['profileconfirm']) {
1036
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
1037
            msg($lang['badpassconfirm'], -1);
1038
            return false;
1039
        }
1040
    }
1041
1042
    $deleted = array();
1043
    $deleted[] = $INPUT->server->str('REMOTE_USER');
1044
    if($auth->triggerUserMod('delete', array($deleted))) {
1045
        // force and immediate logout including removing the sticky cookie
1046
        auth_logoff();
1047
        return true;
1048
    }
1049
1050
    return false;
1051
}
1052
1053
/**
1054
 * Send a  new password
1055
 *
1056
 * This function handles both phases of the password reset:
1057
 *
1058
 *   - handling the first request of password reset
1059
 *   - validating the password reset auth token
1060
 *
1061
 * @author Benoit Chesneau <[email protected]>
1062
 * @author Chris Smith <[email protected]>
1063
 * @author Andreas Gohr <[email protected]>
1064
 *
1065
 * @return bool true on success, false on any error
1066
 */
1067
function act_resendpwd() {
1068
    global $lang;
1069
    global $conf;
1070
    /* @var AuthPlugin $auth */
1071
    global $auth;
1072
    /* @var Input $INPUT */
1073
    global $INPUT;
1074
1075
    if(!actionOK('resendpwd')) {
1076
        msg($lang['resendna'], -1);
1077
        return false;
1078
    }
1079
1080
    $token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
1081
1082
    if($token) {
1083
        // we're in token phase - get user info from token
1084
1085
        $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
1086
        if(!file_exists($tfile)) {
1087
            msg($lang['resendpwdbadauth'], -1);
1088
            $INPUT->remove('pwauth');
1089
            return false;
1090
        }
1091
        // token is only valid for 3 days
1092
        if((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
1093
            msg($lang['resendpwdbadauth'], -1);
1094
            $INPUT->remove('pwauth');
1095
            @unlink($tfile);
1096
            return false;
1097
        }
1098
1099
        $user     = io_readfile($tfile);
1100
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1101
        if(!$userinfo['mail']) {
1102
            msg($lang['resendpwdnouser'], -1);
1103
            return false;
1104
        }
1105
1106
        if(!$conf['autopasswd']) { // we let the user choose a password
1107
            $pass = $INPUT->str('pass');
1108
1109
            // password given correctly?
1110
            if(!$pass) return false;
1111
            if($pass != $INPUT->str('passchk')) {
1112
                msg($lang['regbadpass'], -1);
1113
                return false;
1114
            }
1115
1116
            // change it
1117
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1118
                msg($lang['proffail'], -1);
1119
                return false;
1120
            }
1121
1122
        } else { // autogenerate the password and send by mail
1123
1124
            $pass = auth_pwgen($user);
1125
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1126
                msg($lang['proffail'], -1);
1127
                return false;
1128
            }
1129
1130
            if(auth_sendPassword($user, $pass)) {
1131
                msg($lang['resendpwdsuccess'], 1);
1132
            } else {
1133
                msg($lang['regmailfail'], -1);
1134
            }
1135
        }
1136
1137
        @unlink($tfile);
1138
        return true;
1139
1140
    } else {
1141
        // we're in request phase
1142
1143
        if(!$INPUT->post->bool('save')) return false;
1144
1145
        if(!$INPUT->post->str('login')) {
1146
            msg($lang['resendpwdmissing'], -1);
1147
            return false;
1148
        } else {
1149
            $user = trim($auth->cleanUser($INPUT->post->str('login')));
1150
        }
1151
1152
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1153
        if(!$userinfo['mail']) {
1154
            msg($lang['resendpwdnouser'], -1);
1155
            return false;
1156
        }
1157
1158
        // generate auth token
1159
        $token = md5(auth_randombytes(16)); // random secret
1160
        $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
1161
        $url   = wl('', array('do'=> 'resendpwd', 'pwauth'=> $token), true, '&');
1162
1163
        io_saveFile($tfile, $user);
1164
1165
        $text = rawLocale('pwconfirm');
1166
        $trep = array(
1167
            'FULLNAME' => $userinfo['name'],
1168
            'LOGIN'    => $user,
1169
            'CONFIRM'  => $url
1170
        );
1171
1172
        $mail = new Mailer();
1173
        $mail->to($userinfo['name'].' <'.$userinfo['mail'].'>');
1174
        $mail->subject($lang['regpwmail']);
1175
        $mail->setBody($text, $trep);
1176
        if($mail->send()) {
1177
            msg($lang['resendpwdconfirm'], 1);
1178
        } else {
1179
            msg($lang['regmailfail'], -1);
1180
        }
1181
        return true;
1182
    }
1183
    // never reached
1184
}
1185
1186
/**
1187
 * Encrypts a password using the given method and salt
1188
 *
1189
 * If the selected method needs a salt and none was given, a random one
1190
 * is chosen.
1191
 *
1192
 * @author  Andreas Gohr <[email protected]>
1193
 *
1194
 * @param string $clear The clear text password
1195
 * @param string $method The hashing method
1196
 * @param string $salt A salt, null for random
1197
 * @return  string  The crypted password
1198
 */
1199
function auth_cryptPassword($clear, $method = '', $salt = null) {
1200
    global $conf;
1201
    if(empty($method)) $method = $conf['passcrypt'];
1202
1203
    $pass = new PassHash();
1204
    $call = 'hash_'.$method;
1205
1206
    if(!method_exists($pass, $call)) {
1207
        msg("Unsupported crypt method $method", -1);
1208
        return false;
1209
    }
1210
1211
    return $pass->$call($clear, $salt);
1212
}
1213
1214
/**
1215
 * Verifies a cleartext password against a crypted hash
1216
 *
1217
 * @author Andreas Gohr <[email protected]>
1218
 *
1219
 * @param  string $clear The clear text password
1220
 * @param  string $crypt The hash to compare with
1221
 * @return bool true if both match
1222
 */
1223
function auth_verifyPassword($clear, $crypt) {
1224
    $pass = new PassHash();
1225
    return $pass->verify_hash($clear, $crypt);
1226
}
1227
1228
/**
1229
 * Set the authentication cookie and add user identification data to the session
1230
 *
1231
 * @param string  $user       username
1232
 * @param string  $pass       encrypted password
1233
 * @param bool    $sticky     whether or not the cookie will last beyond the session
1234
 * @return bool
1235
 */
1236
function auth_setCookie($user, $pass, $sticky) {
1237
    global $conf;
1238
    /* @var AuthPlugin $auth */
1239
    global $auth;
1240
    global $USERINFO;
1241
1242
    if(!$auth) return false;
1243
    $USERINFO = $auth->getUserData($user);
1244
1245
    // set cookie
1246
    $cookie    = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode($pass);
1247
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
1248
    $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; //one year
1249
    setcookie(DOKU_COOKIE, $cookie, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
1250
1251
    // set session
1252
    $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
1253
    $_SESSION[DOKU_COOKIE]['auth']['pass'] = sha1($pass);
1254
    $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid();
1255
    $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
1256
    $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
1257
1258
    return true;
1259
}
1260
1261
/**
1262
 * Returns the user, (encrypted) password and sticky bit from cookie
1263
 *
1264
 * @returns array
1265
 */
1266
function auth_getCookie() {
1267
    if(!isset($_COOKIE[DOKU_COOKIE])) {
1268
        return array(null, null, null);
1269
    }
1270
    list($user, $sticky, $pass) = explode('|', $_COOKIE[DOKU_COOKIE], 3);
1271
    $sticky = (bool) $sticky;
1272
    $pass   = base64_decode($pass);
1273
    $user   = base64_decode($user);
1274
    return array($user, $sticky, $pass);
1275
}
1276
1277
//Setup VIM: ex: et ts=2 :
1278