Failed Conditions
Push — stable ( 017e16...b83837 )
by
unknown
07:54 queued 02:55
created

auth.php ➔ auth_aclcheck_cb()   F

Complexity

Conditions 28
Paths 468

Size

Total Lines 111

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 28
nc 468
nop 1
dl 0
loc 111
rs 0.5911
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\Extension\AuthPlugin;
14
use dokuwiki\Extension\Event;
15
use dokuwiki\Extension\PluginController;
16
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...
17
use dokuwiki\Subscriptions\RegistrationSubscriptionSender;
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
 * @param string $user Username
454
 * @param array $groups List of groups the user is in
455
 * @param bool $adminonly when true checks if user is admin
456
 * @param bool $recache set to true to refresh the cache
457
 * @return bool
458
 * @see    auth_isadmin
459
 *
460
 * @author Andreas Gohr <[email protected]>
461
 */
462
function auth_ismanager($user = null, $groups = null, $adminonly = false, $recache=false) {
463
    global $conf;
464
    global $USERINFO;
465
    /* @var AuthPlugin $auth */
466
    global $auth;
467
    /* @var Input $INPUT */
468
    global $INPUT;
469
470
471
    if(!$auth) return false;
472
    if(is_null($user)) {
473
        if(!$INPUT->server->has('REMOTE_USER')) {
474
            return false;
475
        } else {
476
            $user = $INPUT->server->str('REMOTE_USER');
477
        }
478
    }
479
    if(is_null($groups)) {
480
        $groups = $USERINFO ? (array) $USERINFO['grps'] : array();
481
    }
482
483
    // prefer cached result
484
    static $cache = [];
485
    $cachekey = serialize([$user, $adminonly, $groups]);
486
    if (!isset($cache[$cachekey]) || $recache) {
487
        // check superuser match
488
        $ok = auth_isMember($conf['superuser'], $user, $groups);
489
490
        // check managers
491
        if (!$ok && !$adminonly) {
492
            $ok = auth_isMember($conf['manager'], $user, $groups);
493
        }
494
495
        $cache[$cachekey] = $ok;
496
    }
497
498
    return $cache[$cachekey];
499
}
500
501
/**
502
 * Check if a user is admin
503
 *
504
 * Alias to auth_ismanager with adminonly=true
505
 *
506
 * The info is available through $INFO['isadmin'], too
507
 *
508
 * @param string $user Username
509
 * @param array $groups List of groups the user is in
510
 * @param bool $recache set to true to refresh the cache
511
 * @return bool
512
 * @author Andreas Gohr <[email protected]>
513
 * @see auth_ismanager()
514
 *
515
 */
516
function auth_isadmin($user = null, $groups = null, $recache=false) {
517
    return auth_ismanager($user, $groups, true, $recache);
518
}
519
520
/**
521
 * Match a user and his groups against a comma separated list of
522
 * users and groups to determine membership status
523
 *
524
 * Note: all input should NOT be nameencoded.
525
 *
526
 * @param string $memberlist commaseparated list of allowed users and groups
527
 * @param string $user       user to match against
528
 * @param array  $groups     groups the user is member of
529
 * @return bool       true for membership acknowledged
530
 */
531
function auth_isMember($memberlist, $user, array $groups) {
532
    /* @var AuthPlugin $auth */
533
    global $auth;
534
    if(!$auth) return false;
535
536
    // clean user and groups
537
    if(!$auth->isCaseSensitive()) {
538
        $user   = \dokuwiki\Utf8\PhpString::strtolower($user);
539
        $groups = array_map('utf8_strtolower', $groups);
540
    }
541
    $user   = $auth->cleanUser($user);
542
    $groups = array_map(array($auth, 'cleanGroup'), $groups);
543
544
    // extract the memberlist
545
    $members = explode(',', $memberlist);
546
    $members = array_map('trim', $members);
547
    $members = array_unique($members);
548
    $members = array_filter($members);
549
550
    // compare cleaned values
551
    foreach($members as $member) {
552
        if($member == '@ALL' ) return true;
553
        if(!$auth->isCaseSensitive()) $member = \dokuwiki\Utf8\PhpString::strtolower($member);
554
        if($member[0] == '@') {
555
            $member = $auth->cleanGroup(substr($member, 1));
556
            if(in_array($member, $groups)) return true;
557
        } else {
558
            $member = $auth->cleanUser($member);
559
            if($member == $user) return true;
560
        }
561
    }
562
563
    // still here? not a member!
564
    return false;
565
}
566
567
/**
568
 * Convinience function for auth_aclcheck()
569
 *
570
 * This checks the permissions for the current user
571
 *
572
 * @author  Andreas Gohr <[email protected]>
573
 *
574
 * @param  string  $id  page ID (needs to be resolved and cleaned)
575
 * @return int          permission level
576
 */
