Completed
Pull Request — master (#3318)
by
unknown
03:38
created

auth.php ➔ auth_ismanager()   C

Complexity

Conditions 12
Paths 32

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 32
nop 4
dl 0
loc 44
rs 6.9666
c 0
b 0
f 0

How to fix   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
use dokuwiki\Extension\AuthPlugin;
13
use dokuwiki\Extension\Event;
14
use dokuwiki\Extension\PluginController;
15
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...
16
use dokuwiki\Subscriptions\RegistrationSubscriptionSender;
17
18
/**
19
 * Initialize the auth system.
20
 *
21
 * This function is automatically called at the end of init.php
22
 *
23
 * This used to be the main() of the auth.php
24
 *
25
 * @todo backend loading maybe should be handled by the class autoloader
26
 * @todo maybe split into multiple functions at the XXX marked positions
27
 * @triggers AUTH_LOGIN_CHECK
28
 * @return bool
29
 */
30
function auth_setup() {
31
    global $conf;
32
    /* @var AuthPlugin $auth */
33
    global $auth;
34
    /* @var Input $INPUT */
35
    global $INPUT;
36
    global $AUTH_ACL;
37
    global $lang;
38
    /* @var PluginController $plugin_controller */
39
    global $plugin_controller;
40
    $AUTH_ACL = array();
41
42
    if(!$conf['useacl']) return false;
43
44
    // try to load auth backend from plugins
45
    foreach ($plugin_controller->getList('auth') as $plugin) {
46
        if ($conf['authtype'] === $plugin) {
47
            $auth = $plugin_controller->load('auth', $plugin);
48
            break;
49
        }
50
    }
51
52
    if(!isset($auth) || !$auth){
53
        msg($lang['authtempfail'], -1);
54
        return false;
55
    }
56
57
    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...
58
        // degrade to unauthenticated user
59
        unset($auth);
60
        auth_logoff();
61
        msg($lang['authtempfail'], -1);
62
        return false;
63
    }
64
65
    // do the login either by cookie or provided credentials XXX
66
    $INPUT->set('http_credentials', false);
67
    if(!$conf['rememberme']) $INPUT->set('r', false);
68
69
    // handle renamed HTTP_AUTHORIZATION variable (can happen when a fix like
70
    // the one presented at
71
    // http://www.besthostratings.com/articles/http-auth-php-cgi.html is used
72
    // for enabling HTTP authentication with CGI/SuExec)
73
    if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
74
        $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
75
    // streamline HTTP auth credentials (IIS/rewrite -> mod_php)
76
    if(isset($_SERVER['HTTP_AUTHORIZATION'])) {
77
        list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
78
            explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
79
    }
80
81
    // if no credentials were given try to use HTTP auth (for SSO)
82
    if(!$INPUT->str('u') && empty($_COOKIE[DOKU_COOKIE]) && !empty($_SERVER['PHP_AUTH_USER'])) {
83
        $INPUT->set('u', $_SERVER['PHP_AUTH_USER']);
84
        $INPUT->set('p', $_SERVER['PHP_AUTH_PW']);
85
        $INPUT->set('http_credentials', true);
86
    }
87
88
    // apply cleaning (auth specific user names, remove control chars)
89
    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...
90
        $INPUT->set('u', $auth->cleanUser(stripctl($INPUT->str('u'))));
91
        $INPUT->set('p', stripctl($INPUT->str('p')));
92
    }
93
94
    $ok = null;
95
    if (!is_null($auth) && $auth->canDo('external')) {
96
        $ok = $auth->trustExternal($INPUT->str('u'), $INPUT->str('p'), $INPUT->bool('r'));
97
    }
98
99
    if ($ok === null) {
100
        // external trust mechanism not in place, or returns no result,
101
        // then attempt auth_login
102
        $evdata = array(
103
            'user'     => $INPUT->str('u'),
104
            'password' => $INPUT->str('p'),
105
            'sticky'   => $INPUT->bool('r'),
106
            'silent'   => $INPUT->bool('http_credentials')
107
        );
108
        Event::createAndTrigger('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
109
    }
110
111
    //load ACL into a global array XXX
112
    $AUTH_ACL = auth_loadACL();
113
114
    return true;
115
}
116
117
/**
118
 * Loads the ACL setup and handle user wildcards
119
 *
120
 * @author Andreas Gohr <[email protected]>
121
 *
122
 * @return array
123
 */
124
function auth_loadACL() {
125
    global $config_cascade;
126
    global $USERINFO;
127
    /* @var Input $INPUT */
128
    global $INPUT;
129
130
    if(!is_readable($config_cascade['acl']['default'])) return array();
131
132
    $acl = file($config_cascade['acl']['default']);
133
134
    $out = array();
135
    foreach($acl as $line) {
136
        $line = trim($line);
137
        if(empty($line) || ($line[0] == '#')) continue; // skip blank lines & comments
138
        list($id,$rest) = preg_split('/[ \t]+/',$line,2);
139
140
        // substitute user wildcard first (its 1:1)
141
        if(strstr($line, '%USER%')){
142
            // if user is not logged in, this ACL line is meaningless - skip it
143
            if (!$INPUT->server->has('REMOTE_USER')) continue;
144
145
            $id   = str_replace('%USER%',cleanID($INPUT->server->str('REMOTE_USER')),$id);
146
            $rest = str_replace('%USER%',auth_nameencode($INPUT->server->str('REMOTE_USER')),$rest);
147
        }
148
149
        // substitute group wildcard (its 1:m)
150
        if(strstr($line, '%GROUP%')){
151
            // if user is not logged in, grps is empty, no output will be added (i.e. skipped)
152
            if(isset($USERINFO['grps'])){
153
                foreach((array) $USERINFO['grps'] as $grp){
154
                    $nid   = str_replace('%GROUP%',cleanID($grp),$id);
155
                    $nrest = str_replace('%GROUP%','@'.auth_nameencode($grp),$rest);
156
                    $out[] = "$nid\t$nrest";
157
                }
158
            }
159
        } else {
160
            $out[] = "$id\t$rest";
161
        }
162
    }
163
164
    return $out;
165
}
166
167
/**
168
 * Event hook callback for AUTH_LOGIN_CHECK
169
 *
170
 * @param array $evdata
171
 * @return bool
172
 */
