1<?php
2/**
3 * DokuWiki Plugin elasticsearch (Helper Component)
4 *
5 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6 * @author  Andreas Gohr <gohr@cosmocode.de>
7 * @author  Anna Dabrowska <dabrowska@cosmocode.de>
8 */
9
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12
13/**
14 * User-independent ACL methods
15 */
16class helper_plugin_elasticsearch_acl extends DokuWiki_Plugin
17{
18    /**
19     * Returns a full list of (read) permissions for users and groups whose access to a given page
20     * is defined in the ACLs.
21     * Traverses the whole rule set and resolves overrides and exclusions.
22     *
23     * @param string $id Page id
24     * @return array
25     */
26    public function getPageACL($id) {
27        $id    = cleanID($id);
28        $rules = [];
29
30        /** @var admin_plugin_acl $hlpACL */
31        $hlpACL = plugin_load('admin', 'acl');
32        if(method_exists($hlpACL, 'initAclConfig')) {
33            $hlpACL->initAclConfig();
34        } else {
35            /** @deprecated Call for current stable release */
36            $hlpACL->_init_acl_config();
37        }
38
39        // ACL lines as array
40        $acl = $hlpACL->acl;
41        ksort($acl);
42
43        // check for exact id
44        if (isset($acl[$id])) {
45            // process matched rule
46            $this->addRule($acl[$id], $rules);
47            // stop traversing if we reached a total access block for @ALL
48            if (isset($acl[$id]['@ALL'])) return $rules;
49        }
50
51        // walk namespace segments up
52        $ns = $id;
53        do {
54            $ns = getNS($ns);
55            // no namespace, check permissions for root
56            if (!$ns && isset($acl['*'])) {
57                $this->addRule($acl['*'], $rules);
58                // stop traversing if we reached a total access block for @ALL
59                if (isset($acl['*']['@ALL'])) {
60                    $ns = false;
61                    continue;
62                }
63            }
64            // check namespace
65            if (isset($acl[$ns . ':*'])) {
66                $this->addRule($acl[$ns . ':*'], $rules);
67                // stop traversing if we reached a total access block for @ALL
68                if (isset($acl[$ns . ':*']['@ALL'])) $ns = false;
69            }
70        } while ($ns);
71
72        return $rules;
73    }
74
75    /**
76     * Splits a rule set into query-digestible chunks
77     *
78     * @param array $rules
79     * @return array
80     */
81    public function splitRules($rules)
82    {
83        $splitACL = [
84            'groups_include' => [],
85            'groups_exclude' => [],
86            'users_include' => [],
87            'users_exclude' => [],
88        ];
89
90        foreach ($rules as $key => $perm) {
91            if (strpos($key, '@') === 0) {
92                $type = $perm ? 'groups_include' : 'groups_exclude';
93            } else {
94                $type = $perm ? 'users_include' : 'users_exclude';
95            }
96            $splitACL[$type][] = ltrim($key, '@');
97        }
98
99        return $splitACL;
100    }
101
102    /**
103     * Adds specific access rules to a rule set covering a full namespace path.
104     * Omit access block for @ALL since it is assumed.
105     *
106     * @param array $rule Collection of access permissions for a certain location
107     * @param array $rules Set of rules already
108     */
109    protected function addRule($rule, &$rules)
110    {
111        $localrules = [];
112
113        foreach ($rule as $key => $perm) {
114            // set read permissions for a given group or user
115            // but skip if already defined for a more specific path
116            if ($key !== '@ALL' && !array_key_exists($key, $rules)) {
117                $localrules[$key] = $perm > AUTH_NONE;
118            } elseif ($key === '@ALL' && $perm > AUTH_NONE) {
119                $localrules[$key] = true;
120            }
121        }
122
123        $rules = array_merge($rules, $localrules);
124    }
125}
126