577
function auth_quickaclcheck($id) {
578
    global $conf;
579
    global $USERINFO;
580
    /* @var Input $INPUT */
581
    global $INPUT;
582
    # if no ACL is used always return upload rights
583
    if(!$conf['useacl']) return AUTH_UPLOAD;
584
    return auth_aclcheck($id, $INPUT->server->str('REMOTE_USER'), is_array($USERINFO) ? $USERINFO['grps'] : array());
585
}
586
587
/**
588
 * Returns the maximum rights a user has for the given ID or its namespace
589
 *
590
 * @author  Andreas Gohr <[email protected]>
591
 *
592
 * @triggers AUTH_ACL_CHECK
593
 * @param  string       $id     page ID (needs to be resolved and cleaned)
594
 * @param  string       $user   Username
595
 * @param  array|null   $groups Array of groups the user is in
596
 * @return int             permission level
597
 */
598
function auth_aclcheck($id, $user, $groups) {
599
    $data = array(
600
        'id'     => $id,
601
        'user'   => $user,
602
        'groups' => $groups
603
    );
604
605
    return Event::createAndTrigger('AUTH_ACL_CHECK', $data, 'auth_aclcheck_cb');
606
}
607
608
/**
609
 * default ACL check method
610
 *
611
 * DO NOT CALL DIRECTLY, use auth_aclcheck() instead
612
 *
613
 * @author  Andreas Gohr <[email protected]>
614
 *
615
 * @param  array $data event data
616
 * @return int   permission level
617
 */
