Failed Conditions
Pull Request — master (#3418)
by
unknown
03:00
created

auth.php ➔ auth_loadACL()   C

Complexity

Conditions 12
Paths 20

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 20
nop 0
dl 0
loc 52
rs 6.9666
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
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
    global $conf;
130
131
    if(!is_readable($config_cascade['acl']['default'])) return array();
132
133
    $acl = file($config_cascade['acl']['default']);
134
135
    $out = array();
136
    foreach($acl as $line) {
137
        $line = trim($line);
138
        if(empty($line) || ($line[0] == '#')) continue; // skip blank lines & comments
139
        list($id,$rest) = preg_split('/[ \t]+/',$line,2);
140
141
        // substitute user wildcard first (its 1:1)
142
        if(strstr($line, '%USER%')){
143
            // if user is not logged in, this ACL line is meaningless - skip it
144
            if (!$INPUT->server->has('REMOTE_USER')) continue;
145
146
            $id   = str_replace('%USER%',cleanID($INPUT->server->str('REMOTE_USER')),$id);
147
            $rest = str_replace('%USER%',auth_nameencode($INPUT->server->str('REMOTE_USER')),$rest);
148
        }
149
150
        // support for Translation plugin functionality
151
		if (
152
            ( plugin_enable ( 'translation' ) == true )
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

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