173
function auth_login_wrapper($evdata) {
174
    return auth_login(
175
        $evdata['user'],
176
        $evdata['password'],
177
        $evdata['sticky'],
178
        $evdata['silent']
179
    );
180
}
181
182
/**
183
 * This tries to login the user based on the sent auth credentials
184
 *
185
 * The authentication works like this: if a username was given
186
 * a new login is assumed and user/password are checked. If they
187
 * are correct the password is encrypted with blowfish and stored
188
 * together with the username in a cookie - the same info is stored
189
 * in the session, too. Additonally a browserID is stored in the
190
 * session.
191
 *
192
 * If no username was given the cookie is checked: if the username,
193
 * crypted password and browserID match between session and cookie
194
 * no further testing is done and the user is accepted
195
 *
196
 * If a cookie was found but no session info was availabe the
197
 * blowfish encrypted password from the cookie is decrypted and
198
 * together with username rechecked by calling this function again.
199
 *
200
 * On a successful login $_SERVER[REMOTE_USER] and $USERINFO
201
 * are set.
202
 *
203
 * @author  Andreas Gohr <[email protected]>
204
 *
205
 * @param   string  $user    Username
206
 * @param   string  $pass    Cleartext Password
207
 * @param   bool    $sticky  Cookie should not expire
208
 * @param   bool    $silent  Don't show error on bad auth
209
 * @return  bool             true on successful auth
210
 */
211
function auth_login($user, $pass, $sticky = false, $silent = false) {
212
    global $USERINFO;
213
    global $conf;
214
    global $lang;
215
    /* @var AuthPlugin $auth */
216
    global $auth;
217
    /* @var Input $INPUT */
218
    global $INPUT;
219
220
    $sticky ? $sticky = true : $sticky = false; //sanity check
221
222
    if(!$auth) return false;
223
224
    if(!empty($user)) {
225
        //usual login
226
        if(!empty($pass) && $auth->checkPass($user, $pass)) {
227
            // make logininfo globally available
228
            $INPUT->server->set('REMOTE_USER', $user);
229
            $secret                 = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
230
            auth_setCookie($user, auth_encrypt($pass, $secret), $sticky);
231
            return true;
232
        } else {
233
            //invalid credentials - log off
234
            if(!$silent) {
235
                http_status(403, 'Login failed');
236
                msg($lang['badlogin'], -1);
237
            }
238
            auth_logoff();
239
            return false;
240
        }
241
    } else {
242
        // read cookie information
243
        list($user, $sticky, $pass) = auth_getCookie();
244
        if($user && $pass) {
245
            // we got a cookie - see if we can trust it
246
247
            // get session info
248
            $session = $_SESSION[DOKU_COOKIE]['auth'];
249
            if(isset($session) &&
250
                $auth->useSessionCache($user) &&
251
                ($session['time'] >= time() - $conf['auth_security_timeout']) &&
252
                ($session['user'] == $user) &&
253
                ($session['pass'] == sha1($pass)) && //still crypted
254
                ($session['buid'] == auth_browseruid())
255
            ) {
256
257
                // he has session, cookie and browser right - let him in
258
                $INPUT->server->set('REMOTE_USER', $user);
259
                $USERINFO               = $session['info']; //FIXME move all references to session
260
                return true;
261
            }
262
            // no we don't trust it yet - recheck pass but silent
263
            $secret = auth_cookiesalt(!$sticky, true); //bind non-sticky to session
264
            $pass   = auth_decrypt($pass, $secret);
265
            return auth_login($user, $pass, $sticky, true);
266
        }
267
    }
268
    //just to be sure
269
    auth_logoff(true);
270
    return false;
271
}
272
273
/**
274
 * Builds a pseudo UID from browser and IP data
275
 *
276
 * This is neither unique nor unfakable - still it adds some
277
 * security. Using the first part of the IP makes sure
278
 * proxy farms like AOLs are still okay.
279
 *
280
 * @author  Andreas Gohr <[email protected]>
281
 *
282
 * @return  string  a MD5 sum of various browser headers
283
 */
284
function auth_browseruid() {
285
    /* @var Input $INPUT */
286
    global $INPUT;
287
288
    $ip  = clientIP(true);
289
    $uid = '';
290
    $uid .= $INPUT->server->str('HTTP_USER_AGENT');
291
    $uid .= $INPUT->server->str('HTTP_ACCEPT_CHARSET');
292
    $uid .= substr($ip, 0, strpos($ip, '.'));
293
    $uid = \dokuwiki\Utf8\PhpString::strtolower($uid);
294
    return md5($uid);
295
}
296
297
/**
298
 * Creates a random key to encrypt the password in cookies
299
 *
300
 * This function tries to read the password for encrypting
301
 * cookies from $conf['metadir'].'/_htcookiesalt'
302
 * if no such file is found a random key is created and
303
 * and stored in this file.
304
 *
305
 * @author  Andreas Gohr <[email protected]>
306
 *
307
 * @param   bool $addsession if true, the sessionid is added to the salt
308
 * @param   bool $secure     if security is more important than keeping the old value
309
 * @return  string
310
 */