618
function auth_aclcheck_cb($data) {
619
    $id     =& $data['id'];
620
    $user   =& $data['user'];
621
    $groups =& $data['groups'];
622
623
    global $conf;
624
    global $AUTH_ACL;
625
    /* @var AuthPlugin $auth */
626
    global $auth;
627
628
    // if no ACL is used always return upload rights
629
    if(!$conf['useacl']) return AUTH_UPLOAD;
630
    if(!$auth) return AUTH_NONE;
631
632
    //make sure groups is an array
633
    if(!is_array($groups)) $groups = array();
634
635
    //if user is superuser or in superusergroup return 255 (acl_admin)
636
    if(auth_isadmin($user, $groups)) {
637
        return AUTH_ADMIN;
638
    }
639
640
    if(!$auth->isCaseSensitive()) {
641
        $user   = \dokuwiki\Utf8\PhpString::strtolower($user);
642
        $groups = array_map('utf8_strtolower', $groups);
643
    }
644
    $user   = auth_nameencode($auth->cleanUser($user));
645
    $groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
646
647
    //prepend groups with @ and nameencode
648
    foreach($groups as &$group) {
649
        $group = '@'.auth_nameencode($group);
650
    }
651
652
    $ns   = getNS($id);
653
    $perm = -1;
654
655
    //add ALL group
656
    $groups[] = '@ALL';
657
658
    //add User
659
    if($user) $groups[] = $user;
660
661
    //check exact match first
662
    $matches = preg_grep('/^'.preg_quote($id, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
663
    if(count($matches)) {
664
        foreach($matches as $match) {
665
            $match = preg_replace('/#.*$/', '', $match); //ignore comments
666
            $acl   = preg_split('/[ \t]+/', $match);
667
            if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
668
                $acl[1] = \dokuwiki\Utf8\PhpString::strtolower($acl[1]);
669
            }
670
            if(!in_array($acl[1], $groups)) {
671
                continue;
672
            }
673
            if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
674
            if($acl[2] > $perm) {
675
                $perm = $acl[2];
676
            }
677
        }
678
        if($perm > -1) {
679
            //we had a match - return it
680
            return (int) $perm;
681
        }
682
    }
683
684
    //still here? do the namespace checks
685
    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...
686
        $path = $ns.':*';
687
    } else {
688
        $path = '*'; //root document
689
    }
690
691
    do {
692
        $matches = preg_grep('/^'.preg_quote($path, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
693
        if(count($matches)) {
694
            foreach($matches as $match) {
695
                $match = preg_replace('/#.*$/', '', $match); //ignore comments
696
                $acl   = preg_split('/[ \t]+/', $match);
697
                if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
698
                    $acl[1] = \dokuwiki\Utf8\PhpString::strtolower($acl[1]);
699
                }
700
                if(!in_array($acl[1], $groups)) {
701
                    continue;
702
                }
703
                if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
704
                if($acl[2] > $perm) {
705
                    $perm = $acl[2];
706
                }
707
            }
708
            //we had a match - return it
709
            if($perm != -1) {
710
                return (int) $perm;
711
            }
712
        }
713
        //get next higher namespace
714
        $ns = getNS($ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($ns) on line 714 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...
715
716
        if($path != '*') {
717
            $path = $ns.':*';
718
            if($path == ':*') $path = '*';
719
        } else {
720
            //we did this already
721
            //looks like there is something wrong with the ACL
722
            //break here
723
            msg('No ACL setup yet! Denying access to everyone.');
724
            return AUTH_NONE;
725
        }
726
    } while(1); //this should never loop endless
727
    return AUTH_NONE;
728
}
729
730
/**
731
 * Encode ASCII special chars
732
 *
733
 * Some auth backends allow special chars in their user and groupnames
734
 * The special chars are encoded with this function. Only ASCII chars
735
 * are encoded UTF-8 multibyte are left as is (different from usual
736
 * urlencoding!).
737
 *
738
 * Decoding can be done with rawurldecode
739
 *
740
 * @author Andreas Gohr <[email protected]>
741
 * @see rawurldecode()
742
 *
743
 * @param string $name
744
 * @param bool $skip_group
745
 * @return string
746
 */
747
function auth_nameencode($name, $skip_group = false) {
748
    global $cache_authname;
749
    $cache =& $cache_authname;
750
    $name  = (string) $name;
751
752
    // never encode wildcard FS#1955
753
    if($name == '%USER%') return $name;
754
    if($name == '%GROUP%') return $name;
755
756
    if(!isset($cache[$name][$skip_group])) {
757
        if($skip_group && $name[0] == '@') {
758
            $cache[$name][$skip_group] = '@'.preg_replace_callback(
759
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
760
                'auth_nameencode_callback', substr($name, 1)
761
            );
762
        } else {
763
            $cache[$name][$skip_group] = preg_replace_callback(
764
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
765
                'auth_nameencode_callback', $name
766
            );
767
        }
768
    }
769
770
    return $cache[$name][$skip_group];
771
}
772
773
/**
774
 * callback encodes the matches
775
 *
776
 * @param array $matches first complete match, next matching subpatterms
777
 * @return string
778
 */
779
function auth_nameencode_callback($matches) {
780
    return '%'.dechex(ord(substr($matches[1],-1)));
781
}
782
783
/**
784
 * Create a pronouncable password
785
 *
786
 * The $foruser variable might be used by plugins to run additional password
787
 * policy checks, but is not used by the default implementation
788
 *
789
 * @author   Andreas Gohr <[email protected]>
790
 * @link     http://www.phpbuilder.com/annotate/message.php3?id=1014451
791
 * @triggers AUTH_PASSWORD_GENERATE
792
 *
793
 * @param  string $foruser username for which the password is generated
794
 * @return string  pronouncable password
795
 */
796
function auth_pwgen($foruser = '') {
797
    $data = array(
798
        'password' => '',
799
        'foruser'  => $foruser
800
    );
801
802
    $evt = new Event('AUTH_PASSWORD_GENERATE', $data);
803
    if($evt->advise_before(true)) {
804
        $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
805
        $v = 'aeiou'; //vowels
806
        $a = $c.$v; //both
807
        $s = '!$%&?+*~#-_:.;,'; // specials
808
809
        //use thre syllables...
810
        for($i = 0; $i < 3; $i++) {
811
            $data['password'] .= $c[auth_random(0, strlen($c) - 1)];
812
            $data['password'] .= $v[auth_random(0, strlen($v) - 1)];
813
            $data['password'] .= $a[auth_random(0, strlen($a) - 1)];
814
        }
815
        //... and add a nice number and special
816
        $data['password'] .= $s[auth_random(0, strlen($s) - 1)].auth_random(10, 99);
817
    }
818
    $evt->advise_after();
819
820
    return $data['password'];
821
}
822
823
/**
824
 * Sends a password to the given user
825
 *
826
 * @author  Andreas Gohr <[email protected]>
827
 *
828
 * @param string $user Login name of the user
829
 * @param string $password The new password in clear text
830
 * @return bool  true on success
831
 */
832
function auth_sendPassword($user, $password) {
833
    global $lang;
834
    /* @var AuthPlugin $auth */
835
    global $auth;
836
    if(!$auth) return false;
837
838
    $user     = $auth->cleanUser($user);
839
    $userinfo = $auth->getUserData($user, $requireGroups = false);
840
841
    if(!$userinfo['mail']) return false;
842
843
    $text = rawLocale('password');
844
    $trep = array(
845
        'FULLNAME' => $userinfo['name'],
846
        'LOGIN'    => $user,
847
        'PASSWORD' => $password
848
    );
849
850
    $mail = new Mailer();
851
    $mail->to($mail->getCleanName($userinfo['name']).' <'.$userinfo['mail'].'>');
852
    $mail->subject($lang['regpwmail']);
853
    $mail->setBody($text, $trep);
854
    return $mail->send();
855
}
856
857
/**
858
 * Register a new user
859
 *
860
 * This registers a new user - Data is read directly from $_POST
861
 *
862
 * @author  Andreas Gohr <[email protected]>
863
 *
864
 * @return bool  true on success, false on any error
865
 */
866
function register() {
867
    global $lang;
868
    global $conf;
869
    /* @var \dokuwiki\Extension\AuthPlugin $auth */
870
    global $auth;
871
    global $INPUT;
872
873
    if(!$INPUT->post->bool('save')) return false;
874
    if(!actionOK('register')) return false;
875
876
    // gather input
877
    $login    = trim($auth->cleanUser($INPUT->post->str('login')));
878
    $fullname = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('fullname')));
879
    $email    = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('email')));
