phpBB
Statistics
| Revision:

root / trunk / phpBB / includes / auth.php

History | View | Annotate | Download (29.3 kB)

1
<?php
2
/**
3
*
4
* @package phpBB3
5
* @copyright (c) 2005 phpBB Group
6
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
7
*
8
*/
9
10
/**
11
* @ignore
12
*/
13
if (!defined('IN_PHPBB'))
14
{
15
        exit;
16
}
17
18
/**
19
* Permission/Auth class
20
* @package phpBB3
21
*/
22
class auth
23
{
24
        var $acl = array();
25
        var $cache = array();
26
        var $acl_options = array();
27
        var $acl_forum_ids = false;
28
29
        /**
30
        * Init permissions
31
        */
32
        function acl(&$userdata)
33
        {
34
                global $db, $cache;
35
36
                $this->acl = $this->cache = $this->acl_options = array();
37
                $this->acl_forum_ids = false;
38
39
                if (($this->acl_options = $cache->get('_acl_options')) === false)
40
                {
41
                        $sql = 'SELECT auth_option_id, auth_option, is_global, is_local
42
                                FROM ' . ACL_OPTIONS_TABLE . '
43
                                ORDER BY auth_option_id';
44
                        $result = $db->sql_query($sql);
45
46
                        $global = $local = 0;
47
                        $this->acl_options = array();
48
                        while ($row = $db->sql_fetchrow($result))
49
                        {
50
                                if ($row['is_global'])
51
                                {
52
                                        $this->acl_options['global'][$row['auth_option']] = $global++;
53
                                }
54
55
                                if ($row['is_local'])
56
                                {
57
                                        $this->acl_options['local'][$row['auth_option']] = $local++;
58
                                }
59
60
                                $this->acl_options['id'][$row['auth_option']] = (int) $row['auth_option_id'];
61
                                $this->acl_options['option'][(int) $row['auth_option_id']] = $row['auth_option'];
62
                        }
63
                        $db->sql_freeresult($result);
64
65
                        $cache->put('_acl_options', $this->acl_options);
66
                }
67
68
                if (!trim($userdata['user_permissions']))
69
                {
70
                        $this->acl_cache($userdata);
71
                }
72
73
                // Fill ACL array
74
                $this->_fill_acl($userdata['user_permissions']);
75
76
                // Verify bitstring length with options provided...
77
                $renew = false;
78
                $global_length = sizeof($this->acl_options['global']);
79
                $local_length = sizeof($this->acl_options['local']);
80
81
                // Specify comparing length (bitstring is padded to 31 bits)
82
                $global_length = ($global_length % 31) ? ($global_length - ($global_length % 31) + 31) : $global_length;
83
                $local_length = ($local_length % 31) ? ($local_length - ($local_length % 31) + 31) : $local_length;
84
85
                // You thought we are finished now? Noooo... now compare them.
86
                foreach ($this->acl as $forum_id => $bitstring)
87
                {
88
                        if (($forum_id && strlen($bitstring) != $local_length) || (!$forum_id && strlen($bitstring) != $global_length))
89
                        {
90
                                $renew = true;
91
                                break;
92
                        }
93
                }
94
95
                // If a bitstring within the list does not match the options, we have a user with incorrect permissions set and need to renew them
96
                if ($renew)
97
                {
98
                        $this->acl_cache($userdata);
99
                        $this->_fill_acl($userdata['user_permissions']);
100
                }
101
102
                return;
103
        }
104
105
        /**
106
        * Fill ACL array with relevant bitstrings from user_permissions column
107
        * @access private
108
        */
109
        function _fill_acl($user_permissions)
110
        {
111
                $seq_cache = array();
112
                $this->acl = array();
113
                $user_permissions = explode("\n", $user_permissions);
114
115
                foreach ($user_permissions as $f => $seq)
116
                {
117
                        if ($seq)
118
                        {
119
                                $i = 0;
120
121
                                if (!isset($this->acl[$f]))
122
                                {
123
                                        $this->acl[$f] = '';
124
                                }
125
126
                                while ($subseq = substr($seq, $i, 6))
127
                                {
128
                                        if (isset($seq_cache[$subseq]))
129
                                        {
130
                                                $converted = $seq_cache[$subseq];
131
                                        }
132
                                        else
133
                                        {
134
                                                $converted = $seq_cache[$subseq] = str_pad(base_convert($subseq, 36, 2), 31, 0, STR_PAD_LEFT);
135
                                        }
136
137
                                        // We put the original bitstring into the acl array
138
                                        $this->acl[$f] .= $converted;
139
                                        $i += 6;
140
                                }
141
                        }
142
                }
143
        }
144
145
        /**
146
        * Look up an option
147
        * if the option is prefixed with !, then the result becomes negated
148
        *
149
        * If a forum id is specified the local option will be combined with a global option if one exist.
150
        * If a forum id is not specified, only the global option will be checked.
151
        */
152
        function acl_get($opt, $f = 0)
153
        {
154
                $negate = false;
155
156
                if (strpos($opt, '!') === 0)
157
                {
158
                        $negate = true;
159
                        $opt = substr($opt, 1);
160
                }
161
162
                if (!isset($this->cache[$f][$opt]))
163
                {
164
                        // We combine the global/local option with an OR because some options are global and local.
165
                        // If the user has the global permission the local one is true too and vice versa
166
                        $this->cache[$f][$opt] = false;
167
168
                        // Is this option a global permission setting?
169
                        if (isset($this->acl_options['global'][$opt]))
170
                        {
171
                                if (isset($this->acl[0]))
172
                                {
173
                                        $this->cache[$f][$opt] = $this->acl[0][$this->acl_options['global'][$opt]];
174
                                }
175
                        }
176
177
                        // Is this option a local permission setting?
178
                        // But if we check for a global option only, we won't combine the options...
179
                        if ($f != 0 && isset($this->acl_options['local'][$opt]))
180
                        {
181
                                if (isset($this->acl[$f]) && isset($this->acl[$f][$this->acl_options['local'][$opt]]))
182
                                {
183
                                        $this->cache[$f][$opt] |= $this->acl[$f][$this->acl_options['local'][$opt]];
184
                                }
185
                        }
186
                }
187
188
                // Founder always has all global options set to true...
189
                return ($negate) ? !$this->cache[$f][$opt] : $this->cache[$f][$opt];
190
        }
191
192
        /**
193
        * Get forums with the specified permission setting
194
        * if the option is prefixed with !, then the result becomes nagated
195
        *
196
        * @param bool $clean set to true if only values needs to be returned which are set/unset
197
        */
198
        function acl_getf($opt, $clean = false)
199
        {
200
                $acl_f = array();
201
                $negate = false;
202
203
                if (strpos($opt, '!') === 0)
204
                {
205
                        $negate = true;
206
                        $opt = substr($opt, 1);
207
                }
208
209
                // If we retrieve a list of forums not having permissions in, we need to get every forum_id
210
                if ($negate)
211
                {
212
                        if ($this->acl_forum_ids === false)
213
                        {
214
                                global $db;
215
216
                                $sql = 'SELECT forum_id
217
                                        FROM ' . FORUMS_TABLE;
218
219
                                if (sizeof($this->acl))
220
                                {
221
                                        $sql .= ' WHERE ' . $db->sql_in_set('forum_id', array_keys($this->acl), true);
222
                                }
223
                                $result = $db->sql_query($sql);
224
225
                                $this->acl_forum_ids = array();
226
                                while ($row = $db->sql_fetchrow($result))
227
                                {
228
                                        $this->acl_forum_ids[] = $row['forum_id'];
229
                                }
230
                                $db->sql_freeresult($result);
231
                        }
232
                }
233
234
                if (isset($this->acl_options['local'][$opt]))
235
                {
236
                        foreach ($this->acl as $f => $bitstring)
237
                        {
238
                                // Skip global settings
239
                                if (!$f)
240
                                {
241
                                        continue;
242
                                }
243
244
                                $allowed = (!isset($this->cache[$f][$opt])) ? $this->acl_get($opt, $f) : $this->cache[$f][$opt];
245
246
                                if (!$clean)
247
                                {
248
                                        $acl_f[$f][$opt] = ($negate) ? !$allowed : $allowed;
249
                                }
250
                                else
251
                                {
252
                                        if (($negate && !$allowed) || (!$negate && $allowed))
253
                                        {
254
                                                $acl_f[$f][$opt] = 1;
255
                                        }
256
                                }
257
                        }
258
                }
259
260
                // If we get forum_ids not having this permission, we need to fill the remaining parts
261
                if ($negate && sizeof($this->acl_forum_ids))
262
                {
263
                        foreach ($this->acl_forum_ids as $f)
264
                        {
265
                                $acl_f[$f][$opt] = 1;
266
                        }
267
                }
268
269
                return $acl_f;
270
        }
271
272
        /**
273
        * Get local permission state for any forum.
274
        *
275
        * Returns true if user has the permission in one or more forums, false if in no forum.
276
        * If global option is checked it returns the global state (same as acl_get($opt))
277
        * Local option has precedence...
278
        */
279
        function acl_getf_global($opt)
280
        {
281
                if (is_array($opt))
282
                {
283
                        // evaluates to true as soon as acl_getf_global is true for one option
284
                        foreach ($opt as $check_option)
285
                        {
286
                                if ($this->acl_getf_global($check_option))
287
                                {
288
                                        return true;
289
                                }
290
                        }
291
292
                        return false;
293
                }
294
295
                if (isset($this->acl_options['local'][$opt]))
296
                {
297
                        foreach ($this->acl as $f => $bitstring)
298
                        {
299
                                // Skip global settings
300
                                if (!$f)
301
                                {
302
                                        continue;
303
                                }
304
305
                                // as soon as the user has any permission we're done so return true
306
                                if ((!isset($this->cache[$f][$opt])) ? $this->acl_get($opt, $f) : $this->cache[$f][$opt])
307
                                {
308
                                        return true;
309
                                }
310
                        }
311
                }
312
                else if (isset($this->acl_options['global'][$opt]))
313
                {
314
                        return $this->acl_get($opt);
315
                }
316
317
                return false;
318
        }
319
320
        /**
321
        * Get permission settings (more than one)
322
        */
323
        function acl_gets()
324
        {
325
                $args = func_get_args();
326
                $f = array_pop($args);
327
328
                if (!is_numeric($f))
329
                {
330
                        $args[] = $f;
331
                        $f = 0;
332
                }
333
334
                // alternate syntax: acl_gets(array('m_', 'a_'), $forum_id)
335
                if (is_array($args[0]))
336
                {
337
                        $args = $args[0];
338
                }
339
340
                $acl = 0;
341
                foreach ($args as $opt)
342
                {
343
                        $acl |= $this->acl_get($opt, $f);
344
                }
345
346
                return $acl;
347
        }
348
349
        /**
350
        * Get permission listing based on user_id/options/forum_ids
351
        *
352
        * Be careful when using this function with permissions a_, m_, u_ and f_ !
353
        * It may not work correctly. When a user group grants an a_* permission,
354
        * e.g. a_foo, but the user's a_foo permission is set to "Never", then
355
        * the user does not in fact have the a_ permission.
356
        * But the user will still be listed as having the a_ permission.
357
        *
358
        * For more information see: http://tracker.phpbb.com/browse/PHPBB3-10252
359
        */
360
        function acl_get_list($user_id = false, $opts = false, $forum_id = false)
361
        {
362
                if ($user_id !== false && !is_array($user_id) && $opts === false && $forum_id === false)
363
                {
364
                        $hold_ary = array($user_id => $this->acl_raw_data_single_user($user_id));
365
                }
366
                else
367
                {
368
                        $hold_ary = $this->acl_raw_data($user_id, $opts, $forum_id);
369
                }
370
371
                $auth_ary = array();
372
                foreach ($hold_ary as $user_id => $forum_ary)
373
                {
374
                        foreach ($forum_ary as $forum_id => $auth_option_ary)
375
                        {
376
                                foreach ($auth_option_ary as $auth_option => $auth_setting)
377
                                {
378
                                        if ($auth_setting)
379
                                        {
380
                                                $auth_ary[$forum_id][$auth_option][] = $user_id;
381
                                        }
382
                                }
383
                        }
384
                }
385
386
                return $auth_ary;
387
        }
388
389
        /**
390
        * Cache data to user_permissions row
391
        */
392
        function acl_cache(&$userdata)
393
        {
394
                global $db;
395
396
                // Empty user_permissions
397
                $userdata['user_permissions'] = '';
398
399
                $hold_ary = $this->acl_raw_data_single_user($userdata['user_id']);
400
401
                // Key 0 in $hold_ary are global options, all others are forum_ids
402
403
                // If this user is founder we're going to force fill the admin options ...
404
                if ($userdata['user_type'] == USER_FOUNDER)
405
                {
406
                        foreach ($this->acl_options['global'] as $opt => $id)
407
                        {
408
                                if (strpos($opt, 'a_') === 0)
409
                                {
410
                                        $hold_ary[0][$this->acl_options['id'][$opt]] = ACL_YES;
411
                                }
412
                        }
413
                }
414
415
                $hold_str = $this->build_bitstring($hold_ary);
416
417
                if ($hold_str)
418
                {
419
                        $userdata['user_permissions'] = $hold_str;
420
421
                        $sql = 'UPDATE ' . USERS_TABLE . "
422
                                SET user_permissions = '" . $db->sql_escape($userdata['user_permissions']) . "',
423
                                        user_perm_from = 0
424
                                WHERE user_id = " . $userdata['user_id'];
425
                        $db->sql_query($sql);
426
                }
427
428
                return;
429
        }
430
431
        /**
432
        * Build bitstring from permission set
433
        */
434
        function build_bitstring(&$hold_ary)
435
        {
436
                $hold_str = '';
437
438
                if (sizeof($hold_ary))
439
                {
440
                        ksort($hold_ary);
441
442
                        $last_f = 0;
443
444
                        foreach ($hold_ary as $f => $auth_ary)
445
                        {
446
                                $ary_key = (!$f) ? 'global' : 'local';
447
448
                                $bitstring = array();
449
                                foreach ($this->acl_options[$ary_key] as $opt => $id)
450
                                {
451
                                        if (isset($auth_ary[$this->acl_options['id'][$opt]]))
452
                                        {
453
                                                $bitstring[$id] = $auth_ary[$this->acl_options['id'][$opt]];
454
455
                                                $option_key = substr($opt, 0, strpos($opt, '_') + 1);
456
457
                                                // If one option is allowed, the global permission for this option has to be allowed too
458
                                                // example: if the user has the a_ permission this means he has one or more a_* permissions
459
                                                if ($auth_ary[$this->acl_options['id'][$opt]] == ACL_YES && (!isset($bitstring[$this->acl_options[$ary_key][$option_key]]) || $bitstring[$this->acl_options[$ary_key][$option_key]] == ACL_NEVER))
460
                                                {
461
                                                        $bitstring[$this->acl_options[$ary_key][$option_key]] = ACL_YES;
462
                                                }
463
                                        }
464
                                        else
465
                                        {
466
                                                $bitstring[$id] = ACL_NEVER;
467
                                        }
468
                                }
469
470
                                // Now this bitstring defines the permission setting for the current forum $f (or global setting)
471
                                $bitstring = implode('', $bitstring);
472
473
                                // The line number indicates the id, therefore we have to add empty lines for those ids not present
474
                                $hold_str .= str_repeat("\n", $f - $last_f);
475
476
                                // Convert bitstring for storage - we do not use binary/bytes because PHP's string functions are not fully binary safe
477
                                for ($i = 0, $bit_length = strlen($bitstring); $i < $bit_length; $i += 31)
478
                                {
479
                                        $hold_str .= str_pad(base_convert(str_pad(substr($bitstring, $i, 31), 31, 0, STR_PAD_RIGHT), 2, 36), 6, 0, STR_PAD_LEFT);
480
                                }
481
482
                                $last_f = $f;
483
                        }
484
                        unset($bitstring);
485
486
                        $hold_str = rtrim($hold_str);
487
                }
488
489
                return $hold_str;
490
        }
491
492
        /**
493
        * Clear one or all users cached permission settings
494
        */
495
        function acl_clear_prefetch($user_id = false)
496
        {
497
                global $db, $cache;
498
499
                // Rebuild options cache
500
                $cache->destroy('_role_cache');
501
502
                $sql = 'SELECT *
503
                        FROM ' . ACL_ROLES_DATA_TABLE . '
504
                        ORDER BY role_id ASC';
505
                $result = $db->sql_query($sql);
506
507
                $this->role_cache = array();
508
                while ($row = $db->sql_fetchrow($result))
509
                {
510
                        $this->role_cache[$row['role_id']][$row['auth_option_id']] = (int) $row['auth_setting'];
511
                }
512
                $db->sql_freeresult($result);
513
514
                foreach ($this->role_cache as $role_id => $role_options)
515
                {
516
                        $this->role_cache[$role_id] = serialize($role_options);
517
                }
518
519
                $cache->put('_role_cache', $this->role_cache);
520
521
                // Now empty user permissions
522
                $where_sql = '';
523
524
                if ($user_id !== false)
525
                {
526
                        $user_id = (!is_array($user_id)) ? $user_id = array((int) $user_id) : array_map('intval', $user_id);
527
                        $where_sql = ' WHERE ' . $db->sql_in_set('user_id', $user_id);
528
                }
529
530
                $sql = 'UPDATE ' . USERS_TABLE . "
531
                        SET user_permissions = '',
532
                                user_perm_from = 0
533
                        $where_sql";
534
                $db->sql_query($sql);
535
536
                return;
537
        }
538
539
        /**
540
        * Get assigned roles
541
        */
542
        function acl_role_data($user_type, $role_type, $ug_id = false, $forum_id = false)
543
        {
544
                global $db;
545
546
                $roles = array();
547
548
                $sql_id = ($user_type == 'user') ? 'user_id' : 'group_id';
549
550
                $sql_ug = ($ug_id !== false) ? ((!is_array($ug_id)) ? "AND a.$sql_id = $ug_id" : 'AND ' . $db->sql_in_set("a.$sql_id", $ug_id)) : '';
551
                $sql_forum = ($forum_id !== false) ? ((!is_array($forum_id)) ? "AND a.forum_id = $forum_id" : 'AND ' . $db->sql_in_set('a.forum_id', $forum_id)) : '';
552
553
                // Grab assigned roles...
554
                $sql = 'SELECT a.auth_role_id, a.' . $sql_id . ', a.forum_id
555
                        FROM ' . (($user_type == 'user') ? ACL_USERS_TABLE : ACL_GROUPS_TABLE) . ' a, ' . ACL_ROLES_TABLE . " r
556
                        WHERE a.auth_role_id = r.role_id
557
                                AND r.role_type = '" . $db->sql_escape($role_type) . "'
558
                                $sql_ug
559
                                $sql_forum
560
                        ORDER BY r.role_order ASC";
561
                $result = $db->sql_query($sql);
562
563
                while ($row = $db->sql_fetchrow($result))
564
                {
565
                        $roles[$row[$sql_id]][$row['forum_id']] = $row['auth_role_id'];
566
                }
567
                $db->sql_freeresult($result);
568
569
                return $roles;
570
        }
571
572
        /**
573
        * Get raw acl data based on user/option/forum
574
        */
575
        function acl_raw_data($user_id = false, $opts = false, $forum_id = false)
576
        {
577
                global $db;
578
579
                $sql_user = ($user_id !== false) ? ((!is_array($user_id)) ? 'user_id = ' . (int) $user_id : $db->sql_in_set('user_id', array_map('intval', $user_id))) : '';
580
                $sql_forum = ($forum_id !== false) ? ((!is_array($forum_id)) ? 'AND a.forum_id = ' . (int) $forum_id : 'AND ' . $db->sql_in_set('a.forum_id', array_map('intval', $forum_id))) : '';
581
582
                $sql_opts = $sql_opts_select = $sql_opts_from = '';
583
                $hold_ary = array();
584
585
                if ($opts !== false)
586
                {
587
                        $sql_opts_select = ', ao.auth_option';
588
                        $sql_opts_from = ', ' . ACL_OPTIONS_TABLE . ' ao';
589
                        $this->build_auth_option_statement('ao.auth_option', $opts, $sql_opts);
590
                }
591
592
                $sql_ary = array();
593
594
                // Grab non-role settings - user-specific
595
                $sql_ary[] = 'SELECT a.user_id, a.forum_id, a.auth_setting, a.auth_option_id' . $sql_opts_select . '
596
                        FROM ' . ACL_USERS_TABLE . ' a' . $sql_opts_from . '
597
                        WHERE a.auth_role_id = 0 ' .
598
                                (($sql_opts_from) ? 'AND a.auth_option_id = ao.auth_option_id ' : '') .
599
                                (($sql_user) ? 'AND a.' . $sql_user : '') . "
600
                                $sql_forum
601
                                $sql_opts";
602
603
                // Now the role settings - user-specific
604
                $sql_ary[] = 'SELECT a.user_id, a.forum_id, r.auth_option_id, r.auth_setting, r.auth_option_id' . $sql_opts_select . '
605
                        FROM ' . ACL_USERS_TABLE . ' a, ' . ACL_ROLES_DATA_TABLE . ' r' . $sql_opts_from . '
606
                        WHERE a.auth_role_id = r.role_id ' .
607
                                (($sql_opts_from) ? 'AND r.auth_option_id = ao.auth_option_id ' : '') .
608
                                (($sql_user) ? 'AND a.' . $sql_user : '') . "
609
                                $sql_forum
610
                                $sql_opts";
611
612
                foreach ($sql_ary as $sql)
613
                {
614
                        $result = $db->sql_query($sql);
615
616
                        while ($row = $db->sql_fetchrow($result))
617
                        {
618
                                $option = ($sql_opts_select) ? $row['auth_option'] : $this->acl_options['option'][$row['auth_option_id']];
619
                                $hold_ary[$row['user_id']][$row['forum_id']][$option] = $row['auth_setting'];
620
                        }
621
                        $db->sql_freeresult($result);
622
                }
623
624
                $sql_ary = array();
625
626
                // Now grab group settings - non-role specific...
627
                $sql_ary[] = 'SELECT ug.user_id, a.forum_id, a.auth_setting, a.auth_option_id' . $sql_opts_select . '
628
                        FROM ' . ACL_GROUPS_TABLE . ' a, ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g' . $sql_opts_from . '
629
                        WHERE a.auth_role_id = 0 ' .
630
                                (($sql_opts_from) ? 'AND a.auth_option_id = ao.auth_option_id ' : '') . '
631
                                AND a.group_id = ug.group_id
632
                                AND g.group_id = ug.group_id
633
                                AND ug.user_pending = 0
634
                                AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1)
635
                                ' . (($sql_user) ? 'AND ug.' . $sql_user : '') . "
636
                                $sql_forum
637
                                $sql_opts";
638
639
                // Now grab group settings - role specific...
640
                $sql_ary[] = 'SELECT ug.user_id, a.forum_id, r.auth_setting, r.auth_option_id' . $sql_opts_select . '
641
                        FROM ' . ACL_GROUPS_TABLE . ' a, ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g, ' . ACL_ROLES_DATA_TABLE . ' r' . $sql_opts_from . '
642
                        WHERE a.auth_role_id = r.role_id ' .
643
                                (($sql_opts_from) ? 'AND r.auth_option_id = ao.auth_option_id ' : '') . '
644
                                AND a.group_id = ug.group_id
645
                                AND g.group_id = ug.group_id
646
                                AND ug.user_pending = 0
647
                                AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1)