311
function auth_cookiesalt($addsession = false, $secure = false) {
312
    if (defined('SIMPLE_TEST')) {
313
        return 'test';
314
    }
315
    global $conf;
316
    $file = $conf['metadir'].'/_htcookiesalt';
317
    if ($secure || !file_exists($file)) {
318
        $file = $conf['metadir'].'/_htcookiesalt2';
319
    }
320
    $salt = io_readFile($file);
321
    if(empty($salt)) {
322
        $salt = bin2hex(auth_randombytes(64));
323
        io_saveFile($file, $salt);
324
    }
325
    if($addsession) {
326
        $salt .= session_id();
327
    }
328
    return $salt;
329
}
330
331
/**
332
 * Return cryptographically secure random bytes.
333
 *
334
 * @author Niklas Keller <[email protected]>
335
 *
336
 * @param int $length number of bytes
337
 * @return string cryptographically secure random bytes
338
 */
339
function auth_randombytes($length) {
340
    return random_bytes($length);
341
}
342
343
/**
344
 * Cryptographically secure random number generator.
345
 *
346
 * @author Niklas Keller <[email protected]>
347
 *
348
 * @param int $min
349
 * @param int $max
350
 * @return int
351
 */
352
function auth_random($min, $max) {
353
    return random_int($min, $max);
354
}
355
356
/**
357
 * Encrypt data using the given secret using AES
358
 *
359
 * The mode is CBC with a random initialization vector, the key is derived
360
 * using pbkdf2.
361
 *
362
 * @param string $data   The data that shall be encrypted
363
 * @param string $secret The secret/password that shall be used
364
 * @return string The ciphertext
365
 */
366
function auth_encrypt($data, $secret) {
367
    $iv     = auth_randombytes(16);
368
    $cipher = new \phpseclib\Crypt\AES();
369
    $cipher->setPassword($secret);
370
371
    /*
372
    this uses the encrypted IV as IV as suggested in
373
    http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, Appendix C
374
    for unique but necessarily random IVs. The resulting ciphertext is
375
    compatible to ciphertext that was created using a "normal" IV.
376
    */
377
    return $cipher->encrypt($iv.$data);
378
}
379
380
/**
381
 * Decrypt the given AES ciphertext
382
 *
383
 * The mode is CBC, the key is derived using pbkdf2
384
 *
385
 * @param string $ciphertext The encrypted data
386
 * @param string $secret     The secret/password that shall be used
387
 * @return string The decrypted data
388
 */
389
function auth_decrypt($ciphertext, $secret) {
390
    $iv     = substr($ciphertext, 0, 16);
391
    $cipher = new \phpseclib\Crypt\AES();
392
    $cipher->setPassword($secret);
393
    $cipher->setIV($iv);
394
395
    return $cipher->decrypt(substr($ciphertext, 16));
396
}
397
398
/**
399
 * Log out the current user
400
 *
401
 * This clears all authentication data and thus log the user
402
 * off. It also clears session data.
403
 *
404
 * @author  Andreas Gohr <[email protected]>
405
 *
406
 * @param bool $keepbc - when true, the breadcrumb data is not cleared
407
 */