880
    $pass     = $INPUT->post->str('pass');
881
    $passchk  = $INPUT->post->str('passchk');
882
883
    if(empty($login) || empty($fullname) || empty($email)) {
884
        msg($lang['regmissing'], -1);
885
        return false;
886
    }
887
888
    if($conf['autopasswd']) {
889
        $pass = auth_pwgen($login); // automatically generate password
890
    } elseif(empty($pass) || empty($passchk)) {
891
        msg($lang['regmissing'], -1); // complain about missing passwords
892
        return false;
893
    } elseif($pass != $passchk) {
894
        msg($lang['regbadpass'], -1); // complain about misspelled passwords
895
        return false;
896
    }
897
898
    //check mail
899
    if(!mail_isvalid($email)) {
900
        msg($lang['regbadmail'], -1);
901
        return false;
902
    }
903
904
    //okay try to create the user
905
    if(!$auth->triggerUserMod('create', array($login, $pass, $fullname, $email))) {
906
        msg($lang['regfail'], -1);
907
        return false;
908
    }
909
910
    // send notification about the new user
911
    $subscription = new RegistrationSubscriptionSender();
912
    $subscription->sendRegister($login, $fullname, $email);
913
914
    // are we done?
915
    if(!$conf['autopasswd']) {
916
        msg($lang['regsuccess2'], 1);
917
        return true;
918
    }
