blob: 7a89457c1149d2a071ccfdd29cfe37c0cf317b3e [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace Adldap\Models;
4
5use Adldap\Utilities;
6use InvalidArgumentException;
7
8/**
9 * Class Group.
10 *
11 * Represents an LDAP group (security / distribution).
12 */
13class Group extends Entry
14{
15 use Concerns\HasMemberOf;
16 use Concerns\HasDescription;
17
18 /**
19 * Returns all users apart of the current group.
20 *
21 * @link https://msdn.microsoft.com/en-us/library/ms677097(v=vs.85).aspx
22 *
23 * @return \Adldap\Query\Collection
24 */
25 public function getMembers()
26 {
27 $members = $this->getMembersFromAttribute($this->schema->member());
28
29 if (count($members) === 0) {
30 $members = $this->getPaginatedMembers();
31 }
32
33 return $this->newCollection($members);
34 }
35
36 /**
37 * Returns the group's member names only.
38 *
39 * @return array
40 */
41 public function getMemberNames()
42 {
43 $members = [];
44
45 $dns = $this->getAttribute($this->schema->member()) ?: [];
46
47 foreach ($dns as $dn) {
48 $exploded = Utilities::explodeDn($dn);
49
50 if (array_key_exists(0, $exploded)) {
51 $members[] = $exploded[0];
52 }
53 }
54
55 return $members;
56 }
57
58 /**
59 * Sets the groups members using an array of user DNs.
60 *
61 * @param array $entries
62 *
63 * @return $this
64 */
65 public function setMembers(array $entries)
66 {
67 return $this->setAttribute($this->schema->member(), $entries);
68 }
69
70 /**
71 * Adds multiple entries to the current group.
72 *
73 * @param array $members
74 *
75 * @return bool
76 */
77 public function addMembers(array $members)
78 {
79 $members = array_map(function ($member) {
80 return $member instanceof Model
81 ? $member->getDn()
82 : $member;
83 }, $members);
84
85 $mod = $this->newBatchModification(
86 $this->schema->member(),
87 LDAP_MODIFY_BATCH_ADD,
88 $members
89 );
90
91 return $this->addModification($mod)->save();
92 }
93
94 /**
95 * Adds an entry to the current group.
96 *
97 * @param string|Entry $member
98 *
99 * @throws InvalidArgumentException When the given entry is empty or contains no distinguished name.
100 *
101 * @return bool
102 */
103 public function addMember($member)
104 {
105 $member = ($member instanceof Model ? $member->getDn() : $member);
106
107 if (is_null($member)) {
108 throw new InvalidArgumentException(
109 'Cannot add member to group. The members distinguished name cannot be null.'
110 );
111 }
112
113 $mod = $this->newBatchModification(
114 $this->schema->member(),
115 LDAP_MODIFY_BATCH_ADD,
116 [$member]
117 );
118
119 return $this->addModification($mod)->save();
120 }
121
122 /**
123 * Removes an entry from the current group.
124 *
125 * @param string|Entry $member
126 *
127 * @throws InvalidArgumentException
128 *
129 * @return bool
130 */
131 public function removeMember($member)
132 {
133 $member = ($member instanceof Model ? $member->getDn() : $member);
134
135 if (is_null($member)) {
136 throw new InvalidArgumentException(
137 'Cannot remove member to group. The members distinguished name cannot be null.'
138 );
139 }
140
141 $mod = $this->newBatchModification(
142 $this->schema->member(),
143 LDAP_MODIFY_BATCH_REMOVE,
144 [$member]
145 );
146
147 return $this->addModification($mod)->save();
148 }
149
150 /**
151 * Removes all members from the current group.
152 *
153 * @return bool
154 */
155 public function removeMembers()
156 {
157 $mod = $this->newBatchModification(
158 $this->schema->member(),
159 LDAP_MODIFY_BATCH_REMOVE_ALL
160 );
161
162 return $this->addModification($mod)->save();
163 }
164
165 /**
166 * Returns the group type integer.
167 *
168 * @link https://msdn.microsoft.com/en-us/library/ms675935(v=vs.85).aspx
169 *
170 * @return string
171 */
172 public function getGroupType()
173 {
174 return $this->getFirstAttribute($this->schema->groupType());
175 }
176
177 /**
178 * Retrieves group members by the specified model attribute.
179 *
180 * @param $attribute
181 *
182 * @return array
183 */
184 protected function getMembersFromAttribute($attribute)
185 {
186 $members = [];
187
188 $entries = $this->getAttribute($attribute) ?: [];
189
190 $query = $this->query->newInstance();
191
192 // Retrieving the member identifier to allow
193 // compatibility with LDAP variants.
194 $identifier = $this->schema->memberIdentifier();
195
196 foreach ($entries as $entry) {
197 // If our identifier is a distinguished name, then we need to
198 // use an alternate query method, as we can't locate records
199 // by distinguished names using an LDAP filter.
200 if ($identifier == 'dn' || $identifier == 'distinguishedname') {
201 $member = $query->findByDn($entry);
202 } else {
203 // We'll ensure we clear our filters when retrieving each member,
204 // so we can continue fetching the next one in line.
205 $member = $query->clearFilters()->findBy($identifier, $entry);
206 }
207
208 // We'll double check that we've received a model from
209 // our query before adding it into our results.
210 if ($member instanceof Model) {
211 $members[] = $member;
212 }
213 }
214
215 return $members;
216 }
217
218 /**
219 * Retrieves members that are contained in a member range.
220 *
221 * @return array
222 */
223 protected function getPaginatedMembers()
224 {
225 $members = [];
226
227 $keys = array_keys($this->attributes);
228
229 // We need to filter out the model attributes so
230 // we only retrieve the member range.
231 $attributes = array_values(array_filter($keys, function ($key) {
232 return strpos($key, 'member;range') !== false;
233 }));
234
235 // We'll grab the member range key so we can run a
236 // regex on it to determine the range.
237 $key = reset($attributes);
238
239 preg_match_all(
240 '/member;range\=([0-9]{1,4})-([0-9*]{1,4})/',
241 $key,
242 $matches
243 );
244
245 if ($key && count($matches) == 3) {
246 // Retrieve the ending range number.
247 $to = $matches[2][0];
248
249 // Retrieve the current groups members from the
250 // current range string (ex. 'member;0-50').
251 $members = $this->getMembersFromAttribute($key);
252
253 // If the query already included all member results (indicated
254 // by the '*'), then we can return here. Otherwise we need
255 // to continue on and retrieve the rest.
256 if ($to === '*') {
257 return $members;
258 }
259
260 // Determine the amount of members we're requesting per query.
261 $range = $to - $matches[1][0];
262
263 // Set our starting range to our last end range plus one.
264 $from = $to + 1;
265
266 // We'll determine the new end range by adding the
267 // total range to our new starting range.
268 $to = $from + $range;
269
270 // We'll need to query for the current model again but with
271 // a new range to retrieve the other members.
272 /** @var Group $group */
273 $group = $this->query->newInstance()->findByDn(
274 $this->getDn(),
275 [$this->query->getSchema()->memberRange($from, $to)]
276 );
277
278 // Finally, we'll merge our current members
279 // with the newly returned members.
280 $members = array_merge(
281 $members,
282 $group->getMembers()->toArray()
283 );
284 }
285
286 return $members;
287 }
288}