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