408
function auth_logoff($keepbc = false) {
409
    global $conf;
410
    global $USERINFO;
411
    /* @var AuthPlugin $auth */
412
    global $auth;
413
    /* @var Input $INPUT */
414
    global $INPUT;
415
416
    // make sure the session is writable (it usually is)
417
    @session_start();
418
419
    if(isset($_SESSION[DOKU_COOKIE]['auth']['user']))
420
        unset($_SESSION[DOKU_COOKIE]['auth']['user']);
421
    if(isset($_SESSION[DOKU_COOKIE]['auth']['pass']))
422
        unset($_SESSION[DOKU_COOKIE]['auth']['pass']);
423
    if(isset($_SESSION[DOKU_COOKIE]['auth']['info']))
424
        unset($_SESSION[DOKU_COOKIE]['auth']['info']);
425
    if(!$keepbc && isset($_SESSION[DOKU_COOKIE]['bc']))
426
        unset($_SESSION[DOKU_COOKIE]['bc']);
427
    $INPUT->server->remove('REMOTE_USER');
428
    $USERINFO = null; //FIXME
429
430
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
431
    setcookie(DOKU_COOKIE, '', time() - 600000, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
432
433
    if($auth) $auth->logOff();
434
}
435
436
/**
437
 * Check if a user is a manager
438
 *
439
 * Should usually be called without any parameters to check the current
440
 * user.
441
 *
442
 * The info is available through $INFO['ismanager'], too
443
 *
444
 * @param string $user Username
445
 * @param array $groups List of groups the user is in
446
 * @param bool $adminonly when true checks if user is admin
447
 * @param bool $recache set to true to refresh the cache
448
 * @return bool
449
 * @see    auth_isadmin
450
 *
451
 * @author Andreas Gohr <[email protected]>
452
 */
453
function auth_ismanager($user = null, $groups = null, $adminonly = false, $recache=false) {
454
    global $conf;
455
    global $USERINFO;
456
    /* @var AuthPlugin $auth */
457
    global $auth;
458
    /* @var Input $INPUT */
459
    global $INPUT;
460
461
462
    if(!$auth) return false;
463
    if(is_null($user)) {
464
        if(!$INPUT->server->has('REMOTE_USER')) {
465
            return false;
466
        } else {
467
            $user = $INPUT->server->str('REMOTE_USER');
468
        }
469
    }
470
    if(is_null($groups)) {
471
        if ($user === $INPUT->server->str('REMOTE_USER')) {
472
            $groups = $USERINFO ? (array) $USERINFO['grps'] : array();
473
        } else {
474
            $groups = !empty($auth->getUserData($user)['grps']) ?
475
                $auth->getUserData($user)['grps'] :
476
                [];
477
        }
478
    }
479
480
    // prefer cached result
481
    static $cache = [];
482
    $cachekey = serialize([$user, $adminonly, $groups]);
483
    if (!isset($cache[$cachekey]) || $recache) {
484
        // check superuser match
485
        $ok = auth_isMember($conf['superuser'], $user, $groups);
486
487
        // check managers
488
        if (!$ok && !$adminonly) {
489
            $ok = auth_isMember($conf['manager'], $user, $groups);
490
        }
491
492
        $cache[$cachekey] = $ok;
493
    }
494
495
    return $cache[$cachekey];
496
}
497
498
/**
499
 * Check if a user is admin
500
 *
501
 * Alias to auth_ismanager with adminonly=true
502
 *
503
 * The info is available through $INFO['isadmin'], too
504
 *
505
 * @param string $user Username
506
 * @param array $groups List of groups the user is in
507
 * @param bool $recache set to true to refresh the cache
508
 * @return bool
509
 * @author Andreas Gohr <[email protected]>
510
 * @see auth_ismanager()
511
 *
512
 */
513
function auth_isadmin($user = null, $groups = null, $recache=false) {
514
    return auth_ismanager($user, $groups, true, $recache);
515
}
516
517
/**
518
 * Match a user and his groups against a comma separated list of
519
 * users and groups to determine membership status
520
 *
521
 * Note: all input should NOT be nameencoded.
522
 *
523
 * @param string $memberlist commaseparated list of allowed users and groups
524
 * @param string $user       user to match against
525
 * @param array  $groups     groups the user is member of
526
 * @return bool       true for membership acknowledged
527
 */
528
function auth_isMember($memberlist, $user, array $groups) {
529
    /* @var AuthPlugin $auth */
530
    global $auth;
531
    if(!$auth) return false;
532
533
    // clean user and groups
534
    if(!$auth->isCaseSensitive()) {
535
        $user   = \dokuwiki\Utf8\PhpString::strtolower($user);
536
        $groups = array_map([\dokuwiki\Utf8\PhpString::class, 'strtolower'], $groups);
537
    }
538
    $user   = $auth->cleanUser($user);
539
    $groups = array_map(array($auth, 'cleanGroup'), $groups);
540
541
    // extract the memberlist
542
    $members = explode(',', $memberlist);
543
    $members = array_map('trim', $members);
544
    $members = array_unique($members);
545
    $members = array_filter($members);
546
547
    // compare cleaned values
548
    foreach($members as $member) {
549
        if($member == '@ALL' ) return true;
550
        if(!$auth->isCaseSensitive()) $member = \dokuwiki\Utf8\PhpString::strtolower($member);
551
        if($member[0] == '@') {
552
            $member = $auth->cleanGroup(substr($member, 1));
553
            if(in_array($member, $groups)) return true;
554
        } else {
555
            $member = $auth->cleanUser($member);
556
            if($member == $user) return true;
557
        }
558
    }
559
560
    // still here? not a member!
561
    return false;
562
}
563
564
/**
565
 * Convinience function for auth_aclcheck()
566
 *
567
 * This checks the permissions for the current user
568
 *
569
 * @author  Andreas Gohr <[email protected]>
570
 *
571
 * @param  string  $id  page ID (needs to be resolved and cleaned)
572
 * @return int          permission level
573
 */
574
function auth_quickaclcheck($id) {
575
    global $conf;
576
    global $USERINFO;
577
    /* @var Input $INPUT */
578
    global $INPUT;
579
    # if no ACL is used always return upload rights
580
    if(!$conf['useacl']) return AUTH_UPLOAD;
581
    return auth_aclcheck($id, $INPUT->server->str('REMOTE_USER'), is_array($USERINFO) ? $USERINFO['grps'] : array());
582
}
583
584
/**
585
 * Returns the maximum rights a user has for the given ID or its namespace
586
 *
587
 * @author  Andreas Gohr <[email protected]>
588
 *
589
 * @triggers AUTH_ACL_CHECK
590
 * @param  string       $id     page ID (needs to be resolved and cleaned)
591
 * @param  string       $user   Username
592
 * @param  array|null   $groups Array of groups the user is in
593
 * @return int             permission level
594
 */
595
function auth_aclcheck($id, $user, $groups) {
596
    $data = array(
597
        'id'     => $id,
598
        'user'   => $user,
599
        'groups' => $groups
600
    );
601
602
    return Event::createAndTrigger('AUTH_ACL_CHECK', $data, 'auth_aclcheck_cb');
603
}
604
605
/**
606
 * default ACL check method
607
 *
608
 * DO NOT CALL DIRECTLY, use auth_aclcheck() instead
609
 *
610
 * @author  Andreas Gohr <[email protected]>
611
 *
612
 * @param  array $data event data
613
 * @return int   permission level
614
 */
615
function auth_aclcheck_cb($data) {
616
    $id     =& $data['id'];
617
    $user   =& $data['user'];
618
    $groups =& $data['groups'];
619
620
    global $conf;
621
    global $AUTH_ACL;
622
    /* @var AuthPlugin $auth */
623
    global $auth;
624
625
    // if no ACL is used always return upload rights
626
    if(!$conf['useacl']) return AUTH_UPLOAD;
627
    if(!$auth) return AUTH_NONE;
628
629
    //make sure groups is an array
630
    if(!is_array($groups)) $groups = array();
631
632
    //if user is superuser or in superusergroup return 255 (acl_admin)
633
    if(auth_isadmin($user, $groups)) {
634
        return AUTH_ADMIN;
635
    }
636
637
    if(!$auth->isCaseSensitive()) {
638
        $user   = \dokuwiki\Utf8\PhpString::strtolower($user);
639
        $groups = array_map('utf8_strtolower', $groups);
640
    }
641
    $user   = auth_nameencode($auth->cleanUser($user));
642
    $groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
643
644
    //prepend groups with @ and nameencode
645
    foreach($groups as &$group) {
646
        $group = '@'.auth_nameencode($group);
647
    }
648
649
    $ns   = getNS($id);
650
    $perm = -1;
651
652
    //add ALL group
653
    $groups[] = '@ALL';
654
655
    //add User
656
    if($user) $groups[] = $user;
657
658
    //check exact match first
659
    $matches = preg_grep('/^'.preg_quote($id, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
660
    if(count($matches)) {
661
        foreach($matches as $match) {
662
            $match = preg_replace('/#.*$/', '', $match); //ignore comments
663
            $acl   = preg_split('/[ \t]+/', $match);
664
            if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
665
                $acl[1] = \dokuwiki\Utf8\PhpString::strtolower($acl[1]);
666
            }
667
            if(!in_array($acl[1], $groups)) {
668
                continue;
669
            }
670
            if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
671
            if($acl[2] > $perm) {
672
                $perm = $acl[2];
673
            }
674
        }
675
        if($perm > -1) {
676
            //we had a match - return it
677
            return (int) $perm;
678
        }
679
    }
680
681
    //still here? do the namespace checks
682
    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...
683
        $path = $ns.':*';
684
    } else {
685
        $path = '*'; //root document
686
    }
687
688
    do {
689
        $matches = preg_grep('/^'.preg_quote($path, '/').'[ \t]+([^ \t]+)[ \t]+/', $AUTH_ACL);
690
        if(count($matches)) {
691
            foreach($matches as $match) {
692
                $match = preg_replace('/#.*$/', '', $match); //ignore comments
693
                $acl   = preg_split('/[ \t]+/', $match);
694
                if(!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
695
                    $acl[1] = \dokuwiki\Utf8\PhpString::strtolower($acl[1]);
696
                }
697
                if(!in_array($acl[1], $groups)) {
698
                    continue;
699
                }
700
                if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
701
                if($acl[2] > $perm) {
702
                    $perm = $acl[2];
703
                }
704
            }
705
            //we had a match - return it
706
            if($perm != -1) {
707
                return (int) $perm;
708
            }
709
        }
710
        //get next higher namespace
711
        $ns = getNS($ns);
0 ignored issues
show
Security Bug introduced by
It seems like $ns defined by getNS($ns) on line 711 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...
712
713
        if($path != '*') {
714
            $path = $ns.':*';
715
            if($path == ':*') $path = '*';
716
        } else {
717
            //we did this already
718
            //looks like there is something wrong with the ACL
719
            //break here
720
            msg('No ACL setup yet! Denying access to everyone.');
721
            return AUTH_NONE;
722
        }
723
    } while(1); //this should never loop endless
724
    return AUTH_NONE;
725
}
726
727
/**
728
 * Encode ASCII special chars
729
 *
730
 * Some auth backends allow special chars in their user and groupnames
731
 * The special chars are encoded with this function. Only ASCII chars
732
 * are encoded UTF-8 multibyte are left as is (different from usual
733
 * urlencoding!).
734
 *
735
 * Decoding can be done with rawurldecode
736
 *
737
 * @author Andreas Gohr <[email protected]>
738
 * @see rawurldecode()
739
 *
740
 * @param string $name
741
 * @param bool $skip_group
742
 * @return string
743
 */
744
function auth_nameencode($name, $skip_group = false) {
745
    global $cache_authname;
746
    $cache =& $cache_authname;
747
    $name  = (string) $name;
748
749
    // never encode wildcard FS#1955
750
    if($name == '%USER%') return $name;
751
    if($name == '%GROUP%') return $name;
752
753
    if(!isset($cache[$name][$skip_group])) {
754
        if($skip_group && $name[0] == '@') {
755
            $cache[$name][$skip_group] = '@'.preg_replace_callback(
756
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
757
                'auth_nameencode_callback', substr($name, 1)
758
            );
759
        } else {
760
            $cache[$name][$skip_group] = preg_replace_callback(
761
                '/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/',
762
                'auth_nameencode_callback', $name
763
            );
764
        }
765
    }
766
767
    return $cache[$name][$skip_group];
768
}
769
770
/**
771
 * callback encodes the matches
772
 *
773
 * @param array $matches first complete match, next matching subpatterms
774
 * @return string
775
 */
776
function auth_nameencode_callback($matches) {
777
    return '%'.dechex(ord(substr($matches[1],-1)));
778
}
779
780
/**
781
 * Create a pronouncable password
782
 *
783
 * The $foruser variable might be used by plugins to run additional password
784
 * policy checks, but is not used by the default implementation
785
 *
786
 * @author   Andreas Gohr <[email protected]>
787
 * @link     http://www.phpbuilder.com/annotate/message.php3?id=1014451
788
 * @triggers AUTH_PASSWORD_GENERATE
789
 *
790
 * @param  string $foruser username for which the password is generated
791
 * @return string  pronouncable password
792
 */
793
function auth_pwgen($foruser = '') {
794
    $data = array(
795
        'password' => '',
796
        'foruser'  => $foruser
797
    );
798
799
    $evt = new Event('AUTH_PASSWORD_GENERATE', $data);
800
    if($evt->advise_before(true)) {
801
        $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
802
        $v = 'aeiou'; //vowels
803
        $a = $c.$v; //both
804
        $s = '!$%&?+*~#-_:.;,'; // specials
805
806
        //use thre syllables...
807
        for($i = 0; $i < 3; $i++) {
808
            $data['password'] .= $c[auth_random(0, strlen($c) - 1)];
809
            $data['password'] .= $v[auth_random(0, strlen($v) - 1)];
810
            $data['password'] .= $a[auth_random(0, strlen($a) - 1)];
811
        }
812
        //... and add a nice number and special
813
        $data['password'] .= $s[auth_random(0, strlen($s) - 1)].auth_random(10, 99);
814
    }
815
    $evt->advise_after();
816
817
    return $data['password'];
818
}
819
820
/**
821
 * Sends a password to the given user
822
 *
823
 * @author  Andreas Gohr <[email protected]>
824
 *
825
 * @param string $user Login name of the user
826
 * @param string $password The new password in clear text
827
 * @return bool  true on success
828
 */
829
function auth_sendPassword($user, $password) {
830
    global $lang;
831
    /* @var AuthPlugin $auth */
832
    global $auth;
833
    if(!$auth) return false;
834
835
    $user     = $auth->cleanUser($user);
836
    $userinfo = $auth->getUserData($user, $requireGroups = false);
837
838
    if(!$userinfo['mail']) return false;
839
840
    $text = rawLocale('password');
841
    $trep = array(
842
        'FULLNAME' => $userinfo['name'],
843
        'LOGIN'    => $user,
844
        'PASSWORD' => $password
845
    );
846
847
    $mail = new Mailer();
848
    $mail->to($mail->getCleanName($userinfo['name']).' <'.$userinfo['mail'].'>');
849
    $mail->subject($lang['regpwmail']);
850
    $mail->setBody($text, $trep);
851
    return $mail->send();
852
}
853
854
/**
855
 * Register a new user
856
 *
857
 * This registers a new user - Data is read directly from $_POST
858
 *
859
 * @author  Andreas Gohr <[email protected]>
860
 *
861
 * @return bool  true on success, false on any error
862
 */
863
function register() {
864
    global $lang;
865
    global $conf;
866
    /* @var \dokuwiki\Extension\AuthPlugin $auth */
867
    global $auth;
868
    global $INPUT;
869
870
    if(!$INPUT->post->bool('save')) return false;
871
    if(!actionOK('register')) return false;
872
873
    // gather input
874
    $login    = trim($auth->cleanUser($INPUT->post->str('login')));
875
    $fullname = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('fullname')));