648
                                ' . (($sql_user) ? 'AND ug.' . $sql_user : '') . "
649
                                $sql_forum
650
                                $sql_opts";
651
652
                foreach ($sql_ary as $sql)
653
                {
654
                        $result = $db->sql_query($sql);
655
656
                        while ($row = $db->sql_fetchrow($result))
657
                        {
658
                                $option = ($sql_opts_select) ? $row['auth_option'] : $this->acl_options['option'][$row['auth_option_id']];
659
660
                                if (!isset($hold_ary[$row['user_id']][$row['forum_id']][$option]) || (isset($hold_ary[$row['user_id']][$row['forum_id']][$option]) && $hold_ary[$row['user_id']][$row['forum_id']][$option] != ACL_NEVER))
661
                                {
662
                                        $hold_ary[$row['user_id']][$row['forum_id']][$option] = $row['auth_setting'];
663
664
                                        // If we detect ACL_NEVER, we will unset the flag option (within building the bitstring it is correctly set again)
665
                                        if ($row['auth_setting'] == ACL_NEVER)
666
                                        {
667
                                                $flag = substr($option, 0, strpos($option, '_') + 1);
668
669
                                                if (isset($hold_ary[$row['user_id']][$row['forum_id']][$flag]) && $hold_ary[$row['user_id']][$row['forum_id']][$flag] == ACL_YES)
670
                                                {
671
                                                        unset($hold_ary[$row['user_id']][$row['forum_id']][$flag]);
672
673
/*                                                        if (in_array(ACL_YES, $hold_ary[$row['user_id']][$row['forum_id']]))
674
                                                        {
675
                                                                $hold_ary[$row['user_id']][$row['forum_id']][$flag] = ACL_YES;
676
                                                        }
677
*/
678
                                                }
679
                                        }
680
                                }
681
                        }
682
                        $db->sql_freeresult($result);
683
                }