919
920
    // autogenerated password? then send password to user
921
    if(auth_sendPassword($login, $pass)) {
922
        msg($lang['regsuccess'], 1);
923
        return true;
924
    } else {
925
        msg($lang['regmailfail'], -1);
926
        return false;
927
    }
928
}
929
930
/**
931
 * Update user profile
932
 *
933
 * @author    Christopher Smith <[email protected]>
934
 */
935
function updateprofile() {
936
    global $conf;
937
    global $lang;
938
    /* @var AuthPlugin $auth */
939
    global $auth;
940
    /* @var Input $INPUT */
941
    global $INPUT;
942
943
    if(!$INPUT->post->bool('save')) return false;
944
    if(!checkSecurityToken()) return false;
945
946
    if(!actionOK('profile')) {
947
        msg($lang['profna'], -1);
948
        return false;
949
    }
950
951
    $changes         = array();
952
    $changes['pass'] = $INPUT->post->str('newpass');
953
    $changes['name'] = $INPUT->post->str('fullname');
954
    $changes['mail'] = $INPUT->post->str('email');
955
956
    // check misspelled passwords
957
    if($changes['pass'] != $INPUT->post->str('passchk')) {
958
        msg($lang['regbadpass'], -1);
959
        return false;
960
    }
961
962
    // clean fullname and email
963
    $changes['name'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['name']));
964
    $changes['mail'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['mail']));
965
966
    // no empty name and email (except the backend doesn't support them)
967
    if((empty($changes['name']) && $auth->canDo('modName')) ||
968
        (empty($changes['mail']) && $auth->canDo('modMail'))
969
    ) {
970
        msg($lang['profnoempty'], -1);
971
        return false;
972
    }
973
    if(!mail_isvalid($changes['mail']) && $auth->canDo('modMail')) {
974
        msg($lang['regbadmail'], -1);
975
        return false;
976
    }
977
978
    $changes = array_filter($changes);
979
980
    // check for unavailable capabilities
981
    if(!$auth->canDo('modName')) unset($changes['name']);
982
    if(!$auth->canDo('modMail')) unset($changes['mail']);
983
    if(!$auth->canDo('modPass')) unset($changes['pass']);
984
985
    // anything to do?
986
    if(!count($changes)) {
987
        msg($lang['profnochange'], -1);
988
        return false;
989
    }
990
991
    if($conf['profileconfirm']) {
992
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
993
            msg($lang['badpassconfirm'], -1);
994
            return false;
995
        }
996
    }
997
998
    if(!$auth->triggerUserMod('modify', array($INPUT->server->str('REMOTE_USER'), &$changes))) {
999
        msg($lang['proffail'], -1);
1000
        return false;
1001
    }
1002
1003
    if($changes['pass']) {
1004
        // update cookie and session with the changed data
1005
        list( /*user*/, $sticky, /*pass*/) = auth_getCookie();
1006
        $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...
1007
        auth_setCookie($INPUT->server->str('REMOTE_USER'), $pass, (bool) $sticky);
1008
    } else {
1009
        // make sure the session is writable
1010
        @session_start();
1011
        // invalidate session cache
1012
        $_SESSION[DOKU_COOKIE]['auth']['time'] = 0;
1013
        session_write_close();
1014
    }
1015
1016
    return true;
1017
}
1018
1019
/**
1020
 * Delete the current logged-in user
1021
 *
1022
 * @return bool true on success, false on any error
1023
 */
1024
function auth_deleteprofile(){
1025
    global $conf;
1026
    global $lang;
1027
    /* @var \dokuwiki\Extension\AuthPlugin $auth */
1028
    global $auth;
1029
    /* @var Input $INPUT */
1030
    global $INPUT;
1031
1032
    if(!$INPUT->post->bool('delete')) return false;
1033
    if(!checkSecurityToken()) return false;
1034
1035
    // action prevented or auth module disallows
1036
    if(!actionOK('profile_delete') || !$auth->canDo('delUser')) {
1037
        msg($lang['profnodelete'], -1);
1038
        return false;
1039
    }
1040
1041
    if(!$INPUT->post->bool('confirm_delete')){
1042
        msg($lang['profconfdeletemissing'], -1);
1043
        return false;
1044
    }
1045
1046
    if($conf['profileconfirm']) {
1047
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
1048
            msg($lang['badpassconfirm'], -1);
1049
            return false;
1050
        }
1051
    }