876
    $email    = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $INPUT->post->str('email')));
877
    $pass     = $INPUT->post->str('pass');
878
    $passchk  = $INPUT->post->str('passchk');
879
880
    if(empty($login) || empty($fullname) || empty($email)) {
881
        msg($lang['regmissing'], -1);
882
        return false;
883
    }
884
885
    if($conf['autopasswd']) {
886
        $pass = auth_pwgen($login); // automatically generate password
887
    } elseif(empty($pass) || empty($passchk)) {
888
        msg($lang['regmissing'], -1); // complain about missing passwords
889
        return false;
890
    } elseif($pass != $passchk) {
891
        msg($lang['regbadpass'], -1); // complain about misspelled passwords
892
        return false;
893
    }
894
895
    //check mail
896
    if(!mail_isvalid($email)) {
897
        msg($lang['regbadmail'], -1);
898
        return false;
899
    }
900
901
    //okay try to create the user
902
    if(!$auth->triggerUserMod('create', array($login, $pass, $fullname, $email))) {
903
        msg($lang['regfail'], -1);
904
        return false;
905
    }
906
907
    // send notification about the new user
908
    $subscription = new RegistrationSubscriptionSender();
909
    $subscription->sendRegister($login, $fullname, $email);
910
911
    // are we done?
912
    if(!$conf['autopasswd']) {
913
        msg($lang['regsuccess2'], 1);
914
        return true;
915
    }