684
685
                return $hold_ary;
686
        }
687
688
        /**
689
        * Get raw user based permission settings
690
        */
691
        function acl_user_raw_data($user_id = false, $opts = false, $forum_id = false)
692
        {
693
                global $db;
694
695
                $sql_user = ($user_id !== false) ? ((!is_array($user_id)) ? 'user_id = ' . (int) $user_id : $db->sql_in_set('user_id', array_map('intval', $user_id))) : '';
696
                $sql_forum = ($forum_id !== false) ? ((!is_array($forum_id)) ? 'AND a.forum_id = ' . (int) $forum_id : 'AND ' . $db->sql_in_set('a.forum_id', array_map('intval', $forum_id))) : '';
697
698
                $sql_opts = '';
699
                $hold_ary = $sql_ary = array();
700
701
                if ($opts !== false)
702
                {
703
                        $this->build_auth_option_statement('ao.auth_option', $opts, $sql_opts);
704
                }
705
706
                // Grab user settings - non-role specific...
707
                $sql_ary[] = 'SELECT a.user_id, a.forum_id, a.auth_setting, a.auth_option_id, ao.auth_option
708
                        FROM ' . ACL_USERS_TABLE . ' a, ' . ACL_OPTIONS_TABLE . ' ao
709
                        WHERE a.auth_role_id = 0
710
                                AND a.auth_option_id = ao.auth_option_id ' .
711
                                (($sql_user) ? 'AND a.' . $sql_user : '') . "
712
                                $sql_forum
713
                                $sql_opts
714
                        ORDER BY a.forum_id, ao.auth_option";
715
716
                // Now the role settings - user-specific
717
                $sql_ary[] = 'SELECT a.user_id, a.forum_id, r.auth_option_id, r.auth_setting, r.auth_option_id, ao.auth_option
718
                        FROM ' . ACL_USERS_TABLE . ' a, ' . ACL_ROLES_DATA_TABLE . ' r, ' . ACL_OPTIONS_TABLE . ' ao
719
                        WHERE a.auth_role_id = r.role_id
720
                                AND r.auth_option_id = ao.auth_option_id ' .
721
                                (($sql_user) ? 'AND a.' . $sql_user : '') . "
722
                                $sql_forum
723
                                $sql_opts
724
                        ORDER BY a.forum_id, ao.auth_option";
725
726
                foreach ($sql_ary as $sql)
727
                {
728
                        $result = $db->sql_query($sql);
729
730
                        while ($row = $db->sql_fetchrow($result))
731
                        {
732
                                $hold_ary[$row['user_id']][$row['forum_id']][$row['auth_option']] = $row['auth_setting'];
733
                        }
734
                        $db->sql_freeresult($result);
735
                }
736
737
                return $hold_ary;
738
        }