1052
1053
    $deleted = array();
1054
    $deleted[] = $INPUT->server->str('REMOTE_USER');
1055
    if($auth->triggerUserMod('delete', array($deleted))) {
1056
        // force and immediate logout including removing the sticky cookie
1057
        auth_logoff();
1058
        return true;
1059
    }
1060
1061
    return false;
1062
}
1063
1064
/**
1065
 * Send a  new password
1066
 *
1067
 * This function handles both phases of the password reset:
1068
 *
1069
 *   - handling the first request of password reset
1070
 *   - validating the password reset auth token
1071
 *
1072
 * @author Benoit Chesneau <[email protected]>
1073
 * @author Chris Smith <[email protected]>
1074
 * @author Andreas Gohr <[email protected]>
1075
 *
1076
 * @return bool true on success, false on any error
1077
 */
1078
function act_resendpwd() {
1079
    global $lang;
1080
    global $conf;
1081
    /* @var AuthPlugin $auth */
1082
    global $auth;
1083
    /* @var Input $INPUT */
1084
    global $INPUT;
1085
1086
    if(!actionOK('resendpwd')) {
1087
        msg($lang['resendna'], -1);
1088
        return false;
1089
    }
1090
1091
    $token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
1092
1093
    if($token) {
1094
        // we're in token phase - get user info from token
1095
1096
        $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
1097
        if(!file_exists($tfile)) {
1098
            msg($lang['resendpwdbadauth'], -1);
1099
            $INPUT->remove('pwauth');
1100
            return false;
1101
        }
1102
        // token is only valid for 3 days
1103
        if((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
1104
            msg($lang['resendpwdbadauth'], -1);
1105
            $INPUT->remove('pwauth');
1106
            @unlink($tfile);
1107
            return false;
1108
        }
1109
1110
        $user     = io_readfile($tfile);
1111
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1112
        if(!$userinfo['mail']) {
1113
            msg($lang['resendpwdnouser'], -1);
1114
            return false;
1115
        }
1116
1117
        if(!$conf['autopasswd']) { // we let the user choose a password
1118
            $pass = $INPUT->str('pass');
1119
1120
            // password given correctly?
1121
            if(!$pass) return false;
1122
            if($pass != $INPUT->str('passchk')) {
1123
                msg($lang['regbadpass'], -1);
1124
                return false;
1125
            }
1126
1127
            // change it
1128
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1129
                msg($lang['proffail'], -1);
1130
                return false;
1131
            }
1132
1133
        } else { // autogenerate the password and send by mail
1134
1135
            $pass = auth_pwgen($user);
1136
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1137
                msg($lang['proffail'], -1);
1138
                return false;
1139
            }
1140
1141
            if(auth_sendPassword($user, $pass)) {
1142
                msg($lang['resendpwdsuccess'], 1);
1143
            } else {
1144
                msg($lang['regmailfail'], -1);
1145
            }
1146
        }
1147
1148
        @unlink($tfile);
1149
        return true;
1150
1151
    } else {
1152
        // we're in request phase
1153
1154
        if(!$INPUT->post->bool('save')) return false;
1155
1156
        if(!$INPUT->post->str('login')) {
1157
            msg($lang['resendpwdmissing'], -1);
1158
            return false;
1159
        } else {
1160
            $user = trim($auth->cleanUser($INPUT->post->str('login')));
1161
        }
1162
1163
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1164
        if(!$userinfo['mail']) {
1165
            msg($lang['resendpwdnouser'], -1);
1166
            return false;
1167
        }
1168
1169
        // generate auth token
1170
        $token = md5(auth_randombytes(16)); // random secret
1171
        $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
1172
        $url   = wl('', array('do'=> 'resendpwd', 'pwauth'=> $token), true, '&');
1173
1174
        io_saveFile($tfile, $user);
1175
1176
        $text = rawLocale('pwconfirm');
1177
        $trep = array(
1178
            'FULLNAME' => $userinfo['name'],
1179
            'LOGIN'    => $user,
1180
            'CONFIRM'  => $url
1181
        );
1182
1183
        $mail = new Mailer();
1184
        $mail->to($userinfo['name'].' <'.$userinfo['mail'].'>');
1185
        $mail->subject($lang['regpwmail']);
1186
        $mail->setBody($text, $trep);
1187
        if($mail->send()) {
1188
            msg($lang['resendpwdconfirm'], 1);
1189
        } else {
1190
            msg($lang['regmailfail'], -1);
1191
        }
1192
        return true;
1193
    }