916
917
    // autogenerated password? then send password to user
918
    if(auth_sendPassword($login, $pass)) {
919
        msg($lang['regsuccess'], 1);
920
        return true;
921
    } else {
922
        msg($lang['regmailfail'], -1);
923
        return false;
924
    }
925
}
926
927
/**
928
 * Update user profile
929
 *
930
 * @author    Christopher Smith <[email protected]>
931
 */
932
function updateprofile() {
933
    global $conf;
934
    global $lang;
935
    /* @var AuthPlugin $auth */
936
    global $auth;
937
    /* @var Input $INPUT */
938
    global $INPUT;
939
940
    if(!$INPUT->post->bool('save')) return false;
941
    if(!checkSecurityToken()) return false;
942
943
    if(!actionOK('profile')) {
944
        msg($lang['profna'], -1);
945
        return false;
946
    }
947
948
    $changes         = array();
949
    $changes['pass'] = $INPUT->post->str('newpass');
950
    $changes['name'] = $INPUT->post->str('fullname');
951
    $changes['mail'] = $INPUT->post->str('email');
952
953
    // check misspelled passwords
954
    if($changes['pass'] != $INPUT->post->str('passchk')) {
955
        msg($lang['regbadpass'], -1);
956
        return false;
957
    }
958
959
    // clean fullname and email
960
    $changes['name'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['name']));