739
740
        /**
741
        * Get raw group based permission settings
742
        */
743
        function acl_group_raw_data($group_id = false, $opts = false, $forum_id = false)
744
        {
745
                global $db;
746
747
                $sql_group = ($group_id !== false) ? ((!is_array($group_id)) ? 'group_id = ' . (int) $group_id : $db->sql_in_set('group_id', array_map('intval', $group_id))) : '';
748
                $sql_forum = ($forum_id !== false) ? ((!is_array($forum_id)) ? 'AND a.forum_id = ' . (int) $forum_id : 'AND ' . $db->sql_in_set('a.forum_id', array_map('intval', $forum_id))) : '';
749
750
                $sql_opts = '';
751
                $hold_ary = $sql_ary = array();
752
753
                if ($opts !== false)
754
                {
755
                        $this->build_auth_option_statement('ao.auth_option', $opts, $sql_opts);
756
                }
757
758
                // Grab group settings - non-role specific...
759
                $sql_ary[] = 'SELECT a.group_id, a.forum_id, a.auth_setting, a.auth_option_id, ao.auth_option
760
                        FROM ' . ACL_GROUPS_TABLE . ' a, ' . ACL_OPTIONS_TABLE . ' ao
761
                        WHERE a.auth_role_id = 0
762
                                AND a.auth_option_id = ao.auth_option_id ' .
763
                                (($sql_group) ? 'AND a.' . $sql_group : '') . "
764
                                $sql_forum
765
                                $sql_opts
766
                        ORDER BY a.forum_id, ao.auth_option";
767
768
                // Now grab group settings - role specific...
769
                $sql_ary[] = 'SELECT a.group_id, a.forum_id, r.auth_setting, r.auth_option_id, ao.auth_option
770
                        FROM ' . ACL_GROUPS_TABLE . ' a, ' . ACL_ROLES_DATA_TABLE . ' r, ' . ACL_OPTIONS_TABLE . ' ao
771
                        WHERE a.auth_role_id = r.role_id
772
                                AND r.auth_option_id = ao.auth_option_id ' .
773
                                (($sql_group) ? 'AND a.' . $sql_group : '') . "
774
                                $sql_forum
775
                                $sql_opts
776
                        ORDER BY a.forum_id, ao.auth_option";
777
778
                foreach ($sql_ary as $sql)
779
                {
780
                        $result = $db->sql_query($sql);
781
782
                        while ($row = $db->sql_fetchrow($result))
783
                        {
784
                                $hold_ary[$row['group_id']][$row['forum_id']][$row['auth_option']] = $row['auth_setting'];
785
                        }
786
                        $db->sql_freeresult($result);
787
                }
788
789
                return $hold_ary;
790
        }
