|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace SimpleSAML\Module\ldap\Auth; |
|
4
|
|
|
|
|
5
|
|
|
use SimpleSAML\Error; |
|
6
|
|
|
use SimpleSAML\Logger; |
|
7
|
|
|
|
|
8
|
|
|
use Webmozart\Assert\Assert; |
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* Constants defining possible errors |
|
12
|
|
|
*/ |
|
13
|
|
|
|
|
14
|
|
|
define('ERR_INTERNAL', 1); |
|
15
|
|
|
define('ERR_NO_USER', 2); |
|
16
|
|
|
define('ERR_WRONG_PW', 3); |
|
17
|
|
|
define('ERR_AS_DATA_INCONSIST', 4); |
|
18
|
|
|
define('ERR_AS_INTERNAL', 5); |
|
19
|
|
|
define('ERR_AS_ATTRIBUTE', 6); |
|
20
|
|
|
|
|
21
|
|
|
// not defined in earlier PHP versions |
|
22
|
|
|
if (!defined('LDAP_OPT_DIAGNOSTIC_MESSAGE')) { |
|
23
|
|
|
define('LDAP_OPT_DIAGNOSTIC_MESSAGE', 0x0032); |
|
24
|
|
|
} |
|
25
|
|
|
|
|
26
|
|
|
/** |
|
27
|
|
|
* The LDAP class holds helper functions to access an LDAP database. |
|
28
|
|
|
* |
|
29
|
|
|
* @author Andreas Aakre Solberg, UNINETT AS. <[email protected]> |
|
30
|
|
|
* @author Anders Lund, UNINETT AS. <[email protected]> |
|
31
|
|
|
* @package SimpleSAMLphp |
|
32
|
|
|
*/ |
|
33
|
|
|
|
|
34
|
|
|
class Ldap |
|
35
|
|
|
{ |
|
36
|
|
|
/** |
|
37
|
|
|
* LDAP link identifier. |
|
38
|
|
|
* |
|
39
|
|
|
* @var resource |
|
40
|
|
|
*/ |
|
41
|
|
|
protected $ldap; |
|
42
|
|
|
|
|
43
|
|
|
/** |
|
44
|
|
|
* LDAP user: authz_id if SASL is in use, binding dn otherwise |
|
45
|
|
|
* |
|
46
|
|
|
* @var string|null |
|
47
|
|
|
*/ |
|
48
|
|
|
protected $authz_id = null; |
|
49
|
|
|
|
|
50
|
|
|
/** |
|
51
|
|
|
* Timeout value, in seconds. |
|
52
|
|
|
* |
|
53
|
|
|
* @var int |
|
54
|
|
|
*/ |
|
55
|
|
|
protected $timeout = 0; |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* Private constructor restricts instantiation to getInstance(). |
|
59
|
|
|
* |
|
60
|
|
|
* @param string $hostname |
|
61
|
|
|
* @param bool $enable_tls |
|
62
|
|
|
* @param bool $debug |
|
63
|
|
|
* @param int $timeout |
|
64
|
|
|
* @param int $port |
|
65
|
|
|
* @param bool $referrals |
|
66
|
|
|
* @psalm-suppress NullArgument |
|
67
|
|
|
*/ |
|
68
|
|
|
public function __construct( |
|
69
|
|
|
$hostname, |
|
70
|
|
|
$enable_tls = true, |
|
71
|
|
|
$debug = false, |
|
72
|
|
|
$timeout = 0, |
|
73
|
|
|
$port = 389, |
|
74
|
|
|
$referrals = true |
|
75
|
|
|
) { |
|
76
|
|
|
// Debug |
|
77
|
|
|
Logger::debug('Library - LDAP __construct(): Setup LDAP with '. |
|
78
|
|
|
'host=\''.$hostname. |
|
79
|
|
|
'\', tls='.var_export($enable_tls, true). |
|
80
|
|
|
', debug='.var_export($debug, true). |
|
81
|
|
|
', timeout='.var_export($timeout, true). |
|
82
|
|
|
', referrals='.var_export($referrals, true)); |
|
83
|
|
|
|
|
84
|
|
|
/* |
|
85
|
|
|
* Set debug level before calling connect. Note that this passes |
|
86
|
|
|
* NULL to ldap_set_option, which is an undocumented feature. |
|
87
|
|
|
* |
|
88
|
|
|
* OpenLDAP 2.x.x or Netscape Directory SDK x.x needed for this option. |
|
89
|
|
|
*/ |
|
90
|
|
|
if ($debug && !ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7)) { |
|
91
|
|
|
Logger::warning('Library - LDAP __construct(): Unable to set debug level (LDAP_OPT_DEBUG_LEVEL) to 7'); |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/* |
|
95
|
|
|
* Prepare a connection for to this LDAP server. Note that this function |
|
96
|
|
|
* doesn't actually connect to the server. |
|
97
|
|
|
*/ |
|
98
|
|
|
$resource = @ldap_connect($hostname, $port); |
|
99
|
|
|
if ($resource === false) { |
|
100
|
|
|
throw $this->makeException( |
|
101
|
|
|
'Library - LDAP __construct(): Unable to connect to \''.$hostname.'\'', |
|
102
|
|
|
ERR_INTERNAL |
|
103
|
|
|
); |
|
104
|
|
|
} |
|
105
|
|
|
$this->ldap = $resource; |
|
106
|
|
|
|
|
107
|
|
|
// Enable LDAP protocol version 3 |
|
108
|
|
|
if (!@ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3)) { |
|
109
|
|
|
throw $this->makeException( |
|
110
|
|
|
'Library - LDAP __construct(): Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', |
|
111
|
|
|
ERR_INTERNAL |
|
112
|
|
|
); |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
// Set referral option |
|
116
|
|
|
if (!@ldap_set_option($this->ldap, LDAP_OPT_REFERRALS, $referrals)) { |
|
117
|
|
|
throw $this->makeException( |
|
118
|
|
|
'Library - LDAP __construct(): Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to '.$referrals, |
|
119
|
|
|
ERR_INTERNAL |
|
120
|
|
|
); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
// Set timeouts, if supported |
|
124
|
|
|
// (OpenLDAP 2.x.x or Netscape Directory SDK x.x needed) |
|
125
|
|
|
$this->timeout = $timeout; |
|
126
|
|
|
if ($timeout > 0) { |
|
127
|
|
|
if (!@ldap_set_option($this->ldap, LDAP_OPT_NETWORK_TIMEOUT, $timeout)) { |
|
128
|
|
|
Logger::warning( |
|
129
|
|
|
'Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_NETWORK_TIMEOUT) to '.$timeout |
|
130
|
|
|
); |
|
131
|
|
|
} |
|
132
|
|
|
if (!@ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, $timeout)) { |
|
133
|
|
|
Logger::warning( |
|
134
|
|
|
'Library - LDAP __construct(): Unable to set timeouts (LDAP_OPT_TIMELIMIT) to '.$timeout |
|
135
|
|
|
); |
|
136
|
|
|
} |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
// Enable TLS, if needed |
|
140
|
|
|
if (stripos($hostname, "ldaps:") === false && $enable_tls) { |
|
141
|
|
|
if (!@ldap_start_tls($this->ldap)) { |
|
142
|
|
|
throw $this->makeException('Library - LDAP __construct():'. |
|
143
|
|
|
' Unable to force TLS', ERR_INTERNAL); |
|
144
|
|
|
} |
|
145
|
|
|
} |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
|
|
149
|
|
|
/** |
|
150
|
|
|
* Convenience method to create an LDAPException as well as log the |
|
151
|
|
|
* description. |
|
152
|
|
|
* |
|
153
|
|
|
* @param string $description The exception's description |
|
154
|
|
|
* @param int|null $type The exception's type |
|
155
|
|
|
* @return \Exception |
|
156
|
|
|
*/ |
|
157
|
|
|
private function makeException($description, $type = null) |
|
158
|
|
|
{ |
|
159
|
|
|
$errNo = @ldap_errno($this->ldap); |
|
160
|
|
|
|
|
161
|
|
|
// Decide exception type and return |
|
162
|
|
|
if ($type !== null) { |
|
163
|
|
|
if ($errNo !== 0) { |
|
164
|
|
|
// Only log real LDAP errors; not success |
|
165
|
|
|
Logger::error($description.'; cause: \''.ldap_error($this->ldap).'\' (0x'.dechex($errNo).')'); |
|
166
|
|
|
} else { |
|
167
|
|
|
Logger::error($description); |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
|
|
switch ($type) { |
|
171
|
|
|
case ERR_INTERNAL:// 1 - ExInternal |
|
172
|
|
|
return new Error\Exception($description, $errNo); |
|
173
|
|
|
case ERR_NO_USER:// 2 - ExUserNotFound |
|
174
|
|
|
return new Error\UserNotFound($description, $errNo); |
|
175
|
|
|
case ERR_WRONG_PW:// 3 - ExInvalidCredential |
|
176
|
|
|
return new Error\InvalidCredential($description, $errNo); |
|
177
|
|
|
case ERR_AS_DATA_INCONSIST:// 4 - ExAsDataInconsist |
|
178
|
|
|
return new Error\AuthSource('ldap', $description); |
|
179
|
|
|
case ERR_AS_INTERNAL:// 5 - ExAsInternal |
|
180
|
|
|
return new Error\AuthSource('ldap', $description); |
|
181
|
|
|
} |
|
182
|
|
|
} else { |
|
183
|
|
|
if ($errNo !== 0) { |
|
184
|
|
|
$description .= '; cause: \''.ldap_error($this->ldap).'\' (0x'.dechex($errNo).')'; |
|
185
|
|
|
if (@ldap_get_option($this->ldap, LDAP_OPT_DIAGNOSTIC_MESSAGE, $extendedError) |
|
186
|
|
|
&& !empty($extendedError) |
|
187
|
|
|
) { |
|
188
|
|
|
$description .= '; additional: \''.$extendedError.'\''; |
|
189
|
|
|
} |
|
190
|
|
|
} |
|
191
|
|
|
switch ($errNo) { |
|
192
|
|
|
case 0x20://LDAP_NO_SUCH_OBJECT |
|
193
|
|
|
Logger::warning($description); |
|
194
|
|
|
return new Error\UserNotFound($description, $errNo); |
|
195
|
|
|
case 0x31://LDAP_INVALID_CREDENTIALS |
|
196
|
|
|
Logger::info($description); |
|
197
|
|
|
return new Error\InvalidCredential($description, $errNo); |
|
198
|
|
|
case -1://NO_SERVER_CONNECTION |
|
199
|
|
|
Logger::error($description); |
|
200
|
|
|
return new Error\AuthSource('ldap', $description); |
|
201
|
|
|
default: |
|
202
|
|
|
Logger::error($description); |
|
203
|
|
|
return new Error\AuthSource('ldap', $description); |
|
204
|
|
|
} |
|
205
|
|
|
} |
|
206
|
|
|
return new \Exception('Unknown LDAP error.'); |
|
207
|
|
|
} |
|
208
|
|
|
|
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* Search for DN from a single base. |
|
212
|
|
|
* |
|
213
|
|
|
* @param string $base |
|
214
|
|
|
* Indication of root of subtree to search |
|
215
|
|
|
* @param string|array $attribute |
|
216
|
|
|
* The attribute name(s) to search for. |
|
217
|
|
|
* @param string $value |
|
218
|
|
|
* The attribute value to search for. |
|
219
|
|
|
* Additional search filter |
|
220
|
|
|
* @param string|null $searchFilter |
|
221
|
|
|
* The scope of the search |
|
222
|
|
|
* @param string $scope |
|
223
|
|
|
* @return string |
|
224
|
|
|
* The DN of the resulting found element. |
|
225
|
|
|
* @throws Error\Exception if: |
|
226
|
|
|
* - Attribute parameter is wrong type |
|
227
|
|
|
* @throws Error\AuthSource if: |
|
228
|
|
|
* - Not able to connect to LDAP server |
|
229
|
|
|
* - False search result |
|
230
|
|
|
* - Count return false |
|
231
|
|
|
* - Searche found more than one result |
|
232
|
|
|
* - Failed to get first entry from result |
|
233
|
|
|
* - Failed to get DN for entry |
|
234
|
|
|
* @throws Error\UserNotFound if: |
|
235
|
|
|
* - Zero entries were found |
|
236
|
|
|
* @psalm-suppress TypeDoesNotContainType |
|
237
|
|
|
*/ |
|
238
|
|
|
private function search($base, $attribute, $value, $searchFilter = null, $scope = "subtree") |
|
239
|
|
|
{ |
|
240
|
|
|
// Create the search filter |
|
241
|
|
|
/** @var array $attribute */ |
|
242
|
|
|
$attribute = self::escape_filter_value($attribute, false); |
|
243
|
|
|
|
|
244
|
|
|
/** @var string $value */ |
|
245
|
|
|
$value = self::escape_filter_value($value, true); |
|
246
|
|
|
|
|
247
|
|
|
$filter = ''; |
|
248
|
|
|
foreach ($attribute as $attr) { |
|
249
|
|
|
$filter .= '('.$attr.'='.$value.')'; |
|
250
|
|
|
} |
|
251
|
|
|
$filter = '(|'.$filter.')'; |
|
252
|
|
|
|
|
253
|
|
|
// Append LDAP filters if defined |
|
254
|
|
|
if ($searchFilter !== null) { |
|
255
|
|
|
$filter = "(&".$filter."".$searchFilter.")"; |
|
256
|
|
|
} |
|
257
|
|
|
|
|
258
|
|
|
// Search using generated filter |
|
259
|
|
|
Logger::debug('Library - LDAP search(): Searching base ('.$scope.') \''.$base.'\' for \''.$filter.'\''); |
|
260
|
|
|
if ($scope === 'base') { |
|
261
|
|
|
$result = @ldap_read($this->ldap, $base, $filter, [], 0, 0, $this->timeout, LDAP_DEREF_NEVER); |
|
262
|
|
|
} elseif ($scope === 'onelevel') { |
|
263
|
|
|
$result = @ldap_list($this->ldap, $base, $filter, [], 0, 0, $this->timeout, LDAP_DEREF_NEVER); |
|
264
|
|
|
} else { |
|
265
|
|
|
$result = @ldap_search($this->ldap, $base, $filter, [], 0, 0, $this->timeout, LDAP_DEREF_NEVER); |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
if ($result === false) { |
|
269
|
|
|
throw $this->makeException( |
|
270
|
|
|
'Library - LDAP search(): Failed search on base \''.$base.'\' for \''.$filter.'\'' |
|
271
|
|
|
); |
|
272
|
|
|
} |
|
273
|
|
|
|
|
274
|
|
|
// Sanity checks on search results |
|
275
|
|
|
$count = @ldap_count_entries($this->ldap, $result); |
|
276
|
|
|
if ($count === false) { |
|
277
|
|
|
throw $this->makeException('Library - LDAP search(): Failed to get number of entries returned'); |
|
278
|
|
|
} elseif ($count > 1) { |
|
279
|
|
|
// More than one entry is found. External error |
|
280
|
|
|
throw $this->makeException( |
|
281
|
|
|
'Library - LDAP search(): Found '.$count.' entries searching base \''.$base.'\' for \''.$filter.'\'', |
|
282
|
|
|
ERR_AS_DATA_INCONSIST |
|
283
|
|
|
); |
|
284
|
|
|
} elseif ($count === 0) { |
|
285
|
|
|
// No entry is fond => wrong username is given (or not registered in the catalogue). User error |
|
286
|
|
|
throw $this->makeException( |
|
287
|
|
|
'Library - LDAP search(): Found no entries searching base \''.$base.'\' for \''.$filter.'\'', |
|
288
|
|
|
ERR_NO_USER |
|
289
|
|
|
); |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
|
|
293
|
|
|
// Resolve the DN from the search result |
|
294
|
|
|
$entry = @ldap_first_entry($this->ldap, $result); |
|
295
|
|
|
if ($entry === false) { |
|
296
|
|
|
throw $this->makeException( |
|
297
|
|
|
'Library - LDAP search(): Unable to retrieve result after searching base \''. |
|
298
|
|
|
$base.'\' for \''.$filter.'\'' |
|
299
|
|
|
); |
|
300
|
|
|
} |
|
301
|
|
|
$dn = @ldap_get_dn($this->ldap, $entry); |
|
302
|
|
|
if ($dn === false) { |
|
303
|
|
|
throw $this->makeException( |
|
304
|
|
|
'Library - LDAP search(): Unable to get DN after searching base \''.$base.'\' for \''.$filter.'\'' |
|
305
|
|
|
); |
|
306
|
|
|
} |
|
307
|
|
|
return $dn; |
|
308
|
|
|
} |
|
309
|
|
|
|
|
310
|
|
|
|
|
311
|
|
|
/** |
|
312
|
|
|
* Search for a DN. |
|
313
|
|
|
* |
|
314
|
|
|
* @param string|array $base |
|
315
|
|
|
* The base, or bases, which to search from. |
|
316
|
|
|
* @param string|array $attribute |
|
317
|
|
|
* The attribute name(s) searched for. |
|
318
|
|
|
* @param string $value |
|
319
|
|
|
* The attribute value searched for. |
|
320
|
|
|
* @param bool $allowZeroHits |
|
321
|
|
|
* Determines if the method will throw an exception if no hits are found. |
|
322
|
|
|
* Defaults to FALSE. |
|
323
|
|
|
* @param string|null $searchFilter |
|
324
|
|
|
* Additional searchFilter to be added to the (attribute=value) filter |
|
325
|
|
|
* @param string $scope |
|
326
|
|
|
* The scope of the search |
|
327
|
|
|
* @return string|null |
|
328
|
|
|
* The DN of the matching element, if found. If no element was found and |
|
329
|
|
|
* $allowZeroHits is set to FALSE, an exception will be thrown; otherwise |
|
330
|
|
|
* NULL will be returned. |
|
331
|
|
|
* @throws Error\AuthSource if: |
|
332
|
|
|
* - LDAP search encounter some problems when searching cataloge |
|
333
|
|
|
* - Not able to connect to LDAP server |
|
334
|
|
|
* @throws Error\UserNotFound if: |
|
335
|
|
|
* - $allowZeroHits is FALSE and no result is found |
|
336
|
|
|
* |
|
337
|
|
|
*/ |
|
338
|
|
|
public function searchfordn( |
|
339
|
|
|
$base, |
|
340
|
|
|
$attribute, |
|
341
|
|
|
$value, |
|
342
|
|
|
$allowZeroHits = false, |
|
343
|
|
|
$searchFilter = null, |
|
344
|
|
|
$scope = 'subtree' |
|
345
|
|
|
) { |
|
346
|
|
|
// Traverse all search bases, returning DN if found |
|
347
|
|
|
$bases = \SimpleSAML\Utils\Arrays::arrayize($base); |
|
348
|
|
|
foreach ($bases as $current) { |
|
349
|
|
|
try { |
|
350
|
|
|
// Single base search |
|
351
|
|
|
$result = $this->search($current, $attribute, $value, $searchFilter, $scope); |
|
352
|
|
|
|
|
353
|
|
|
// We don't hawe to look any futher if user is found |
|
354
|
|
|
if (!empty($result)) { |
|
355
|
|
|
return $result; |
|
356
|
|
|
} |
|
357
|
|
|
// If search failed, attempt the other base DNs |
|
358
|
|
|
} catch (Error\UserNotFound $e) { |
|
359
|
|
|
// Just continue searching |
|
360
|
|
|
} |
|
361
|
|
|
} |
|
362
|
|
|
// Decide what to do for zero entries |
|
363
|
|
|
Logger::debug('Library - LDAP searchfordn(): No entries found'); |
|
364
|
|
|
if ($allowZeroHits) { |
|
365
|
|
|
// Zero hits allowed |
|
366
|
|
|
return null; |
|
367
|
|
|
} else { |
|
368
|
|
|
// Zero hits not allowed |
|
369
|
|
|
throw $this->makeException('Library - LDAP searchfordn(): LDAP search returned zero entries for'. |
|
370
|
|
|
' filter \'('.join(' | ', \SimpleSAML\Utils\Arrays::arrayize($attribute)).' = '.$value.')\' on base(s) \'('.join(' & ', $bases).')\'', 2); |
|
371
|
|
|
} |
|
372
|
|
|
} |
|
373
|
|
|
|
|
374
|
|
|
|
|
375
|
|
|
/** |
|
376
|
|
|
* This method was created specifically for the ldap:AttributeAddUsersGroups->searchActiveDirectory() |
|
377
|
|
|
* method, but could be used for other LDAP search needs. It will search LDAP and return all the entries. |
|
378
|
|
|
* |
|
379
|
|
|
* @throws \Exception |
|
380
|
|
|
* @param string|array $bases |
|
381
|
|
|
* @param string|array $filters Array of 'attribute' => 'values' to be combined into the filter, |
|
382
|
|
|
* or a raw filter string |
|
383
|
|
|
* @param string|array $attributes Array of attributes requested from LDAP |
|
384
|
|
|
* @param bool $and If multiple filters defined, then either bind them with & or | |
|
385
|
|
|
* @param bool $escape Weather to escape the filter values or not |
|
386
|
|
|
* @param string $scope The scope of the search |
|
387
|
|
|
* @return array |
|
388
|
|
|
*/ |
|
389
|
|
|
public function searchformultiple( |
|
390
|
|
|
$bases, |
|
391
|
|
|
$filters, |
|
392
|
|
|
$attributes = [], |
|
393
|
|
|
$and = true, |
|
394
|
|
|
$escape = true, |
|
395
|
|
|
$scope = 'subtree' |
|
396
|
|
|
) { |
|
397
|
|
|
// Escape the filter values, if requested |
|
398
|
|
|
if ($escape) { |
|
399
|
|
|
$filters = $this->escape_filter_value($filters, false); |
|
400
|
|
|
} |
|
401
|
|
|
|
|
402
|
|
|
// Build search filter |
|
403
|
|
|
$filter = ''; |
|
404
|
|
|
if (is_array($filters)) { |
|
405
|
|
|
foreach ($filters as $attribute => $value) { |
|
406
|
|
|
$filter .= "($attribute=$value)"; |
|
407
|
|
|
} |
|
408
|
|
|
if (count($filters) > 1) { |
|
409
|
|
|
$filter = ($and ? '(&' : '(|').$filter.')'; |
|
410
|
|
|
} |
|
411
|
|
|
} else { |
|
412
|
|
|
Assert::string($filters); |
|
413
|
|
|
$filter = $filters; |
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
|
|
// Verify filter was created |
|
417
|
|
|
if ($filter == '' || $filter == '(=)') { |
|
418
|
|
|
throw $this->makeException('ldap:LdapConnection->search_manual : No search filters defined', ERR_INTERNAL); |
|
419
|
|
|
} |
|
420
|
|
|
|
|
421
|
|
|
// Verify at least one base was passed |
|
422
|
|
|
$bases = (array) $bases; |
|
423
|
|
|
if (empty($bases)) { |
|
424
|
|
|
throw $this->makeException('ldap:LdapConnection->search_manual : No base DNs were passed', ERR_INTERNAL); |
|
425
|
|
|
} |
|
426
|
|
|
|
|
427
|
|
|
$attributes = \SimpleSAML\Utils\Arrays::arrayize($attributes); |
|
428
|
|
|
|
|
429
|
|
|
// Search each base until result is found |
|
430
|
|
|
$result = false; |
|
431
|
|
|
foreach ($bases as $base) { |
|
432
|
|
|
if ($scope === 'base') { |
|
433
|
|
|
$result = @ldap_read($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); |
|
434
|
|
|
} elseif ($scope === 'onelevel') { |
|
435
|
|
|
$result = @ldap_list($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); |
|
436
|
|
|
} else { |
|
437
|
|
|
$result = @ldap_search($this->ldap, $base, $filter, $attributes, 0, 0, $this->timeout); |
|
438
|
|
|
} |
|
439
|
|
|
|
|
440
|
|
|
if ($result !== false && @ldap_count_entries($this->ldap, $result) > 0) { |
|
441
|
|
|
break; |
|
442
|
|
|
} |
|
443
|
|
|
} |
|
444
|
|
|
|
|
445
|
|
|
// Verify that a result was found in one of the bases |
|
446
|
|
|
if ($result === false) { |
|
447
|
|
|
throw $this->makeException( |
|
448
|
|
|
'ldap:LdapConnection->search_manual : Failed to search LDAP using base(s) ['. |
|
449
|
|
|
implode('; ', $bases).'] with filter ['.$filter.']. LDAP error ['. |
|
450
|
|
|
ldap_error($this->ldap).']' |
|
451
|
|
|
); |
|
452
|
|
|
} elseif (@ldap_count_entries($this->ldap, $result) < 1) { |
|
453
|
|
|
throw $this->makeException( |
|
454
|
|
|
'ldap:LdapConnection->search_manual : No entries found in LDAP using base(s) ['. |
|
455
|
|
|
implode('; ', $bases).'] with filter ['.$filter.']', |
|
456
|
|
|
ERR_NO_USER |
|
457
|
|
|
); |
|
458
|
|
|
} |
|
459
|
|
|
|
|
460
|
|
|
// Get all results |
|
461
|
|
|
$results = ldap_get_entries($this->ldap, $result); |
|
462
|
|
|
if ($results === false) { |
|
463
|
|
|
throw $this->makeException( |
|
464
|
|
|
'ldap:LdapConnection->search_manual : Unable to retrieve entries from search results' |
|
465
|
|
|
); |
|
466
|
|
|
} |
|
467
|
|
|
|
|
468
|
|
|
// parse each entry and process its attributes |
|
469
|
|
|
for ($i = 0; $i < $results['count']; $i++) { |
|
470
|
|
|
$entry = $results[$i]; |
|
471
|
|
|
|
|
472
|
|
|
// iterate over the attributes of the entry |
|
473
|
|
|
for ($j = 0; $j < $entry['count']; $j++) { |
|
474
|
|
|
$name = $entry[$j]; |
|
475
|
|
|
$attribute = $entry[$name]; |
|
476
|
|
|
|
|
477
|
|
|
// decide whether to base64 encode or not |
|
478
|
|
|
for ($k = 0; $k < $attribute['count']; $k++) { |
|
479
|
|
|
// base64 encode binary attributes |
|
480
|
|
|
if (strtolower($name) === 'jpegphoto' |
|
481
|
|
|
|| strtolower($name) === 'objectguid' |
|
482
|
|
|
|| strtolower($name) === 'objectsid' |
|
483
|
|
|
|| strtolower($name) === 'ms-ds-consistencyguid' |
|
484
|
|
|
) { |
|
485
|
|
|
$results[$i][$name][$k] = base64_encode($attribute[$k]); |
|
486
|
|
|
} |
|
487
|
|
|
} |
|
488
|
|
|
} |
|
489
|
|
|
} |
|
490
|
|
|
|
|
491
|
|
|
// Remove the count and return |
|
492
|
|
|
unset($results['count']); |
|
493
|
|
|
return $results; |
|
494
|
|
|
} |
|
495
|
|
|
|
|
496
|
|
|
|
|
497
|
|
|
/** |
|
498
|
|
|
* Bind to LDAP with a specific DN and password. Simple wrapper around |
|
499
|
|
|
* ldap_bind() with some additional logging. |
|
500
|
|
|
* |
|
501
|
|
|
* @param string $dn |
|
502
|
|
|
* The DN used. |
|
503
|
|
|
* @param string $password |
|
504
|
|
|
* The password used. |
|
505
|
|
|
* @param array $sasl_args |
|
506
|
|
|
* Array of SASL options for SASL bind |
|
507
|
|
|
* @return bool |
|
508
|
|
|
* Returns TRUE if successful, FALSE if |
|
509
|
|
|
* LDAP_INVALID_CREDENTIALS, LDAP_X_PROXY_AUTHZ_FAILURE, |
|
510
|
|
|
* LDAP_INAPPROPRIATE_AUTH, LDAP_INSUFFICIENT_ACCESS |
|
511
|
|
|
* @throws Error\Exception on other errors |
|
512
|
|
|
*/ |
|
513
|
|
|
public function bind($dn, $password, array $sasl_args = null) |
|
514
|
|
|
{ |
|
515
|
|
|
if ($sasl_args != null) { |
|
516
|
|
|
if (!function_exists('ldap_sasl_bind')) { |
|
517
|
|
|
$ex_msg = 'Library - missing SASL support'; |
|
518
|
|
|
throw $this->makeException($ex_msg); |
|
519
|
|
|
} |
|
520
|
|
|
|
|
521
|
|
|
// SASL Bind, with error handling |
|
522
|
|
|
$authz_id = $sasl_args['authz_id']; |
|
523
|
|
|
$error = @ldap_sasl_bind( |
|
524
|
|
|
$this->ldap, |
|
525
|
|
|
$dn, |
|
526
|
|
|
$password, |
|
527
|
|
|
$sasl_args['mech'], |
|
528
|
|
|
$sasl_args['realm'], |
|
529
|
|
|
$sasl_args['authc_id'], |
|
530
|
|
|
$sasl_args['authz_id'], |
|
531
|
|
|
$sasl_args['props'] |
|
532
|
|
|
); |
|
533
|
|
|
} else { |
|
534
|
|
|
// Simple Bind, with error handling |
|
535
|
|
|
$authz_id = $dn; |
|
536
|
|
|
$error = @ldap_bind($this->ldap, $dn, $password); |
|
537
|
|
|
} |
|
538
|
|
|
|
|
539
|
|
|
if ($error === true) { |
|
540
|
|
|
// Good |
|
541
|
|
|
$this->authz_id = $authz_id; |
|
542
|
|
|
Logger::debug('Library - LDAP bind(): Bind successful with DN \''.$dn.'\''); |
|
543
|
|
|
return true; |
|
544
|
|
|
} |
|
545
|
|
|
|
|
546
|
|
|
/* Handle errors |
|
547
|
|
|
* LDAP_INVALID_CREDENTIALS |
|
548
|
|
|
* LDAP_INSUFFICIENT_ACCESS */ |
|
549
|
|
|
switch (ldap_errno($this->ldap)) { |
|
550
|
|
|
case 32: // LDAP_NO_SUCH_OBJECT |
|
551
|
|
|
// no break |
|
552
|
|
|
case 47: // LDAP_X_PROXY_AUTHZ_FAILURE |
|
553
|
|
|
// no break |
|
554
|
|
|
case 48: // LDAP_INAPPROPRIATE_AUTH |
|
555
|
|
|
// no break |
|
556
|
|
|
case 49: // LDAP_INVALID_CREDENTIALS |
|
557
|
|
|
// no break |
|
558
|
|
|
case 50: // LDAP_INSUFFICIENT_ACCESS |
|
559
|
|
|
return false; |
|
560
|
|
|
default: |
|
561
|
|
|
break; |
|
562
|
|
|
} |
|
563
|
|
|
|
|
564
|
|
|
// Bad |
|
565
|
|
|
throw $this->makeException('Library - LDAP bind(): Bind failed with DN \''.$dn.'\''); |
|
566
|
|
|
} |
|
567
|
|
|
|
|
568
|
|
|
|
|
569
|
|
|
/** |
|
570
|
|
|
* Applies an LDAP option to the current connection. |
|
571
|
|
|
* |
|
572
|
|
|
* @throws Exception |
|
573
|
|
|
* @param mixed $option |
|
574
|
|
|
* @param mixed $value |
|
575
|
|
|
* @return void |
|
576
|
|
|
*/ |
|
577
|
|
|
public function setOption($option, $value) |
|
578
|
|
|
{ |
|
579
|
|
|
// Attempt to set the LDAP option |
|
580
|
|
|
if (!@ldap_set_option($this->ldap, $option, $value)) { |
|
581
|
|
|
throw $this->makeException( |
|
582
|
|
|
'ldap:LdapConnection->setOption : Failed to set LDAP option ['. |
|
583
|
|
|
$option.'] with the value ['.$value.'] error: '.ldap_error($this->ldap), |
|
584
|
|
|
ERR_INTERNAL |
|
585
|
|
|
); |
|
586
|
|
|
} |
|
587
|
|
|
|
|
588
|
|
|
// Log debug message |
|
589
|
|
|
Logger::debug( |
|
590
|
|
|
'ldap:LdapConnection->setOption : Set the LDAP option ['. |
|
591
|
|
|
$option.'] with the value ['.$value.']' |
|
592
|
|
|
); |
|
593
|
|
|
} |
|
594
|
|
|
|
|
595
|
|
|
|
|
596
|
|
|
/** |
|
597
|
|
|
* Search a given DN for attributes, and return the resulting associative |
|
598
|
|
|
* array. |
|
599
|
|
|
* |
|
600
|
|
|
* @param string $dn |
|
601
|
|
|
* The DN of an element. |
|
602
|
|
|
* @param string|array $attributes |
|
603
|
|
|
* The names of the attribute(s) to retrieve. Defaults to NULL; that is, |
|
604
|
|
|
* all available attributes. Note that this is not very effective. |
|
605
|
|
|
* @param int $maxsize |
|
606
|
|
|
* The maximum size of any attribute's value(s). If exceeded, the attribute |
|
607
|
|
|
* will not be returned. |
|
608
|
|
|
* @return array |
|
609
|
|
|
* The array of attributes and their values. |
|
610
|
|
|
* @see http://no.php.net/manual/en/function.ldap-read.php |
|
611
|
|
|
*/ |
|
612
|
|
|
public function getAttributes($dn, $attributes = null, $maxsize = null) |
|
613
|
|
|
{ |
|
614
|
|
|
// Preparations, including a pretty debug message... |
|
615
|
|
|
$description = 'all attributes'; |
|
616
|
|
|
if (is_array($attributes)) { |
|
617
|
|
|
$description = '\''.join(',', $attributes).'\''; |
|
618
|
|
|
} else { |
|
619
|
|
|
// Get all attributes... |
|
620
|
|
|
// TODO: Verify that this originally was the intended behaviour. Could $attributes be a string? |
|
621
|
|
|
$attributes = []; |
|
622
|
|
|
} |
|
623
|
|
|
Logger::debug('Library - LDAP getAttributes(): Getting '.$description.' from DN \''.$dn.'\''); |
|
624
|
|
|
|
|
625
|
|
|
// Attempt to get attributes |
|
626
|
|
|
// TODO: Should aliases be dereferenced? |
|
627
|
|
|
/** @var array $attributes */ |
|
628
|
|
|
$result = @ldap_read($this->ldap, $dn, 'objectClass=*', $attributes, 0, 0, $this->timeout); |
|
629
|
|
|
if ($result === false) { |
|
630
|
|
|
throw $this->makeException('Library - LDAP getAttributes(): Failed to get attributes from DN \''.$dn.'\''); |
|
631
|
|
|
} |
|
632
|
|
|
$entry = @ldap_first_entry($this->ldap, $result); |
|
633
|
|
|
if ($entry === false) { |
|
634
|
|
|
throw $this->makeException('Library - LDAP getAttributes(): Could not get first entry from DN \''.$dn.'\''); |
|
635
|
|
|
} |
|
636
|
|
|
unset($attributes); |
|
637
|
|
|
|
|
638
|
|
|
/** @var array|false $attributes */ |
|
639
|
|
|
$attributes = @ldap_get_attributes($this->ldap, $entry); |
|
640
|
|
|
if ($attributes === false) { |
|
641
|
|
|
throw $this->makeException( |
|
642
|
|
|
'Library - LDAP getAttributes(): Could not get attributes of first entry from DN \''.$dn.'\'' |
|
643
|
|
|
); |
|
644
|
|
|
} |
|
645
|
|
|
|
|
646
|
|
|
// Parsing each found attribute into our result set |
|
647
|
|
|
$result = []; // Recycling $result... Possibly bad practice. |
|
648
|
|
|
for ($i = 0; $i < $attributes['count']; $i++) { |
|
649
|
|
|
// Ignore attributes that exceed the maximum allowed size |
|
650
|
|
|
$name = $attributes[$i]; |
|
651
|
|
|
$attribute = $attributes[$name]; |
|
652
|
|
|
|
|
653
|
|
|
// Deciding whether to base64 encode |
|
654
|
|
|
$values = []; |
|
655
|
|
|
for ($j = 0; $j < $attribute['count']; $j++) { |
|
656
|
|
|
$value = $attribute[$j]; |
|
657
|
|
|
|
|
658
|
|
|
if (!empty($maxsize) && strlen($value) > $maxsize) { |
|
659
|
|
|
// Ignoring and warning |
|
660
|
|
|
Logger::warning('Library - LDAP getAttributes(): Attribute \''. |
|
661
|
|
|
$name.'\' exceeded maximum allowed size by '.(strlen($value) - $maxsize)); |
|
662
|
|
|
continue; |
|
663
|
|
|
} |
|
664
|
|
|
|
|
665
|
|
|
// Base64 encode binary attributes |
|
666
|
|
|
if (strtolower($name) === 'jpegphoto' |
|
667
|
|
|
|| strtolower($name) === 'objectguid' |
|
668
|
|
|
|| strtolower($name) === 'objectsid' |
|
669
|
|
|
|| strtolower($name) === 'ms-ds-consistencyguid' |
|
670
|
|
|
) { |
|
671
|
|
|
$values[] = base64_encode($value); |
|
672
|
|
|
} else { |
|
673
|
|
|
$values[] = $value; |
|
674
|
|
|
} |
|
675
|
|
|
} |
|
676
|
|
|
|
|
677
|
|
|
// Adding |
|
678
|
|
|
$result[$name] = $values; |
|
679
|
|
|
} |
|
680
|
|
|
|
|
681
|
|
|
// We're done |
|
682
|
|
|
Logger::debug('Library - LDAP getAttributes(): Found attributes \'('.join(',', array_keys($result)).')\''); |
|
683
|
|
|
return $result; |
|
684
|
|
|
} |
|
685
|
|
|
|
|
686
|
|
|
|
|
687
|
|
|
/** |
|
688
|
|
|
* Enter description here... |
|
689
|
|
|
* |
|
690
|
|
|
* @param array $config |
|
691
|
|
|
* @param string $username |
|
692
|
|
|
* @param string $password |
|
693
|
|
|
* @return array|false |
|
694
|
|
|
*/ |
|
695
|
|
|
public function validate($config, $username, $password = null) |
|
696
|
|
|
{ |
|
697
|
|
|
/** |
|
698
|
|
|
* Escape any characters with a special meaning in LDAP. The following |
|
699
|
|
|
* characters have a special meaning (according to RFC 2253): |
|
700
|
|
|
* ',', '+', '"', '\', '<', '>', ';', '*' |
|
701
|
|
|
* These characters are escaped by prefixing them with '\'. |
|
702
|
|
|
*/ |
|
703
|
|
|
$username = addcslashes($username, ',+"\\<>;*'); |
|
704
|
|
|
|
|
705
|
|
|
if (isset($config['priv_user_dn'])) { |
|
706
|
|
|
$this->bind($config['priv_user_dn'], $config['priv_user_pw']); |
|
707
|
|
|
} |
|
708
|
|
|
if (isset($config['dnpattern'])) { |
|
709
|
|
|
$dn = str_replace('%username%', $username, $config['dnpattern']); |
|
710
|
|
|
} else { |
|
711
|
|
|
/** @var string $dn */ |
|
712
|
|
|
$dn = $this->searchfordn($config['searchbase'], $config['searchattributes'], $username, false); |
|
713
|
|
|
} |
|
714
|
|
|
|
|
715
|
|
|
if ($password !== null) { |
|
716
|
|
|
// checking users credentials ... assuming below that she may read her own attributes ... |
|
717
|
|
|
// escape characters with a special meaning, also in the password |
|
718
|
|
|
$password = addcslashes($password, ',+"\\<>;*'); |
|
719
|
|
|
if (!$this->bind($dn, $password)) { |
|
720
|
|
|
Logger::info( |
|
721
|
|
|
'Library - LDAP validate(): Failed to authenticate \''.$username.'\' using DN \''.$dn.'\'' |
|
722
|
|
|
); |
|
723
|
|
|
return false; |
|
724
|
|
|
} |
|
725
|
|
|
} |
|
726
|
|
|
|
|
727
|
|
|
/** |
|
728
|
|
|
* Retrieve attributes from LDAP |
|
729
|
|
|
*/ |
|
730
|
|
|
$attributes = $this->getAttributes($dn, $config['attributes']); |
|
731
|
|
|
return $attributes; |
|
732
|
|
|
} |
|
733
|
|
|
|
|
734
|
|
|
|
|
735
|
|
|
/** |
|
736
|
|
|
* Borrowed function from PEAR:LDAP. |
|
737
|
|
|
* |
|
738
|
|
|
* Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters. |
|
739
|
|
|
* |
|
740
|
|
|
* Any control characters with an ACII code < 32 as well as the characters with special meaning in |
|
741
|
|
|
* LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a |
|
742
|
|
|
* backslash followed by two hex digits representing the hexadecimal value of the character. |
|
743
|
|
|
* |
|
744
|
|
|
* @static |
|
745
|
|
|
* @param string|array $values Array of values to escape |
|
746
|
|
|
* @param bool $singleValue |
|
747
|
|
|
* @return string|array Array $values, but escaped |
|
748
|
|
|
*/ |
|
749
|
|
|
public static function escape_filter_value($values = [], $singleValue = true) |
|
750
|
|
|
{ |
|
751
|
|
|
// Parameter validation |
|
752
|
|
|
$values = \SimpleSAML\Utils\Arrays::arrayize($values); |
|
753
|
|
|
|
|
754
|
|
|
foreach ($values as $key => $val) { |
|
755
|
|
|
if ($val === null) { |
|
756
|
|
|
$val = '\0'; // apply escaped "null" if string is empty |
|
757
|
|
|
} else { |
|
758
|
|
|
// Escaping of filter meta characters |
|
759
|
|
|
$val = str_replace('\\', '\5c', $val); |
|
760
|
|
|
$val = str_replace('*', '\2a', $val); |
|
761
|
|
|
$val = str_replace('(', '\28', $val); |
|
762
|
|
|
$val = str_replace(')', '\29', $val); |
|
763
|
|
|
|
|
764
|
|
|
// ASCII < 32 escaping |
|
765
|
|
|
$val = self::asc2hex32($val); |
|
766
|
|
|
} |
|
767
|
|
|
|
|
768
|
|
|
$values[$key] = $val; |
|
769
|
|
|
} |
|
770
|
|
|
if ($singleValue) { |
|
771
|
|
|
return $values[0]; |
|
772
|
|
|
} |
|
773
|
|
|
return $values; |
|
774
|
|
|
} |
|
775
|
|
|
|
|
776
|
|
|
|
|
777
|
|
|
/** |
|
778
|
|
|
* Borrowed function from PEAR:LDAP. |
|
779
|
|
|
* |
|
780
|
|
|
* Converts all ASCII chars < 32 to "\HEX" |
|
781
|
|
|
* |
|
782
|
|
|
* @param string $string String to convert |
|
783
|
|
|
* |
|
784
|
|
|
* @static |
|
785
|
|
|
* @return string |
|
786
|
|
|
*/ |
|
787
|
|
|
public static function asc2hex32($string) |
|
788
|
|
|
{ |
|
789
|
|
|
for ($i = 0; $i < strlen($string); $i++) { |
|
790
|
|
|
$char = substr($string, $i, 1); |
|
791
|
|
|
if (ord($char) < 32) { |
|
792
|
|
|
$hex = dechex(ord($char)); |
|
793
|
|
|
if (strlen($hex) == 1) { |
|
794
|
|
|
$hex = '0'.$hex; |
|
795
|
|
|
} |
|
796
|
|
|
$string = str_replace($char, '\\'.$hex, $string); |
|
797
|
|
|
} |
|
798
|
|
|
} |
|
799
|
|
|
return $string; |
|
800
|
|
|
} |
|
801
|
|
|
|
|
802
|
|
|
|
|
803
|
|
|
/** |
|
804
|
|
|
* Convert SASL authz_id into a DN |
|
805
|
|
|
* |
|
806
|
|
|
* @param string $searchBase |
|
807
|
|
|
* @param array $searchAttributes |
|
808
|
|
|
* @param string $authz_id |
|
809
|
|
|
* @return string|null |
|
810
|
|
|
*/ |
|
811
|
|
|
private function authzidToDn($searchBase, $searchAttributes, $authz_id) |
|
812
|
|
|
{ |
|
813
|
|
|
if (preg_match("/^dn:/", $authz_id)) { |
|
814
|
|
|
return preg_replace("/^dn:/", "", $authz_id); |
|
815
|
|
|
} |
|
816
|
|
|
|
|
817
|
|
|
if (preg_match("/^u:/", $authz_id)) { |
|
818
|
|
|
return $this->searchfordn( |
|
819
|
|
|
$searchBase, |
|
820
|
|
|
$searchAttributes, |
|
821
|
|
|
preg_replace("/^u:/", "", $authz_id) |
|
822
|
|
|
); |
|
823
|
|
|
} |
|
824
|
|
|
return $authz_id; |
|
825
|
|
|
} |
|
826
|
|
|
|
|
827
|
|
|
|
|
828
|
|
|
/** |
|
829
|
|
|
* ldap_exop_whoami accessor, if available. Use requested authz_id |
|
830
|
|
|
* otherwise. |
|
831
|
|
|
* |
|
832
|
|
|
* ldap_exop_whoami() has been provided as a third party patch that |
|
833
|
|
|
* waited several years to get its way upstream: |
|
834
|
|
|
* http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/databases/php-ldap/files |
|
835
|
|
|
* |
|
836
|
|
|
* When it was integrated into PHP repository, the function prototype |
|
837
|
|
|
* was changed, The new prototype was used in third party patch for |
|
838
|
|
|
* PHP 7.0 and 7.1, hence the version test below. |
|
839
|
|
|
* |
|
840
|
|
|
* @param string $searchBase |
|
841
|
|
|
* @param array $searchAttributes |
|
842
|
|
|
* @throws \Exception |
|
843
|
|
|
* @return string |
|
844
|
|
|
*/ |
|
845
|
|
|
public function whoami($searchBase, $searchAttributes) |
|
846
|
|
|
{ |
|
847
|
|
|
$authz_id = ''; |
|
848
|
|
|
if (function_exists('ldap_exop_whoami')) { |
|
849
|
|
|
if (version_compare(phpversion(), '7', '<')) { |
|
850
|
|
|
/** @psalm-suppress TooManyArguments */ |
|
851
|
|
|
if (ldap_exop_whoami($this->ldap, $authz_id) === false) { |
|
852
|
|
|
throw $this->makeException('LDAP whoami exop failure'); |
|
853
|
|
|
} |
|
854
|
|
|
} else { |
|
855
|
|
|
/** @var string|false $authz_id */ |
|
856
|
|
|
$authz_id = ldap_exop_whoami($this->ldap); |
|
857
|
|
|
if ($authz_id === false) { |
|
858
|
|
|
throw $this->makeException('LDAP whoami exop failure'); |
|
859
|
|
|
} |
|
860
|
|
|
} |
|
861
|
|
|
} else { |
|
862
|
|
|
Assert::string($authz_id); |
|
863
|
|
|
/** @var string $authz_id */ |
|
864
|
|
|
$authz_id = $this->authz_id; |
|
865
|
|
|
} |
|
866
|
|
|
|
|
867
|
|
|
$dn = $this->authzidToDn($searchBase, $searchAttributes, $authz_id); |
|
868
|
|
|
|
|
869
|
|
|
if (empty($dn)) { |
|
870
|
|
|
throw $this->makeException('Cannot figure userID'); |
|
871
|
|
|
} |
|
872
|
|
|
|
|
873
|
|
|
return $dn; |
|
874
|
|
|
} |
|
875
|
|
|
|
|
876
|
|
|
|
|
877
|
|
|
/** |
|
878
|
|
|
* Set for a given DN attributes |
|
879
|
|
|
* |
|
880
|
|
|
* @param string $dn |
|
881
|
|
|
* The DN of an element. |
|
882
|
|
|
* @param array $attributes |
|
883
|
|
|
* The names and value of the attribute(s) to set using ldap_mod_replace structure; |
|
884
|
|
|
* @return bool |
|
885
|
|
|
* Result of operation |
|
886
|
|
|
*/ |
|
887
|
|
|
public function setAttributes($dn, array $attributes) { |
|
888
|
|
|
Logger::debug('Library - LDAP setAttributes(): Received arraydata:'.print_r($attributes, true)); |
|
889
|
|
|
|
|
890
|
|
|
// Attempt to set attributes |
|
891
|
|
|
$result = @ldap_mod_replace($this->ldap, $dn, $attributes); |
|
892
|
|
|
if ($result === false) { |
|
893
|
|
|
throw $this->makeException('Library - LDAP setAttributes(): Failed to set attributes for DN \''.$dn.'\'. Bind necessary?'); |
|
894
|
|
|
} |
|
895
|
|
|
|
|
896
|
|
|
return $result; |
|
897
|
|
|
} |
|
898
|
|
|
|
|
899
|
|
|
|
|
900
|
|
|
/** |
|
901
|
|
|
* Adds for a given DN attributes |
|
902
|
|
|
* |
|
903
|
|
|
* @param string $dn |
|
904
|
|
|
* The DN of an element. |
|
905
|
|
|
* @param array $attributes |
|
906
|
|
|
* The names and value of the attribute(s) to set using ldap_mod_add structure; |
|
907
|
|
|
* @return bool |
|
908
|
|
|
* Result of operation |
|
909
|
|
|
*/ |
|
910
|
|
|
public function addAttributes($dn, array $attributes) { |
|
911
|
|
|
Logger::debug('Library - LDAP addAttributes(): Received arraydata:'.print_r($attributes, true)); |
|
912
|
|
|
|
|
913
|
|
|
// Attempt to add attributes |
|
914
|
|
|
$result = @ldap_mod_add($this->ldap, $dn, $attributes); |
|
915
|
|
|
if ($result === false) { |
|
916
|
|
|
throw $this->makeException('Library - LDAP addAttributes(): Failed to set attributes for DN \''.$dn.'\'. Bind necessary?'); |
|
917
|
|
|
} |
|
918
|
|
|
|
|
919
|
|
|
return $result; |
|
920
|
|
|
} |
|
921
|
|
|
} |
|
922
|
|
|
|