961
    $changes['mail'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/', '', $changes['mail']));
962
963
    // no empty name and email (except the backend doesn't support them)
964
    if((empty($changes['name']) && $auth->canDo('modName')) ||
965
        (empty($changes['mail']) && $auth->canDo('modMail'))
966
    ) {
967
        msg($lang['profnoempty'], -1);
968
        return false;
969
    }
970
    if(!mail_isvalid($changes['mail']) && $auth->canDo('modMail')) {
971
        msg($lang['regbadmail'], -1);
972
        return false;
973
    }
974
975
    $changes = array_filter($changes);
976
977
    // check for unavailable capabilities
978
    if(!$auth->canDo('modName')) unset($changes['name']);
979
    if(!$auth->canDo('modMail')) unset($changes['mail']);
980
    if(!$auth->canDo('modPass')) unset($changes['pass']);
981
982
    // anything to do?
983
    if(!count($changes)) {
984
        msg($lang['profnochange'], -1);
985
        return false;
986
    }
987
988
    if($conf['profileconfirm']) {
989
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
990
            msg($lang['badpassconfirm'], -1);
991
            return false;
992
        }
993
    }
994
995
    if(!$auth->triggerUserMod('modify', array($INPUT->server->str('REMOTE_USER'), &$changes))) {
996
        msg($lang['proffail'], -1);
997
        return false;
998
    }
999
1000
    if($changes['pass']) {
1001
        // update cookie and session with the changed data
1002
        list( /*user*/, $sticky, /*pass*/) = auth_getCookie();
1003
        $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...
1004
        auth_setCookie($INPUT->server->str('REMOTE_USER'), $pass, (bool) $sticky);
1005
    } else {
1006
        // make sure the session is writable
1007
        @session_start();
1008
        // invalidate session cache
1009
        $_SESSION[DOKU_COOKIE]['auth']['time'] = 0;
1010
        session_write_close();
1011
    }
1012
1013
    return true;
1014
}
1015
1016
/**
1017
 * Delete the current logged-in user
1018
 *
1019
 * @return bool true on success, false on any error
1020
 */
1021
function auth_deleteprofile(){
1022
    global $conf;
1023
    global $lang;
1024
    /* @var \dokuwiki\Extension\AuthPlugin $auth */
1025
    global $auth;
1026
    /* @var Input $INPUT */
1027
    global $INPUT;
1028
1029
    if(!$INPUT->post->bool('delete')) return false;
1030
    if(!checkSecurityToken()) return false;
1031
1032
    // action prevented or auth module disallows
1033
    if(!actionOK('profile_delete') || !$auth->canDo('delUser')) {
1034
        msg($lang['profnodelete'], -1);
1035
        return false;
1036
    }
1037
1038
    if(!$INPUT->post->bool('confirm_delete')){
1039
        msg($lang['profconfdeletemissing'], -1);
1040
        return false;
1041
    }
1042
1043
    if($conf['profileconfirm']) {
1044
        if(!$auth->checkPass($INPUT->server->str('REMOTE_USER'), $INPUT->post->str('oldpass'))) {
1045
            msg($lang['badpassconfirm'], -1);
1046
            return false;
1047
        }
1048
    }
1049
1050
    $deleted = array();
1051
    $deleted[] = $INPUT->server->str('REMOTE_USER');
1052
    if($auth->triggerUserMod('delete', array($deleted))) {
1053
        // force and immediate logout including removing the sticky cookie
1054
        auth_logoff();
1055
        return true;
1056
    }
1057
1058
    return false;
1059
}
1060
1061
/**
1062
 * Send a  new password
1063
 *
1064
 * This function handles both phases of the password reset:
1065
 *
1066
 *   - handling the first request of password reset
1067
 *   - validating the password reset auth token
1068
 *
1069
 * @author Benoit Chesneau <[email protected]>
1070
 * @author Chris Smith <[email protected]>
1071
 * @author Andreas Gohr <[email protected]>
1072
 *
1073
 * @return bool true on success, false on any error
1074
 */