791
792
        /**
793
        * Get raw acl data based on user for caching user_permissions
794
        * This function returns the same data as acl_raw_data(), but without the user id as the first key within the array.
795
        */
796
        function acl_raw_data_single_user($user_id)
797
        {
798
                global $db, $cache;
799
800
                // Check if the role-cache is there
801
                if (($this->role_cache = $cache->get('_role_cache')) === false)
802
                {
803
                        $this->role_cache = array();
804
805
                        // We pre-fetch roles
806
                        $sql = 'SELECT *
807
                                FROM ' . ACL_ROLES_DATA_TABLE . '
808
                                ORDER BY role_id ASC';
809
                        $result = $db->sql_query($sql);
810
811
                        while ($row = $db->sql_fetchrow($result))
812
                        {
813
                                $this->role_cache[$row['role_id']][$row['auth_option_id']] = (int) $row['auth_setting'];
814
                        }
815
                        $db->sql_freeresult($result);
816
817
                        foreach ($this->role_cache as $role_id => $role_options)
818
                        {
819
                                $this->role_cache[$role_id] = serialize($role_options);
820
                        }
821
822
                        $cache->put('_role_cache', $this->role_cache);
823
                }
824
825
                $hold_ary = array();
826
827
                // Grab user-specific permission settings
828
                $sql = 'SELECT forum_id, auth_option_id, auth_role_id, auth_setting
829
                        FROM ' . ACL_USERS_TABLE . '
830
                        WHERE user_id = ' . $user_id;
831
                $result = $db->sql_query($sql);
832
833
                while ($row = $db->sql_fetchrow($result))
834
                {
835
                        // If a role is assigned, assign all options included within this role. Else, only set this one option.
836
                        if ($row['auth_role_id'])
837
                        {
838
                                $hold_ary[$row['forum_id']] = (empty($hold_ary[$row['forum_id']])) ? unserialize($this->role_cache[$row['auth_role_id']]) : $hold_ary[$row['forum_id']] + unserialize($this->role_cache[$row['auth_role_id']]);
839
                        }
840
                        else
841
                        {
842
                                $hold_ary[$row['forum_id']][$row['auth_option_id']] = $row['auth_setting'];
843
                        }
844
                }
845
                $db->sql_freeresult($result);
846
847
                // Now grab group-specific permission settings
848
                $sql = 'SELECT a.forum_id, a.auth_option_id, a.auth_role_id, a.auth_setting
849
                        FROM ' . ACL_GROUPS_TABLE . ' a, ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
850
                        WHERE a.group_id = ug.group_id
851
                                AND g.group_id = ug.group_id
852
                                AND ug.user_pending = 0
853
                                AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1)
