1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* For licensing terms, see /license.txt */ |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* This files is included by newUser.ldap.php and login.ldap.php |
7
|
|
|
* It implements the functions nedded by both files. |
8
|
|
|
* */ |
9
|
|
|
require_once __DIR__.'/../../inc/global.inc.php'; |
10
|
|
|
|
11
|
|
|
$debug = false; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Returns a transcoded and trimmed string. |
15
|
|
|
* |
16
|
|
|
* @param string |
17
|
|
|
* |
18
|
|
|
* @return string |
19
|
|
|
* |
20
|
|
|
* @author ndiechburg <[email protected]> |
21
|
|
|
* */ |
22
|
|
|
function extldap_purify_string($string) |
23
|
|
|
{ |
24
|
|
|
global $extldap_config; |
25
|
|
|
if (isset($extldap_config['encoding'])) { |
26
|
|
|
return trim(api_to_system_encoding($string, $extldap_config['encoding'])); |
27
|
|
|
} else { |
28
|
|
|
return trim($string); |
29
|
|
|
} |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Establishes a connection to the LDAP server and sets the protocol version. |
34
|
|
|
* |
35
|
|
|
* @return resource|bool ldap link identifier or false |
36
|
|
|
* |
37
|
|
|
* @author ndiechburg <[email protected]> |
38
|
|
|
* */ |
39
|
|
|
function extldap_connect() |
40
|
|
|
{ |
41
|
|
|
global $extldap_config, $debug; |
42
|
|
|
|
43
|
|
|
if (!is_array($extldap_config['host'])) { |
44
|
|
|
$extldap_config['host'] = [$extldap_config['host']]; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
foreach ($extldap_config['host'] as $host) { |
48
|
|
|
//Trying to connect |
49
|
|
|
if (isset($extldap_config['port'])) { |
50
|
|
|
$ds = ldap_connect($host, $extldap_config['port']); |
51
|
|
|
} else { |
52
|
|
|
$ds = ldap_connect($host); |
53
|
|
|
} |
54
|
|
|
if (!$ds) { |
55
|
|
|
$port = isset($extldap_config['port']) ? $extldap_config['port'] : 389; |
56
|
|
|
if ($debug) { |
57
|
|
|
error_log( |
58
|
|
|
'EXTLDAP ERROR : cannot connect to '.$extldap_config['host'].':'.$port |
59
|
|
|
); |
60
|
|
|
} |
61
|
|
|
} else { |
62
|
|
|
break; |
63
|
|
|
} |
64
|
|
|
} |
65
|
|
|
if (!$ds) { |
|
|
|
|
66
|
|
|
if ($debug) { |
67
|
|
|
error_log('EXTLDAP ERROR : no valid server found'); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
return false; |
71
|
|
|
} |
72
|
|
|
// Setting protocol version |
73
|
|
|
if (isset($extldap_config['protocol_version'])) { |
74
|
|
|
if (!ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $extldap_config['protocol_version'])) { |
75
|
|
|
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 2); |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
// Setting protocol version |
80
|
|
|
if (isset($extldap_config['referrals'])) { |
81
|
|
|
if (!ldap_set_option($ds, LDAP_OPT_REFERRALS, $extldap_config['referrals'])) { |
82
|
|
|
ldap_set_option($ds, LDAP_OPT_REFERRALS, $extldap_config['referrals']); |
83
|
|
|
} |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
return $ds; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Authenticate user on external ldap server and return user ldap entry if that succeeds. |
91
|
|
|
* |
92
|
|
|
* @param string $password |
93
|
|
|
* |
94
|
|
|
* @return mixed false if user cannot authenticate on ldap, user ldap entry if tha succeeds |
95
|
|
|
* |
96
|
|
|
* @author ndiechburg <[email protected]> |
97
|
|
|
* Modified by [email protected] |
98
|
|
|
* Add possibility to get user info from LDAP without check password (if CAS auth and LDAP profil update) |
99
|
|
|
* |
100
|
|
|
* */ |
101
|
|
|
function extldap_authenticate($username, $password, $in_auth_with_no_password = false) |
102
|
|
|
{ |
103
|
|
|
global $extldap_config, $debug; |
104
|
|
|
|
105
|
|
|
if (empty($username) || empty($password)) { |
106
|
|
|
return false; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
$ds = extldap_connect(); |
110
|
|
|
if (!$ds) { |
111
|
|
|
return false; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
// Connection as admin to search dn of user |
115
|
|
|
$ldapbind = @ldap_bind($ds, $extldap_config['admin_dn'], $extldap_config['admin_password']); |
116
|
|
|
if ($ldapbind === false) { |
117
|
|
|
if ($debug) { |
118
|
|
|
error_log( |
119
|
|
|
'EXTLDAP ERROR : cannot connect with admin login/password' |
120
|
|
|
); |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
return false; |
124
|
|
|
} |
125
|
|
|
$user_search = extldap_get_user_search_string($username); |
126
|
|
|
// Search distinguish name of user |
127
|
|
|
$sr = ldap_search($ds, $extldap_config['base_dn'], $user_search); |
128
|
|
|
if (!$sr) { |
129
|
|
|
if ($debug) { |
130
|
|
|
error_log( |
131
|
|
|
'EXTLDAP ERROR : ldap_search('.$ds.', '.$extldap_config['base_dn'].", $user_search) failed" |
132
|
|
|
); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
return false; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
$entries_count = ldap_count_entries($ds, $sr); |
139
|
|
|
|
140
|
|
|
if ($entries_count > 1) { |
141
|
|
|
if ($debug) { |
142
|
|
|
error_log( |
143
|
|
|
'EXTLDAP ERROR : more than one entry for that user ( ldap_search(ds, '.$extldap_config['base_dn'].", $user_search) )" |
144
|
|
|
); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return false; |
148
|
|
|
} |
149
|
|
|
if ($entries_count < 1) { |
150
|
|
|
if ($debug) { |
151
|
|
|
error_log( |
152
|
|
|
'EXTLDAP ERROR : No entry for that user ( ldap_search(ds, '.$extldap_config['base_dn'].", $user_search) )" |
153
|
|
|
); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return false; |
157
|
|
|
} |
158
|
|
|
$users = ldap_get_entries($ds, $sr); |
159
|
|
|
$user = $users[0]; |
160
|
|
|
|
161
|
|
|
// If we just want to have user info from LDAP and not to check password |
162
|
|
|
if ($in_auth_with_no_password) { |
163
|
|
|
return $user; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// now we try to autenthicate the user in the ldap |
167
|
|
|
$ubind = @ldap_bind($ds, $user['dn'], $password); |
168
|
|
|
if ($ubind !== false) { |
169
|
|
|
return $user; |
170
|
|
|
} else { |
171
|
|
|
if ($debug) { |
172
|
|
|
error_log('EXTLDAP : Wrong password for '.$user['dn']); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
return false; |
176
|
|
|
} |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Return an array with userinfo compatible with chamilo using $extldap_user_correspondance |
181
|
|
|
* configuration array declared in ldap.conf.php file. |
182
|
|
|
* |
183
|
|
|
* @param array ldap user |
184
|
|
|
* @param array correspondance array (if not set use extldap_user_correspondance declared in auth.conf.php |
185
|
|
|
* |
186
|
|
|
* @return array userinfo array |
187
|
|
|
* |
188
|
|
|
* @author ndiechburg <[email protected]> |
189
|
|
|
* */ |
190
|
|
|
function extldap_get_chamilo_user($ldap_user, $cor = null) |
191
|
|
|
{ |
192
|
|
|
global $extldap_user_correspondance, $debug; |
193
|
|
|
if (is_null($cor)) { |
194
|
|
|
$cor = $extldap_user_correspondance; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
$chamilo_user = []; |
198
|
|
|
foreach ($cor as $chamilo_field => $ldap_field) { |
199
|
|
|
if (is_array($ldap_field)) { |
200
|
|
|
$chamilo_user[$chamilo_field] = extldap_get_chamilo_user($ldap_user, $ldap_field); |
201
|
|
|
continue; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
switch ($ldap_field) { |
205
|
|
|
case 'func': |
206
|
|
|
$func = "extldap_get_$chamilo_field"; |
207
|
|
|
if (function_exists($func)) { |
208
|
|
|
$chamilo_user[$chamilo_field] = extldap_purify_string($func($ldap_user)); |
209
|
|
|
} else { |
210
|
|
|
if ($debug) { |
211
|
|
|
error_log( |
212
|
|
|
"EXTLDAP WARNING : You forgot to declare $func" |
213
|
|
|
); |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
break; |
217
|
|
|
default: |
218
|
|
|
//if string begins with "!", then this is a constant |
219
|
|
|
if ($ldap_field[0] === '!') { |
220
|
|
|
$chamilo_user[$chamilo_field] = trim($ldap_field, "!\t\n\r\0"); |
221
|
|
|
break; |
222
|
|
|
} |
223
|
|
|
if (!array_key_exists($ldap_field, $ldap_user)) { |
224
|
|
|
$lowerCaseFieldName = strtolower($ldap_field); |
225
|
|
|
if (array_key_exists($lowerCaseFieldName, $ldap_user)) { |
226
|
|
|
$ldap_field = $lowerCaseFieldName; |
227
|
|
|
} |
228
|
|
|
} |
229
|
|
|
if (isset($ldap_user[$ldap_field][0])) { |
230
|
|
|
$chamilo_user[$chamilo_field] = extldap_purify_string($ldap_user[$ldap_field][0]); |
231
|
|
|
} else { |
232
|
|
|
if ($debug) { |
233
|
|
|
error_log( |
234
|
|
|
'EXTLDAP WARNING : '.$ldap_field.'[0] field is not set in ldap array' |
235
|
|
|
); |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
break; |
239
|
|
|
} |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
return $chamilo_user; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Please declare here all the function you use in extldap_user_correspondance |
247
|
|
|
* All these functions must have an $ldap_user parameter. This parameter is the |
248
|
|
|
* array returned by the ldap for the user. |
249
|
|
|
* */ |
250
|
|
|
function extldap_get_status($ldap_user) |
251
|
|
|
{ |
252
|
|
|
return STUDENT; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
function extldap_get_admin($ldap_user) |
256
|
|
|
{ |
257
|
|
|
return false; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* return the string used to search a user in ldap. |
262
|
|
|
* |
263
|
|
|
* @param string username |
264
|
|
|
* |
265
|
|
|
* @return string the serach string |
266
|
|
|
* |
267
|
|
|
* @author ndiechburg <[email protected]> |
268
|
|
|
* */ |
269
|
|
|
function extldap_get_user_search_string($username) |
270
|
|
|
{ |
271
|
|
|
global $extldap_config; |
272
|
|
|
// init |
273
|
|
|
$filter = '('.$extldap_config['user_search'].')'; |
274
|
|
|
// replacing %username% by the actual username |
275
|
|
|
$filter = str_replace('%username%', $username, $filter); |
276
|
|
|
// append a global filter if needed |
277
|
|
|
if (isset($extldap_config['filter']) && $extldap_config['filter'] != "") { |
278
|
|
|
$filter = '(&'.$filter.'('.$extldap_config['filter'].'))'; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
return $filter; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Imports all LDAP users into Chamilo. |
286
|
|
|
* |
287
|
|
|
* @return false|null false on error, true otherwise |
288
|
|
|
*/ |
289
|
|
|
function extldap_import_all_users() |
290
|
|
|
{ |
291
|
|
|
global $extldap_config, $debug; |
292
|
|
|
//echo "Connecting...\n"; |
293
|
|
|
$ds = extldap_connect(); |
294
|
|
|
if (!$ds) { |
295
|
|
|
return false; |
296
|
|
|
} |
297
|
|
|
//echo "Binding...\n"; |
298
|
|
|
$ldapbind = false; |
299
|
|
|
//Connection as admin to search dn of user |
300
|
|
|
$ldapbind = @ldap_bind($ds, $extldap_config['admin_dn'], $extldap_config['admin_password']); |
301
|
|
|
if ($ldapbind === false) { |
302
|
|
|
if ($debug) { |
303
|
|
|
error_log( |
304
|
|
|
'EXTLDAP ERROR : cannot connect with admin login/password' |
305
|
|
|
); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
return false; |
309
|
|
|
} |
310
|
|
|
//browse ASCII values from a to z to avoid 1000 results limit of LDAP |
311
|
|
|
$count = 0; |
312
|
|
|
$alphanum = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; |
313
|
|
|
for ($a = 97; $a <= 122; $a++) { |
314
|
|
|
$alphanum[] = chr($a); |
315
|
|
|
} |
316
|
|
|
foreach ($alphanum as $char1) { |
317
|
|
|
foreach ($alphanum as $char2) { |
318
|
|
|
$user_search = $extldap_config['user_search_import_all_users']; |
319
|
|
|
//Search distinguish name of user |
320
|
|
|
$sr = ldap_search($ds, $extldap_config['base_dn'], $user_search); |
321
|
|
|
if (!$sr) { |
322
|
|
|
if ($debug) { |
323
|
|
|
error_log( |
324
|
|
|
'EXTLDAP ERROR : ldap_search('.$ds.', '.$extldap_config['base_dn'].", $user_search) failed" |
325
|
|
|
); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
return false; |
329
|
|
|
} |
330
|
|
|
//echo "Getting entries\n"; |
331
|
|
|
$users = ldap_get_entries($ds, $sr); |
332
|
|
|
//echo "Entries: ".$users['count']."\n"; |
333
|
|
|
for ($key = 0; $key < $users['count']; $key++) { |
334
|
|
|
$user_id = extldap_add_user_by_array($users[$key], true); |
335
|
|
|
$count++; |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
//echo "Found $count users in total\n"; |
340
|
|
|
@ldap_close($ds); |
|
|
|
|
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Insert users from an array of user fields. |
345
|
|
|
*/ |
346
|
|
|
function extldap_add_user_by_array($data, $update_if_exists = true) |
347
|
|
|
{ |
348
|
|
|
global $extldap_user_correspondance; |
349
|
|
|
|
350
|
|
|
$lastname = api_convert_encoding($data[$extldap_user_correspondance['lastname']][0], api_get_system_encoding(), 'UTF-8'); |
351
|
|
|
$firstname = api_convert_encoding($data[$extldap_user_correspondance['firstname']][0], api_get_system_encoding(), 'UTF-8'); |
352
|
|
|
$email = $data[$extldap_user_correspondance['email']][0]; |
353
|
|
|
$username = $data[$extldap_user_correspondance['username']][0]; |
354
|
|
|
|
355
|
|
|
// TODO the password, if encrypted at the source, will be encrypted twice, which makes it useless. Try to fix that. |
356
|
|
|
$passwordKey = isset($extldap_user_correspondance['password']) ? $extldap_user_correspondance['password'] : 'userPassword'; |
357
|
|
|
$password = $data[$passwordKey][0]; |
358
|
|
|
|
359
|
|
|
// To ease management, we add the step-year (etape-annee) code |
360
|
|
|
//$official_code = $etape."-".$annee; |
361
|
|
|
$official_code = api_convert_encoding($data[$extldap_user_correspondance['official_code']][0], api_get_system_encoding(), 'UTF-8'); |
362
|
|
|
$auth_source = 'ldap'; |
363
|
|
|
|
364
|
|
|
// No expiration date for students (recover from LDAP's shadow expiry) |
365
|
|
|
$expiration_date = ''; |
366
|
|
|
$active = 1; |
367
|
|
|
$status = 5; |
368
|
|
|
$phone = ''; |
369
|
|
|
$picture_uri = ''; |
370
|
|
|
// Adding user |
371
|
|
|
$user_id = 0; |
372
|
|
|
if (UserManager::is_username_available($username)) { |
373
|
|
|
//echo "$username\n"; |
374
|
|
|
$user_id = UserManager::create_user( |
375
|
|
|
$firstname, |
376
|
|
|
$lastname, |
377
|
|
|
$status, |
378
|
|
|
$email, |
379
|
|
|
$username, |
380
|
|
|
$password, |
381
|
|
|
$official_code, |
382
|
|
|
api_get_setting('platformLanguage'), |
383
|
|
|
$phone, |
384
|
|
|
$picture_uri, |
385
|
|
|
$auth_source, |
386
|
|
|
$expiration_date, |
387
|
|
|
$active |
388
|
|
|
); |
389
|
|
|
} else { |
390
|
|
|
if ($update_if_exists) { |
391
|
|
|
$user = api_get_user_info($username); |
392
|
|
|
$user_id = $user['user_id']; |
393
|
|
|
//echo "$username\n"; |
394
|
|
|
UserManager::update_user( |
395
|
|
|
$user_id, |
396
|
|
|
$firstname, |
397
|
|
|
$lastname, |
398
|
|
|
$username, |
399
|
|
|
null, |
400
|
|
|
null, |
401
|
|
|
$email, |
402
|
|
|
$status, |
403
|
|
|
$official_code, |
404
|
|
|
$phone, |
405
|
|
|
$picture_uri, |
406
|
|
|
$expiration_date, |
407
|
|
|
$active |
408
|
|
|
); |
409
|
|
|
} |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
return $user_id; |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* Get one user's single attribute value. |
417
|
|
|
* User is identified by filter. |
418
|
|
|
* $extldap_config['filter'] is also applied in complement, if defined. |
419
|
|
|
* |
420
|
|
|
* @param $filter string LDAP entry filter, such as '(uid=10000)' |
421
|
|
|
* @param $attribute string name of the LDAP attribute to read the value from |
422
|
|
|
* |
423
|
|
|
* @throws Exception if more than one entries matched or on internal error |
424
|
|
|
* |
425
|
|
|
* @return string|bool the single matching user entry's single attribute value or false if not found |
426
|
|
|
*/ |
427
|
|
|
function extldapGetUserAttributeValue($filter, $attribute) |
428
|
|
|
{ |
429
|
|
|
global $extldap_config; |
430
|
|
|
|
431
|
|
|
if (array_key_exists('filter', $extldap_config) && !empty($extldap_config['filter'])) { |
432
|
|
|
$filter = '(&'.$filter.'('.$extldap_config['filter'].'))'; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
$ldap = extldap_connect(); |
436
|
|
|
if (false === $ldap) { |
437
|
|
|
throw new Exception(get_lang('LDAPConnectFailed')); |
438
|
|
|
} |
439
|
|
|
|
440
|
|
|
if (false === ldap_bind($ldap, $extldap_config['admin_dn'], $extldap_config['admin_password'])) { |
441
|
|
|
throw new Exception(get_lang('LDAPBindFailed')); |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
$searchResult = ldap_search($ldap, $extldap_config['base_dn'], $filter, [$attribute]); |
445
|
|
|
if (false === $searchResult) { |
446
|
|
|
throw new Exception(get_lang('LDAPSearchFailed')); |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
switch (ldap_count_entries($ldap, $searchResult)) { |
450
|
|
|
case 0: |
451
|
|
|
return false; |
452
|
|
|
case 1: |
453
|
|
|
$entry = ldap_first_entry($ldap, $searchResult); |
454
|
|
|
if (false === $entry) { |
455
|
|
|
throw new Exception(get_lang('LDAPFirstEntryFailed')); |
456
|
|
|
} |
457
|
|
|
$values = ldap_get_values($ldap, $entry, $attribute); |
458
|
|
|
if (false == $values) { |
459
|
|
|
throw new Exception(get_lang('LDAPGetValuesFailed')); |
460
|
|
|
} |
461
|
|
|
if ($values['count'] == 1) { |
462
|
|
|
return $values[0]; |
463
|
|
|
} |
464
|
|
|
throw new Exception(get_lang('MoreThanOneAttributeValueFound')); |
465
|
|
|
default: |
466
|
|
|
throw new Exception(get_lang('MoreThanOneUserMatched')); |
467
|
|
|
} |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* Get the username from the CAS-supplied user identifier. |
472
|
|
|
* |
473
|
|
|
* searches in attribute $extldap_user_correspondance['extra']['cas_user'] or 'uid' by default |
474
|
|
|
* reads value from attribute $extldap_user_correspondance['username'] or 'uid' by default |
475
|
|
|
* |
476
|
|
|
* @param $casUser string code returned from the CAS server to identify the user |
477
|
|
|
* |
478
|
|
|
* @throws Exception on error |
479
|
|
|
* |
480
|
|
|
* @return string|bool user login name, false if not found |
481
|
|
|
*/ |
482
|
|
|
function extldapCasUserLogin($casUser) |
483
|
|
|
{ |
484
|
|
|
global $extldap_user_correspondance; |
485
|
|
|
|
486
|
|
|
// which LDAP attribute is the cas user identifier stored in ? |
487
|
|
|
$attributeToFilterOn = 'uid'; |
488
|
|
|
if (is_array($extldap_user_correspondance) && array_key_exists('extra', $extldap_user_correspondance)) { |
489
|
|
|
$extra = $extldap_user_correspondance['extra']; |
490
|
|
|
if (is_array($extra) && array_key_exists('cas_user', $extra) && !empty($extra['cas_user'])) { |
491
|
|
|
$attributeToFilterOn = $extra['cas_user']; |
492
|
|
|
} |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
// which LDAP attribute is the username ? |
496
|
|
|
$attributeToRead = 'uid'; |
497
|
|
|
if (is_array($extldap_user_correspondance) |
498
|
|
|
&& array_key_exists('username', $extldap_user_correspondance) |
499
|
|
|
&& !empty($extldap_user_correspondance['username']) |
500
|
|
|
) { |
501
|
|
|
$attributeToRead = $extldap_user_correspondance['username']; |
502
|
|
|
} |
503
|
|
|
|
504
|
|
|
// return the value |
505
|
|
|
return extldapGetUserAttributeValue("($attributeToFilterOn=$casUser)", $attributeToRead); |
506
|
|
|
} |
507
|
|
|
|