1<?php 2 3namespace dokuwiki\plugin\pureldap\classes; 4 5use FreeDSx\Ldap\Entry\Entries; 6use FreeDSx\Ldap\Entry\Entry; 7use FreeDSx\Ldap\Exception\OperationException; 8use FreeDSx\Ldap\Exception\ProtocolException; 9use FreeDSx\Ldap\Operations; 10use FreeDSx\Ldap\Search\Filters; 11 12class ADClient extends Client 13{ 14 15 /** @inheritDoc */ 16 public function getUser($username, $fetchgroups = true) 17 { 18 if (!$this->autoAuth()) return null; 19 20 $filter = Filters::and( 21 Filters::equal('objectClass', 'user'), 22 Filters::equal('userPrincipalName', $username) 23 ); 24 $this->debug('Searching ' . $filter->toString(), __FILE__, __LINE__); 25 26 try { 27 /** @var Entries $entries */ 28 $entries = $this->ldap->search(Operations::search($filter)); 29 } catch (OperationException $e) { 30 $this->fatal($e); 31 return null; 32 } 33 if ($entries->count() !== 1) return null; 34 $entry = $entries->first(); 35 return $this->entry2User($entry); 36 } 37 38 /** @inheritDoc */ 39 public function getGroups($match = null, $filtermethod = 'equal') 40 { 41 if (!$this->autoAuth()) return []; 42 43 $filter = Filters::and( 44 Filters::equal('objectClass', 'group') 45 ); 46 if ($match !== null) { 47 $filter->add(Filters::$filtermethod('cn', $match)); 48 } 49 50 $this->debug('Searching ' . $filter->toString(), __FILE__, __LINE__); 51 $search = Operations::search($filter, 'cn'); 52 $paging = $this->ldap->paging($search); 53 54 $groups = []; 55 while ($paging->hasEntries()) { 56 try { 57 $entries = $paging->getEntries(); 58 } catch (ProtocolException $e) { 59 $this->fatal($e); 60 return $groups; // we return what we got so far 61 } 62 63 foreach ($entries as $entry) { 64 /** @var Entry $entry */ 65 $groups[$entry->getDn()->toString()] = $this->attr2str($entry->get('cn')); 66 } 67 } 68 69 return $groups; 70 } 71 72 /** 73 * Fetch users matching the given filters 74 * 75 * @param array $match 76 * @param string $filtermethod The method to use for filtering 77 * @return array 78 */ 79 public function getFilteredUsers($match, $filtermethod = 'equal') 80 { 81 if (!$this->autoAuth()) return []; 82 83 $filter = Filters::and(Filters::equal('objectClass', 'user')); 84 if (isset($match['user'])) { 85 $filter->add(Filters::$filtermethod('userPrincipalName', $match['user'])); 86 } 87 if (isset($match['name'])) { 88 $filter->add(Filters::$filtermethod('displayName', $match['name'])); 89 } 90 if (isset($match['mail'])) { 91 $filter->add(Filters::$filtermethod('mail', $match['mail'])); 92 } 93 if (isset($match['grps'])) { 94 // memberOf can not be checked with a substring match, so we need to get the right groups first 95 $groups = $this->getGroups($match['grps'], $filtermethod); 96 $or = Filters::or(); 97 foreach ($groups as $dn => $group) { 98 $or->add(Filters::equal('memberOf', $dn)); 99 } 100 $filter->add($or); 101 } 102 $this->debug('Searching ' . $filter->toString(), __FILE__, __LINE__); 103 $search = Operations::search($filter); 104 $paging = $this->ldap->paging($search); 105 106 $users = []; 107 while ($paging->hasEntries()) { 108 try { 109 $entries = $paging->getEntries(); 110 } catch (ProtocolException $e) { 111 $this->fatal($e); 112 return $users; // we return what we got so far 113 } 114 115 foreach ($entries as $entry) { 116 $users[] = $this->entry2User($entry); 117 } 118 } 119 120 return $users; 121 } 122 123 /** 124 * Transform an LDAP entry to a user info array 125 * 126 * @param Entry $entry 127 * @return array 128 */ 129 protected function entry2User(Entry $entry) 130 { 131 return [ 132 'user' => $this->attr2str($entry->get('UserPrincipalName')), 133 'name' => $this->attr2str($entry->get('DisplayName')) ?: $this->attr2str($entry->get('Name')), 134 'mail' => $this->attr2str($entry->get('mail')), 135 'dn' => $entry->getDn()->toString(), 136 'grps' => $this->getUserGroups($entry), // we always return groups because its currently inexpensive 137 ]; 138 } 139 140 /** 141 * Get the list of groups the given user is member of 142 * 143 * This method currently does no LDAP queries and thus is inexpensive. 144 * 145 * @param Entry $userentry 146 * @return array 147 * @todo implement nested group memberships 148 */ 149 protected function getUserGroups(Entry $userentry) 150 { 151 $groups = [$this->config['defaultgroup']]; // always add default 152 153 // we simply take the first CN= part of the group DN and return it as the group name 154 // this should be correct for ActiveDirectory and saves us additional LDAP queries 155 if ($userentry->has('memberOf')) { 156 foreach ($userentry->get('memberOf')->getValues() as $dn) { 157 list($cn) = explode(',', $dn, 2); 158 $groups[] = substr($cn, 3); 159 } 160 } 161 162 // resolving the primary group in AD is complicated but basically never needed 163 // http://support.microsoft.com/?kbid=321360 164 $gid = $userentry->get('primaryGroupID')->firstValue(); 165 if ($gid == 513) { 166 $groups[] = 'Domain Users'; 167 } 168 169 return $groups; 170 } 171} 172