854
                                AND ug.user_id = ' . $user_id;
855
                $result = $db->sql_query($sql);
856
857
                while ($row = $db->sql_fetchrow($result))
858
                {
859
                        if (!$row['auth_role_id'])
860
                        {
861
                                $this->_set_group_hold_ary($hold_ary[$row['forum_id']], $row['auth_option_id'], $row['auth_setting']);
862
                        }
863
                        else if (!empty($this->role_cache[$row['auth_role_id']]))
864
                        {
865
                                foreach (unserialize($this->role_cache[$row['auth_role_id']]) as $option_id => $setting)
866
                                {
867
                                        $this->_set_group_hold_ary($hold_ary[$row['forum_id']], $option_id, $setting);
868
                                }
869
                        }
870
                }
871
                $db->sql_freeresult($result);
872
873
                return $hold_ary;
874
        }
875
876
        /**
877
        * Private function snippet for setting a specific piece of the hold_ary
878
        */
879
        function _set_group_hold_ary(&$hold_ary, $option_id, $setting)
880
        {
881
                if (!isset($hold_ary[$option_id]) || (isset($hold_ary[$option_id]) && $hold_ary[$option_id] != ACL_NEVER))
882
                {
883
                        $hold_ary[$option_id] = $setting;
884
885
                        // If we detect ACL_NEVER, we will unset the flag option (within building the bitstring it is correctly set again)
886
                        if ($setting == ACL_NEVER)
887
                        {
888
                                $flag = substr($this->acl_options['option'][$option_id], 0, strpos($this->acl_options['option'][$option_id], '_') + 1);
889
                                $flag = (int) $this->acl_options['id'][$flag];
890
891
                                if (isset($hold_ary[$flag]) && $hold_ary[$flag] == ACL_YES)
892
                                {
893
                                        unset($hold_ary[$flag]);
894
895
/*                                        This is uncommented, because i suspect this being slightly wrong due to mixed permission classes being possible
896
                                        if (in_array(ACL_YES, $hold_ary))
897
                                        {
898
                                                $hold_ary[$flag] = ACL_YES;
899
                                        }*/
900
                                }
901
                        }
902
                }
903
        }
