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