1075
function act_resendpwd() {
1076
    global $lang;
1077
    global $conf;
1078
    /* @var AuthPlugin $auth */
1079
    global $auth;
1080
    /* @var Input $INPUT */
1081
    global $INPUT;
1082
1083
    if(!actionOK('resendpwd')) {
1084
        msg($lang['resendna'], -1);
1085
        return false;
1086
    }
1087
1088
    $token = preg_replace('/[^a-f0-9]+/', '', $INPUT->str('pwauth'));
1089
1090
    if($token) {
1091
        // we're in token phase - get user info from token
1092
1093
        $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
1094
        if(!file_exists($tfile)) {
1095
            msg($lang['resendpwdbadauth'], -1);
1096
            $INPUT->remove('pwauth');
1097
            return false;
1098
        }
1099
        // token is only valid for 3 days
1100
        if((time() - filemtime($tfile)) > (3 * 60 * 60 * 24)) {
1101
            msg($lang['resendpwdbadauth'], -1);
1102
            $INPUT->remove('pwauth');
1103
            @unlink($tfile);
1104
            return false;
1105
        }
1106
1107
        $user     = io_readfile($tfile);
1108
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1109
        if(!$userinfo['mail']) {
1110
            msg($lang['resendpwdnouser'], -1);
1111
            return false;
1112
        }
1113
1114
        if(!$conf['autopasswd']) { // we let the user choose a password
1115
            $pass = $INPUT->str('pass');
1116
1117
            // password given correctly?
1118
            if(!$pass) return false;
1119
            if($pass != $INPUT->str('passchk')) {
1120
                msg($lang['regbadpass'], -1);
1121
                return false;
1122
            }
1123
1124
            // change it
1125
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1126
                msg($lang['proffail'], -1);
1127
                return false;
1128
            }
1129
1130
        } else { // autogenerate the password and send by mail
1131
1132
            $pass = auth_pwgen($user);
1133
            if(!$auth->triggerUserMod('modify', array($user, array('pass' => $pass)))) {
1134
                msg($lang['proffail'], -1);
1135
                return false;
1136
            }
1137
1138
            if(auth_sendPassword($user, $pass)) {
1139
                msg($lang['resendpwdsuccess'], 1);
1140
            } else {
1141
                msg($lang['regmailfail'], -1);
1142
            }
1143
        }
1144
1145
        @unlink($tfile);
1146
        return true;
1147
1148
    } else {
1149
        // we're in request phase
1150
1151
        if(!$INPUT->post->bool('save')) return false;
1152
1153
        if(!$INPUT->post->str('login')) {
1154
            msg($lang['resendpwdmissing'], -1);
1155
            return false;
1156
        } else {
1157
            $user = trim($auth->cleanUser($INPUT->post->str('login')));
1158
        }
1159
1160
        $userinfo = $auth->getUserData($user, $requireGroups = false);
1161
        if(!$userinfo['mail']) {
1162
            msg($lang['resendpwdnouser'], -1);
1163
            return false;
1164
        }
1165
1166
        // generate auth token
1167
        $token = md5(auth_randombytes(16)); // random secret
1168
        $tfile = $conf['cachedir'].'/'.$token[0].'/'.$token.'.pwauth';
1169
        $url   = wl('', array('do'=> 'resendpwd', 'pwauth'=> $token), true, '&');
1170
1171
        io_saveFile($tfile, $user);
1172
1173
        $text = rawLocale('pwconfirm');
1174
        $trep = array(
1175
            'FULLNAME' => $userinfo['name'],
1176
            'LOGIN'    => $user,
1177
            'CONFIRM'  => $url
1178
        );
1179
1180
        $mail = new Mailer();
1181
        $mail->to($userinfo['name'].' <'.$userinfo['mail'].'>');
1182
        $mail->subject($lang['regpwmail']);
1183
        $mail->setBody($text, $trep);
1184
        if($mail->send()) {
1185
            msg($lang['resendpwdconfirm'], 1);
1186
        } else {
1187
            msg($lang['regmailfail'], -1);
1188
        }
1189
        return true;
1190
    }
1191
    // never reached
1192
}
1193
1194
/**
1195
 * Encrypts a password using the given method and salt
1196
 *
1197
 * If the selected method needs a salt and none was given, a random one
1198
 * is chosen.
1199
 *
1200
 * @author  Andreas Gohr <[email protected]>
1201
 *
1202
 * @param string $clear The clear text password
1203
 * @param string $method The hashing method
1204
 * @param string $salt A salt, null for random
1205
 * @return  string  The crypted password
1206
 */
1207
function auth_cryptPassword($clear, $method = '', $salt = null) {
1208
    global $conf;
1209
    if(empty($method)) $method = $conf['passcrypt'];
1210
1211
    $pass = new PassHash();
1212
    $call = 'hash_'.$method;
1213
1214
    if(!method_exists($pass, $call)) {
1215
        msg("Unsupported crypt method $method", -1);
1216
        return false;
1217
    }
1218
1219
    return $pass->$call($clear, $salt);
1220
}
1221
1222
/**
1223
 * Verifies a cleartext password against a crypted hash
1224
 *
1225
 * @author Andreas Gohr <[email protected]>
1226
 *
1227
 * @param  string $clear The clear text password
1228
 * @param  string $crypt The hash to compare with
1229
 * @return bool true if both match
1230
 */
1231
function auth_verifyPassword($clear, $crypt) {
1232
    $pass = new PassHash();
1233
    return $pass->verify_hash($clear, $crypt);
1234
}
1235
1236
/**
1237
 * Set the authentication cookie and add user identification data to the session
1238
 *
1239
 * @param string  $user       username
1240
 * @param string  $pass       encrypted password
1241
 * @param bool    $sticky     whether or not the cookie will last beyond the session
1242
 * @return bool
1243
 */
1244
function auth_setCookie($user, $pass, $sticky) {
1245
    global $conf;
1246
    /* @var AuthPlugin $auth */
1247
    global $auth;
1248
    global $USERINFO;
1249
1250
    if(!$auth) return false;
1251
    $USERINFO = $auth->getUserData($user);
1252
1253
    // set cookie
1254
    $cookie    = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode($pass);
1255
    $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
1256
    $time      = $sticky ? (time() + 60 * 60 * 24 * 365) : 0; //one year
1257
    setcookie(DOKU_COOKIE, $cookie, $time, $cookieDir, '', ($conf['securecookie'] && is_ssl()), true);
1258
1259
    // set session
1260
    $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
1261
    $_SESSION[DOKU_COOKIE]['auth']['pass'] = sha1($pass);
1262
    $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid();
1263
    $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
1264
    $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
1265
1266
    return true;
1267
}
1268
1269
/**
1270
 * Returns the user, (encrypted) password and sticky bit from cookie
1271
 *
1272
 * @returns array
1273
 */
1274
function auth_getCookie() {
1275
    if(!isset($_COOKIE[DOKU_COOKIE])) {
1276
        return array(null, null, null);
1277
    }
1278
    list($user, $sticky, $pass) = explode('|', $_COOKIE[DOKU_COOKIE], 3);
1279
    $sticky = (bool) $sticky;
1280
    $pass   = base64_decode($pass);
1281
    $user   = base64_decode($user);
1282
    return array($user, $sticky, $pass);
1283
}
1284
1285
//Setup VIM: ex: et ts=2 :
1286