904
905
        /**
906
        * Authentication plug-ins is largely down to Sergey Kanareykin, our thanks to him.
907
        */
908
        function login($username, $password, $autologin = false, $viewonline = 1, $admin = 0)
909
        {
910
                global $config, $db, $user, $phpbb_root_path, $phpEx;
911
912
                $method = trim(basename($config['auth_method']));
913
                include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
914
915
                $method = 'login_' . $method;
916
                if (function_exists($method))
917
                {
918
                        $login = $method($username, $password, $user->ip, $user->browser, $user->forwarded_for);
919
920
                        // If the auth module wants us to create an empty profile do so and then treat the status as LOGIN_SUCCESS
921
                        if ($login['status'] == LOGIN_SUCCESS_CREATE_PROFILE)
922
                        {
923
                                // we are going to use the user_add function so include functions_user.php if it wasn't defined yet
924
                                if (!function_exists('user_add'))
925
                                {
926
                                        include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
927
                                }
928
929
                                user_add($login['user_row'], (isset($login['cp_data'])) ? $login['cp_data'] : false);
930
931
                                $sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type
932
                                        FROM ' . USERS_TABLE . "
933
                                        WHERE username_clean = '" . $db->sql_escape(utf8_clean_string($username)) . "'";
934
                                $result = $db->sql_query($sql);
935
                                $row = $db->sql_fetchrow($result);
936
                                $db->sql_freeresult($result);
937
938
                                if (!$row)
939
                                {
940
                                        return array(
941
                                                'status'                => LOGIN_ERROR_EXTERNAL_AUTH,
942
                                                'error_msg'                => 'AUTH_NO_PROFILE_CREATED',
943
                                                'user_row'                => array('user_id' => ANONYMOUS),
944
                                        );
945
                                }
946
947
                                $login = array(
948
                                        'status'        => LOGIN_SUCCESS,
949
                                        'error_msg'        => false,
950
                                        'user_row'        => $row,
951
                                );
952
                        }
953
954
                        // If login succeeded, we will log the user in... else we pass the login array through...
955
                        if ($login['status'] == LOGIN_SUCCESS)
956
                        {
957
                                $old_session_id = $user->session_id;
958
959
                                if ($admin)
960
                                {
961
                                        global $SID, $_SID;
962
963
                                        $cookie_expire = time() - 31536000;
964
                                        $user->set_cookie('u', '', $cookie_expire);
965
                                        $user->set_cookie('sid', '', $cookie_expire);
966
                                        unset($cookie_expire);
967
968
                                        $SID = '?sid=';
969
                                        $user->session_id = $_SID = '';
970
                                }
971
972
                                $result = $user->session_create($login['user_row']['user_id'], $admin, $autologin, $viewonline);
973
974
                                // Successful session creation
975
                                if ($result === true)
976
                                {
977
                                        // If admin re-authentication we remove the old session entry because a new one has been created...
978
                                        if ($admin)
979
                                        {
980
                                                // the login array is used because the user ids do not differ for re-authentication
981
                                                $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
982
                                                        WHERE session_id = '" . $db->sql_escape($old_session_id) . "'
983
                                                        AND session_user_id = {$login['user_row']['user_id']}";
984
                                                $db->sql_query($sql);
985
                                        }
986
987
                                        return array(
988
                                                'status'                => LOGIN_SUCCESS,
989
                                                'error_msg'                => false,
990
                                                'user_row'                => $login['user_row'],
991
                                        );
992
                                }
993
994
                                return array(
995
                                        'status'                => LOGIN_BREAK,
996
                                        'error_msg'                => $result,
997
                                        'user_row'                => $login['user_row'],
998
                                );
999
                        }
1000
1001
                        return $login;
1002
                }
1003
1004
                trigger_error('Authentication method not found', E_USER_ERROR);
1005
        }
