xref: /plugin/pureldap/classes/GroupHierarchyCache.php (revision 5dcabeda2fad4e4ee9d5e2783f1e5e830b0344f4)
1e7339d5aSAndreas Gohr<?php
2e7339d5aSAndreas Gohr
3e7339d5aSAndreas Gohrnamespace dokuwiki\plugin\pureldap\classes;
4e7339d5aSAndreas Gohr
5e7339d5aSAndreas Gohruse FreeDSx\Ldap\Entry\Entry;
6e7339d5aSAndreas Gohruse FreeDSx\Ldap\Exception\ProtocolException;
7e7339d5aSAndreas Gohruse FreeDSx\Ldap\LdapClient;
8e7339d5aSAndreas Gohruse FreeDSx\Ldap\Operations;
9e7339d5aSAndreas Gohruse FreeDSx\Ldap\Search\Filters;
10e7339d5aSAndreas Gohr
11e7339d5aSAndreas Gohr/**
12e7339d5aSAndreas Gohr * Keeps a copy of all AD groups and provides recursive operations
13e7339d5aSAndreas Gohr *
14e7339d5aSAndreas Gohr * All groups are cached as full DN here
15e7339d5aSAndreas Gohr */
16e7339d5aSAndreas Gohrclass GroupHierarchyCache
17e7339d5aSAndreas Gohr{
18e7339d5aSAndreas Gohr    /** @var LdapClient */
19e7339d5aSAndreas Gohr    protected $ldap;
20e7339d5aSAndreas Gohr
21e7339d5aSAndreas Gohr    /** @var array List of group DNs and their parent and children */
22e7339d5aSAndreas Gohr    protected $groupHierarchy;
23e7339d5aSAndreas Gohr
24e7339d5aSAndreas Gohr    /**
25e7339d5aSAndreas Gohr     * GroupHierarchyCache constructor.
26e7339d5aSAndreas Gohr     *
27e7339d5aSAndreas Gohr     * @param LdapClient $ldap
28*5dcabedaSAndreas Gohr     * @param bool $usefs Use filesystem caching?
29e7339d5aSAndreas Gohr     */
30*5dcabedaSAndreas Gohr    public function __construct(LdapClient $ldap, $usefs)
31e7339d5aSAndreas Gohr    {
32e7339d5aSAndreas Gohr        $this->ldap = $ldap;
33e7339d5aSAndreas Gohr
34*5dcabedaSAndreas Gohr        if($usefs) {
35e7339d5aSAndreas Gohr            $this->groupHierarchy = $this->getCachedGroupList();
36*5dcabedaSAndreas Gohr        } else {
37*5dcabedaSAndreas Gohr            $this->groupHierarchy = $this->getGroupList();
38*5dcabedaSAndreas Gohr        }
39e7339d5aSAndreas Gohr    }
40e7339d5aSAndreas Gohr
41e7339d5aSAndreas Gohr    /**
42e7339d5aSAndreas Gohr     * Use a file system cached version of the group hierarchy
43e7339d5aSAndreas Gohr     *
44e7339d5aSAndreas Gohr     * The cache expires after $conf['auth_security_timeout']
45e7339d5aSAndreas Gohr     *
46e7339d5aSAndreas Gohr     * @return array
47e7339d5aSAndreas Gohr     */
48e7339d5aSAndreas Gohr    protected function getCachedGroupList()
49e7339d5aSAndreas Gohr    {
50e7339d5aSAndreas Gohr        global $conf;
51e7339d5aSAndreas Gohr
52e7339d5aSAndreas Gohr        $cachename = getcachename('grouphierarchy', '.pureldap-gch');
53e7339d5aSAndreas Gohr        $cachetime = @filemtime($cachename);
54e7339d5aSAndreas Gohr
55e7339d5aSAndreas Gohr        // valid file system cache? use it
56e7339d5aSAndreas Gohr        if ($cachetime && (time() - $cachetime) < $conf['auth_security_timeout']) {
57e7339d5aSAndreas Gohr            return json_decode(file_get_contents($cachename), true);
58e7339d5aSAndreas Gohr        }
59e7339d5aSAndreas Gohr
60e7339d5aSAndreas Gohr        // get fresh data and store in cache
61e7339d5aSAndreas Gohr        $groups = $this->getGroupList();
62e7339d5aSAndreas Gohr        file_put_contents($cachename, json_encode($groups));
63e7339d5aSAndreas Gohr        return $groups;
64e7339d5aSAndreas Gohr    }
65e7339d5aSAndreas Gohr
66e7339d5aSAndreas Gohr    /**
67e7339d5aSAndreas Gohr     * Load all group information from AD
68e7339d5aSAndreas Gohr     *
69e7339d5aSAndreas Gohr     * @return array
70e7339d5aSAndreas Gohr     */
71e7339d5aSAndreas Gohr    protected function getGroupList()
72e7339d5aSAndreas Gohr    {
73e7339d5aSAndreas Gohr        $filter = Filters::equal('objectCategory', 'group');
74e7339d5aSAndreas Gohr        $search = Operations::search($filter, 'memberOf', 'cn');
75e7339d5aSAndreas Gohr        $paging = $this->ldap->paging($search);
76e7339d5aSAndreas Gohr
77e7339d5aSAndreas Gohr        $groups = [];
78e7339d5aSAndreas Gohr
79e7339d5aSAndreas Gohr        while ($paging->hasEntries()) {
80e7339d5aSAndreas Gohr            try {
81e7339d5aSAndreas Gohr                $entries = $paging->getEntries();
82e7339d5aSAndreas Gohr            } catch (ProtocolException $e) {
83e7339d5aSAndreas Gohr                return $groups; // return what we have
84e7339d5aSAndreas Gohr            }
85e7339d5aSAndreas Gohr            /** @var Entry $entry */
86e7339d5aSAndreas Gohr            foreach ($entries as $entry) {
87e7339d5aSAndreas Gohr                $dn = (string)$entry->getDn();
88e7339d5aSAndreas Gohr                $groups[$dn] = [];
89e7339d5aSAndreas Gohr                if ($entry->has('memberOf')) {
90e7339d5aSAndreas Gohr                    $parents = $entry->get('memberOf')->getValues();
91e7339d5aSAndreas Gohr                    $groups[$dn]['parents'] = $parents;
92e7339d5aSAndreas Gohr                    foreach ($parents as $parent) {
93e7339d5aSAndreas Gohr                        $groups[$parent]['children'][] = $dn;
94e7339d5aSAndreas Gohr                    }
95e7339d5aSAndreas Gohr                }
96e7339d5aSAndreas Gohr            }
97e7339d5aSAndreas Gohr        }
98e7339d5aSAndreas Gohr        return $groups;
99e7339d5aSAndreas Gohr    }
100e7339d5aSAndreas Gohr
101e7339d5aSAndreas Gohr    /**
102e7339d5aSAndreas Gohr     * Recursive method to get all children or parents
103e7339d5aSAndreas Gohr     *
104e7339d5aSAndreas Gohr     * @param string $group
105e7339d5aSAndreas Gohr     * @param string $type
106e7339d5aSAndreas Gohr     * @param array $data list to fill
107e7339d5aSAndreas Gohr     */
108e7339d5aSAndreas Gohr    protected function getHierarchy($group, $type, &$data)
109e7339d5aSAndreas Gohr    {
110e7339d5aSAndreas Gohr        if (empty($this->groupHierarchy[$group][$type])) return;
111e7339d5aSAndreas Gohr
112e7339d5aSAndreas Gohr        $parents = $this->groupHierarchy[$group][$type];
113e7339d5aSAndreas Gohr        foreach ($parents as $parent) {
114e7339d5aSAndreas Gohr            if (in_array($parent, $data)) continue; // we did this one already
115e7339d5aSAndreas Gohr            $data[] = $parent;
116e7339d5aSAndreas Gohr            $this->getHierarchy($parent, $type, $data);
117e7339d5aSAndreas Gohr        }
118e7339d5aSAndreas Gohr    }
119e7339d5aSAndreas Gohr
120e7339d5aSAndreas Gohr    /**
121e7339d5aSAndreas Gohr     * Get all parents of a group
122e7339d5aSAndreas Gohr     *
123e7339d5aSAndreas Gohr     * @param string $group
124e7339d5aSAndreas Gohr     * @return string[]
125e7339d5aSAndreas Gohr     */
126e7339d5aSAndreas Gohr    public function getParents($group)
127e7339d5aSAndreas Gohr    {
128e7339d5aSAndreas Gohr        $parents = [];
129e7339d5aSAndreas Gohr        $this->getHierarchy($group, 'parents', $parents);
130e7339d5aSAndreas Gohr        return $parents;
131e7339d5aSAndreas Gohr    }
132e7339d5aSAndreas Gohr
133e7339d5aSAndreas Gohr    /**
134e7339d5aSAndreas Gohr     * Get all children of a group
135e7339d5aSAndreas Gohr     *
136e7339d5aSAndreas Gohr     * @param string $group
137e7339d5aSAndreas Gohr     * @return string[]
138e7339d5aSAndreas Gohr     */
139e7339d5aSAndreas Gohr    public function getChildren($group)
140e7339d5aSAndreas Gohr    {
141e7339d5aSAndreas Gohr        $children = [];
142e7339d5aSAndreas Gohr        $this->getHierarchy($group, 'children', $children);
143e7339d5aSAndreas Gohr        return $children;
144e7339d5aSAndreas Gohr    }
145e7339d5aSAndreas Gohr}
146e7339d5aSAndreas Gohr
147