1194
    // never reached
1195
}
1196
1197
/**
1198
 * Encrypts a password using the given method and salt
1199
 *
1200
 * If the selected method needs a salt and none was given, a random one
1201
 * is chosen.
1202
 *
1203
 * @author  Andreas Gohr <[email protected]>
1204
 *
1205
 * @param string $clear The clear text password
1206
 * @param string $method The hashing method
1207
 * @param string $salt A salt, null for random
1208
 * @return  string  The crypted password
1209
 */
1210
function auth_cryptPassword($clear, $method = '', $salt = null) {
1211
    global $conf;
1212
    if(empty($method)) $method = $conf['passcrypt'];
1213
1214
    $pass = new PassHash();
1215
    $call = 'hash_'.$method;
1216
1217
    if(!method_exists($pass, $call)) {
1218
        msg("Unsupported crypt method $method", -1);
1219
        return false;
1220
    }
1221
1222
    return $pass->$call($clear, $salt);
1223
}
1224
1225
/**
1226
 * Verifies a cleartext password against a crypted hash
1227
 *
1228
 * @author Andreas Gohr <[email protected]>
1229
 *
1230
 * @param  string $clear The clear text password
1231
 * @param  string $crypt The hash to compare with
1232
 * @return bool true if both match
1233
 */
1234
function auth_verifyPassword($clear, $crypt) {
1235
    $pass = new PassHash();
1236
    return $pass->verify_hash($clear, $crypt);
1237
}
1238
1239
/**
1240
 * Set the authentication cookie and add user identification data to the session
1241
 *
1242
 * @param string  $user       username
1243
 * @param string  $pass       encrypted password
1244
 * @param bool    $sticky     whether or not the cookie will last beyond the session
1245
 * @return bool
1246
 */
1247
function auth_setCookie($user, $pass, $sticky) {
1248
    global $conf;
1249
    /* @var AuthPlugin $auth */
1250
    global $auth;
1251
    global $USERINFO;
1252
1253
    if(!$auth) return false;
1254
    $USERINFO = $auth->getUserData($user);
1255
1256
    // set cookie
1257
    $cookie    = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode($pass);
1258
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
1259
    $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; //one year
1260
    setcookie(DOKU_COOKIE, $cookie, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
1261
1262
    // set session
1263
    $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
1264
    $_SESSION[DOKU_COOKIE]['auth']['pass'] = sha1($pass);
1265
    $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid();
1266
    $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
1267
    $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
1268
1269
    return true;
1270
}
1271
1272
/**
1273
 * Returns the user, (encrypted) password and sticky bit from cookie
1274
 *
1275
 * @returns array
1276
 */
1277
function auth_getCookie() {
1278
    if(!isset($_COOKIE[DOKU_COOKIE])) {
1279
        return array(null, null, null);
1280
    }
1281
    list($user, $sticky, $pass) = explode('|', $_COOKIE[DOKU_COOKIE], 3);
1282
    $sticky = (bool) $sticky;
1283
    $pass   = base64_decode($pass);
1284
    $user   = base64_decode($user);
1285
    return array($user, $sticky, $pass);
1286
}
1287
1288
//Setup VIM: ex: et ts=2 :
1289