1006
1007
        /**
1008
        * Fill auth_option statement for later querying based on the supplied options
1009
        */
1010
        function build_auth_option_statement($key, $auth_options, &$sql_opts)
1011
        {
1012
                global $db;
1013
1014
                if (!is_array($auth_options))
1015
                {
1016
                        if (strpos($auth_options, '%') !== false)
1017
                        {
1018
                                $sql_opts = "AND $key " . $db->sql_like_expression(str_replace('%', $db->any_char, $auth_options));
1019
                        }
1020
                        else
1021
                        {
1022
                                $sql_opts = "AND $key = '" . $db->sql_escape($auth_options) . "'";
1023
                        }
1024
                }
1025
                else
1026
                {
1027
                        $is_like_expression = false;
1028
1029
                        foreach ($auth_options as $option)
1030
                        {
1031
                                if (strpos($option, '%') !== false)
1032
                                {
1033
                                        $is_like_expression = true;
1034
                                }
1035
                        }
1036
1037
                        if (!$is_like_expression)
1038
                        {
1039
                                $sql_opts = 'AND ' . $db->sql_in_set($key, $auth_options);
1040
                        }
1041
                        else
1042
                        {
1043
                                $sql = array();
1044
1045
                                foreach ($auth_options as $option)
1046
                                {
1047
                                        if (strpos($option, '%') !== false)
1048
                                        {
1049
                                                $sql[] = $key . ' ' . $db->sql_like_expression(str_replace('%', $db->any_char, $option));
1050
                                        }
1051
                                        else
1052
                                        {
1053
                                                $sql[] = $key . " = '" . $db->sql_escape($option) . "'";
1054
                                        }
1055
                                }
1056
1057
                                $sql_opts = 'AND (' . implode(' OR ', $sql) . ')';
1058
                        }
1059
                }
1060
        }
1061
}