1 | <?php |
||||||
2 | /** |
||||||
3 | * API - accounts LDAP backend |
||||||
4 | * |
||||||
5 | * @link http://www.egroupware.org |
||||||
6 | * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> complete rewrite in 6/2006 |
||||||
7 | * |
||||||
8 | * This class replaces the former accounts_ldap class written by |
||||||
9 | * Joseph Engo <[email protected]>, Lars Kneschke <[email protected]>, |
||||||
10 | * Miles Lott <[email protected]> and Bettina Gille <[email protected]>. |
||||||
11 | * Copyright (C) 2000 - 2002 Joseph Engo, Lars Kneschke |
||||||
12 | * Copyright (C) 2003 Lars Kneschke, Bettina Gille |
||||||
13 | * |
||||||
14 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||||
15 | * @package api |
||||||
16 | * @subpackage accounts |
||||||
17 | */ |
||||||
18 | |||||||
19 | namespace EGroupware\Api\Accounts; |
||||||
20 | |||||||
21 | use EGroupware\Api; |
||||||
22 | |||||||
23 | // explicitly reference classes still in phpgwapi or old structure |
||||||
24 | use setup_cmd_ldap; |
||||||
25 | |||||||
26 | /** |
||||||
27 | * LDAP Backend for accounts |
||||||
28 | * |
||||||
29 | * The LDAP backend of the accounts class now stores accounts, groups and the memberships completly in LDAP. |
||||||
30 | * It does NO longer use the ACL class/table for group membership information. |
||||||
31 | * Nor does it use the phpgwAcounts schema (part of that information is stored via shadowAccount now). |
||||||
32 | * |
||||||
33 | * A user is recogniced by eGW, if he's in the user_context tree AND has the posixAccount object class AND |
||||||
34 | * matches the LDAP search filter specified in setup >> configuration. |
||||||
35 | * A group is recogniced by eGW, if it's in the group_context tree AND has the posixGroup object class. |
||||||
36 | * The group members are stored as memberuid's. |
||||||
37 | * |
||||||
38 | * The (positive) group-id's (gidnumber) of LDAP groups are mapped in this class to negative numeric |
||||||
39 | * account_id's to not conflict with the user-id's, as both share in eGW internaly the same numberspace! |
||||||
40 | * |
||||||
41 | * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> |
||||||
42 | * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
||||||
43 | * @access internal only use the interface provided by the accounts class |
||||||
44 | */ |
||||||
45 | class Ldap |
||||||
46 | { |
||||||
47 | /** |
||||||
48 | * Name of mail attribute |
||||||
49 | */ |
||||||
50 | const MAIL_ATTR = 'mail'; |
||||||
51 | /** |
||||||
52 | * resource with connection to the ldap server |
||||||
53 | * |
||||||
54 | * @var resource |
||||||
55 | */ |
||||||
56 | var $ds; |
||||||
57 | /** |
||||||
58 | * LDAP context for users, eg. ou=account,dc=domain,dc=com |
||||||
59 | * |
||||||
60 | * @var string |
||||||
61 | */ |
||||||
62 | var $user_context; |
||||||
63 | /** |
||||||
64 | * LDAP search filter for user accounts, eg. (uid=%user) |
||||||
65 | * |
||||||
66 | * @var string |
||||||
67 | */ |
||||||
68 | var $account_filter; |
||||||
69 | /** |
||||||
70 | * LDAP context for groups, eg. ou=groups,dc=domain,dc=com |
||||||
71 | * |
||||||
72 | * @var string |
||||||
73 | */ |
||||||
74 | var $group_context; |
||||||
75 | /** |
||||||
76 | * total number of found entries from get_list method |
||||||
77 | * |
||||||
78 | * @var int |
||||||
79 | */ |
||||||
80 | var $total; |
||||||
81 | |||||||
82 | var $ldapServerInfo; |
||||||
83 | |||||||
84 | /** |
||||||
85 | * required classe for user and groups |
||||||
86 | * |
||||||
87 | * @var array |
||||||
88 | */ |
||||||
89 | var $requiredObjectClasses = array( |
||||||
90 | 'user' => array( |
||||||
91 | 'top','person','organizationalperson','inetorgperson','posixaccount','shadowaccount' |
||||||
92 | ), |
||||||
93 | 'user-if-supported' => array( // these classes get added, if server supports them |
||||||
94 | 'mozillaabpersonalpha', 'mozillaorgperson', 'evolutionperson', |
||||||
95 | 'univentionperson', 'univentionmail', array('univentionobject', 'univentionObjectType' => 'users/user'), |
||||||
96 | ), |
||||||
97 | 'group' => array( |
||||||
98 | 'top','posixgroup','groupofnames' |
||||||
99 | ), |
||||||
100 | 'group-if-supported' => array( // these classes get added, if servers supports them |
||||||
101 | 'univentiongroup', array('univentionobject', 'univentionObjectType' => 'groups/group'), |
||||||
102 | ) |
||||||
103 | ); |
||||||
104 | /** |
||||||
105 | * Classes allowing to set a mail-address for a group and specify the memberaddresses as forwarding addresses |
||||||
106 | * |
||||||
107 | * $objectclass => $forward |
||||||
108 | * $objectclass => [$forward, $extra_attr, $keep_objectclass] |
||||||
109 | * $forward : name of attribute to set forwards for members mail addresses, false if not used/required |
||||||
110 | * $extra_attr : required attribute (eg. 'uid'), which need to be set, default none |
||||||
111 | * $keep_objectclass : true to not remove objectclass, if not mail set |
||||||
112 | * |
||||||
113 | * @var array |
||||||
114 | */ |
||||||
115 | var $group_mail_classes = array( |
||||||
116 | 'dbmailforwardingaddress' => 'mailforwardingaddress', |
||||||
117 | 'dbmailuser' => array('mailforwardingaddress','uid'), |
||||||
118 | 'qmailuser' => array('mailforwardingaddress','uid'), |
||||||
119 | 'mailaccount' => 'mailalias', |
||||||
120 | 'univentiongroup' => array(false, false, true), |
||||||
121 | ); |
||||||
122 | |||||||
123 | /** |
||||||
124 | * Reference to our frontend |
||||||
125 | * |
||||||
126 | * @var Api\Accounts |
||||||
127 | */ |
||||||
128 | protected $frontend; |
||||||
129 | |||||||
130 | /** |
||||||
131 | * Instance of the ldap class |
||||||
132 | * |
||||||
133 | * @var Api\Ldap |
||||||
134 | */ |
||||||
135 | private $ldap; |
||||||
136 | |||||||
137 | /** |
||||||
138 | * does backend allow to change account_lid |
||||||
139 | */ |
||||||
140 | const CHANGE_ACCOUNT_LID = true; |
||||||
141 | |||||||
142 | /** |
||||||
143 | * does backend requires password to be set, before allowing to enable an account |
||||||
144 | */ |
||||||
145 | const REQUIRE_PASSWORD_FOR_ENABLE = false; |
||||||
146 | |||||||
147 | /** |
||||||
148 | * Constructor |
||||||
149 | * |
||||||
150 | * @param Api\Accounts $frontend reference to the frontend class, to be able to call it's methods if needed |
||||||
151 | */ |
||||||
152 | function __construct(Api\Accounts $frontend) |
||||||
153 | { |
||||||
154 | $this->frontend = $frontend; |
||||||
155 | |||||||
156 | // enable the caching in the session, done by the accounts class extending this class. |
||||||
157 | $this->use_session_cache = true; |
||||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||||||
158 | |||||||
159 | $this->ldap = Api\Ldap::factory(false, $this->frontend->config['ldap_host'], |
||||||
0 ignored issues
–
show
It seems like
EGroupware\Api\Ldap::fac...config['ldap_root_pw']) can also be of type resource . However, the property $ldap is declared as type EGroupware\Api\Ldap . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||||||
160 | $this->frontend->config['ldap_root_dn'],$this->frontend->config['ldap_root_pw']); |
||||||
161 | $this->ds = $this->ldap->ds; |
||||||
162 | |||||||
163 | $this->user_context = $this->frontend->config['ldap_context']; |
||||||
164 | $this->account_filter = $this->frontend->config['ldap_search_filter']; |
||||||
165 | $this->group_context = $this->frontend->config['ldap_group_context'] ? |
||||||
166 | $this->frontend->config['ldap_group_context'] : $this->frontend->config['ldap_context']; |
||||||
167 | } |
||||||
168 | |||||||
169 | /** |
||||||
170 | * Reads the data of one account |
||||||
171 | * |
||||||
172 | * @param int $account_id numeric account-id |
||||||
173 | * @return array|boolean array with account data (keys: account_id, account_lid, ...) or false if account not found |
||||||
174 | */ |
||||||
175 | function read($account_id) |
||||||
176 | { |
||||||
177 | if (!(int)$account_id) return false; |
||||||
178 | |||||||
179 | if ($account_id < 0) |
||||||
180 | { |
||||||
181 | return $this->_read_group($account_id); |
||||||
182 | } |
||||||
183 | return $this->_read_user($account_id); |
||||||
184 | } |
||||||
185 | |||||||
186 | /** |
||||||
187 | * Saves / adds the data of one account |
||||||
188 | * |
||||||
189 | * If no account_id is set in data the account is added and the new id is set in $data. |
||||||
190 | * |
||||||
191 | * @param array $data array with account-data |
||||||
192 | * @return int|boolean the account_id or false on error |
||||||
193 | */ |
||||||
194 | function save(&$data) |
||||||
195 | { |
||||||
196 | $is_group = $data['account_id'] < 0 || $data['account_type'] === 'g'; |
||||||
197 | |||||||
198 | $data_utf8 = Api\Translation::convert($data,Api\Translation::charset(),'utf-8'); |
||||||
199 | $members = $data['account_members']; |
||||||
200 | |||||||
201 | if (!is_object($this->ldapServerInfo)) |
||||||
202 | { |
||||||
203 | $this->ldapServerInfo = $this->ldap->getLDAPServerInfo($this->frontend->config['ldap_host']); |
||||||
0 ignored issues
–
show
The call to
EGroupware\Api\Ldap::getLDAPServerInfo() has too many arguments starting with $this->frontend->config['ldap_host'] .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
204 | } |
||||||
205 | // common code for users and groups |
||||||
206 | // checks if accout_lid (dn) has been changed or required objectclass'es are missing |
||||||
207 | if ($data_utf8['account_id'] && $data_utf8['account_lid']) |
||||||
208 | { |
||||||
209 | // read the entry first, to check if the dn (account_lid) has changed |
||||||
210 | $sri = $is_group ? ldap_search($this->ds,$this->group_context,'gidnumber='.abs($data['account_id'])) : |
||||||
211 | ldap_search($this->ds,$this->user_context,'uidnumber='.$data['account_id']); |
||||||
212 | $old = ldap_get_entries($this->ds, $sri); |
||||||
213 | |||||||
214 | if (!$old['count']) |
||||||
215 | { |
||||||
216 | unset($old); |
||||||
217 | } |
||||||
218 | else |
||||||
219 | { |
||||||
220 | $old = Api\Ldap::result2array($old[0]); |
||||||
221 | $old['objectclass'] = array_map('strtolower', $old['objectclass']); |
||||||
222 | $key = false; |
||||||
223 | if ($is_group && ($key = array_search('namedobject',$old['objectclass'])) !== false || |
||||||
224 | $is_group && ($old['cn'] != $data_utf8['account_lid'] || substr($old['dn'],0,3) != 'cn=') || |
||||||
225 | !$is_group && ($old['uid'] != $data_utf8['account_lid'] || substr($old['dn'],0,4) != 'uid=')) |
||||||
226 | { |
||||||
227 | // query the memberships to set them again later |
||||||
228 | if (!$is_group) |
||||||
229 | { |
||||||
230 | $memberships = $this->memberships($data['account_id']); |
||||||
231 | } |
||||||
232 | else |
||||||
233 | { |
||||||
234 | $members = $old ? $old['memberuid'] : $this->members($data['account_id']); |
||||||
235 | } |
||||||
236 | // if dn has changed --> delete the old entry, as we cant rename the dn |
||||||
237 | $this->delete($data['account_id']); |
||||||
238 | unset($old['dn']); |
||||||
239 | // removing the namedObject object-class, if it's included |
||||||
240 | if ($key !== false) unset($old['objectclass'][$key]); |
||||||
0 ignored issues
–
show
|
|||||||
241 | $to_write = $old; |
||||||
242 | unset($old); |
||||||
243 | } |
||||||
244 | } |
||||||
245 | } |
||||||
246 | if (!$data['account_id']) // new account |
||||||
247 | { |
||||||
248 | if (!($data['account_id'] = $data_utf8['account_id'] = $this->_get_nextid($is_group ? 'g' : 'u'))) |
||||||
249 | { |
||||||
250 | return false; |
||||||
251 | } |
||||||
252 | } |
||||||
253 | // check if we need to write the objectclass: new entry or required object classes are missing |
||||||
254 | if (!$old || array_diff($this->requiredObjectClasses[$is_group ? 'group' : 'user'],$old['objectclass'])) |
||||||
255 | { |
||||||
256 | // additional objectclasse might be already set in $to_write or $old |
||||||
257 | if (!is_array($to_write['objectclass'])) |
||||||
258 | { |
||||||
259 | $to_write['objectclass'] = $old ? $old['objectclass'] : array(); |
||||||
260 | } |
||||||
261 | if (!$old) // for new accounts add additional addressbook object classes, if supported by server |
||||||
262 | { // as setting them later might loose eg. password, if we are not allowed to read them |
||||||
263 | foreach($this->requiredObjectClasses[$is_group?'group-if-supported':'user-if-supported'] as $additional) |
||||||
264 | { |
||||||
265 | $add = array(); |
||||||
266 | if (is_array($additional)) |
||||||
267 | { |
||||||
268 | $add = $additional; |
||||||
269 | $additional = array_shift($add); |
||||||
270 | } |
||||||
271 | if ($this->ldapServerInfo->supportsObjectClass($additional)) |
||||||
272 | { |
||||||
273 | $to_write['objectclass'][] = $additional; |
||||||
274 | if ($add) $to_write += $add; |
||||||
275 | } |
||||||
276 | } |
||||||
277 | } |
||||||
278 | $to_write['objectclass'] = array_values(array_unique(array_merge($to_write['objectclass'], |
||||||
279 | $this->requiredObjectClasses[$is_group ? 'group' : 'user']))); |
||||||
280 | } |
||||||
281 | if (!($dn = $old['dn'])) |
||||||
282 | { |
||||||
283 | if (!$data['account_lid']) return false; |
||||||
284 | |||||||
285 | $dn = $is_group ? 'cn='.$data_utf8['account_lid'].','.$this->group_context : |
||||||
286 | 'uid='.$data_utf8['account_lid'].','.$this->user_context; |
||||||
287 | } |
||||||
288 | // now we merge the user or group data |
||||||
289 | if ($is_group) |
||||||
290 | { |
||||||
291 | $to_write = $this->_merge_group($to_write, $data_utf8, $old); |
||||||
292 | $data['account_type'] = 'g'; |
||||||
293 | |||||||
294 | $objectclass = $old ? $old['objectclass'] : $to_write['objectclass']; |
||||||
295 | if ($members || !$old && array_intersect(array('groupofnames','groupofuniquenames','univentiongroup'), $objectclass)) |
||||||
296 | { |
||||||
297 | $to_write = array_merge($to_write, $this->set_members($members, $data['account_id'], $objectclass, $dn)); |
||||||
298 | } |
||||||
299 | // check if we should set a mail address and forwards for each member |
||||||
300 | foreach($this->group_mail_classes as $objectclass => $forward) |
||||||
301 | { |
||||||
302 | $extra_attr = false; |
||||||
303 | $keep_objectclass = false; |
||||||
304 | if (is_array($forward)) list($forward,$extra_attr,$keep_objectclass) = $forward; |
||||||
305 | |||||||
306 | if ($this->ldapServerInfo->supportsObjectClass($objectclass) && |
||||||
307 | ($old && in_array($objectclass,$old['objectclass']) || $data_utf8['account_email'] || $old[static::MAIL_ATTR])) |
||||||
308 | { |
||||||
309 | if ($data_utf8['account_email']) // setting an email |
||||||
310 | { |
||||||
311 | if (!in_array($objectclass,$old ? $old['objectclass'] : $to_write['objectclass'])) |
||||||
312 | { |
||||||
313 | if ($old) $to_write['objectclass'] = $old['objectclass']; |
||||||
314 | $to_write['objectclass'][] = $objectclass; |
||||||
315 | } |
||||||
316 | if ($extra_attr) $to_write[$extra_attr] = $data_utf8['account_lid']; |
||||||
317 | $to_write[static::MAIL_ATTR] = $data_utf8['account_email']; |
||||||
318 | |||||||
319 | if ($forward) |
||||||
320 | { |
||||||
321 | if (!$members) $members = $this->members($data['account_id']); |
||||||
322 | $to_write[$forward] = array(); |
||||||
323 | foreach (array_keys($members) as $member) |
||||||
324 | { |
||||||
325 | if (($email = $this->id2name($member,'account_email'))) |
||||||
326 | { |
||||||
327 | $to_write[$forward][] = $email; |
||||||
328 | } |
||||||
329 | } |
||||||
330 | } |
||||||
331 | } |
||||||
332 | elseif($old) // remove the mail and forwards only for existing entries |
||||||
333 | { |
||||||
334 | $to_write[static::MAIL_ATTR] = array(); |
||||||
335 | if ($forward) $to_write[$forward] = array(); |
||||||
336 | if ($extra_attr) $to_write[$extra_attr] = array(); |
||||||
337 | if (!$keep_objectclass && ($key = array_search($objectclass,$old['objectclass']))) |
||||||
338 | { |
||||||
339 | $to_write['objectclass'] = $old['objectclass']; |
||||||
340 | unset($to_write['objectclass'][$key]); |
||||||
341 | $to_write['objectclass'] = array_values($to_write['objectclass']); |
||||||
342 | } |
||||||
343 | } |
||||||
344 | break; |
||||||
345 | } |
||||||
346 | } |
||||||
347 | |||||||
348 | } |
||||||
349 | else |
||||||
350 | { |
||||||
351 | $to_write = $this->_merge_user($to_write,$data_utf8,!$old); |
||||||
352 | // make sure multiple email-addresses in the mail attribute "survive" |
||||||
353 | if (isset($to_write[static::MAIL_ATTR]) && is_array($old[static::MAIL_ATTR]) && count($old[static::MAIL_ATTR]) > 1) |
||||||
354 | { |
||||||
355 | $mail = $old[static::MAIL_ATTR]; |
||||||
356 | $mail[0] = $to_write[static::MAIL_ATTR]; |
||||||
357 | $to_write[static::MAIL_ATTR] = array_values(array_unique($mail)); |
||||||
358 | } |
||||||
359 | $data['account_type'] = 'u'; |
||||||
360 | |||||||
361 | // Check if an account already exists as system user, and if it does deny creation |
||||||
362 | if (!$GLOBALS['egw_info']['server']['ldap_allow_systemusernames'] && !$old && |
||||||
363 | function_exists('posix_getpwnam') && posix_getpwnam($data['account_lid'])) |
||||||
364 | { |
||||||
365 | throw new Api\Exception\WrongUserinput(lang('There already is a system-user with this name. User\'s should not have the same name as a systemuser')); |
||||||
366 | } |
||||||
367 | } |
||||||
368 | |||||||
369 | // remove memberuid when adding a group |
||||||
370 | if(!$old && is_array($to_write['memberuid']) && empty($to_write['memberuid'])) { |
||||||
371 | unset($to_write['memberuid']); |
||||||
372 | } |
||||||
373 | // modifying or adding the entry |
||||||
374 | if ($old && !@ldap_modify($this->ds,$dn,$to_write) || |
||||||
0 ignored issues
–
show
|
|||||||
375 | !$old && !@ldap_add($this->ds,$dn,$to_write)) |
||||||
376 | { |
||||||
377 | $err = true; |
||||||
378 | if ($is_group && ($key = array_search('groupofnames',$to_write['objectclass'])) !== false) |
||||||
379 | { |
||||||
380 | // try again with removed groupOfNames stuff, as I cant detect if posixGroup is a structural object |
||||||
381 | unset($to_write['objectclass'][$key]); |
||||||
382 | $to_write['objectclass'] = array_values($to_write['objectclass']); |
||||||
383 | unset($to_write['member']); |
||||||
384 | $err = $old ? !ldap_modify($this->ds,$dn,$to_write) : !ldap_add($this->ds,$dn,$to_write); |
||||||
385 | } |
||||||
386 | if ($err) |
||||||
387 | { |
||||||
388 | error_log(__METHOD__."() ldap_".($old ? 'modify' : 'add')."(,'$dn',".array2string($to_write).") --> ldap_error()=".ldap_error($this->ds)); |
||||||
389 | return false; |
||||||
390 | } |
||||||
391 | } |
||||||
392 | if ($memberships) |
||||||
393 | { |
||||||
394 | $this->set_memberships($memberships,$data['account_id']); |
||||||
395 | } |
||||||
396 | return $data['account_id']; |
||||||
397 | } |
||||||
398 | |||||||
399 | /** |
||||||
400 | * Delete one account, deletes also all acl-entries for that account |
||||||
401 | * |
||||||
402 | * @param int $account_id numeric account_id |
||||||
403 | * @return boolean true on success, false otherwise |
||||||
404 | */ |
||||||
405 | function delete($account_id) |
||||||
406 | { |
||||||
407 | if (!(int)$account_id) return false; |
||||||
408 | |||||||
409 | if ($account_id < 0) |
||||||
410 | { |
||||||
411 | $sri = ldap_search($this->ds, $this->group_context, 'gidnumber=' . abs($account_id)); |
||||||
412 | } |
||||||
413 | else |
||||||
414 | { |
||||||
415 | // remove the user's memberships |
||||||
416 | $this->set_memberships(array(),$account_id); |
||||||
417 | |||||||
418 | $sri = ldap_search($this->ds, $this->user_context, 'uidnumber=' . $account_id); |
||||||
419 | } |
||||||
420 | if (!$sri) return false; |
||||||
0 ignored issues
–
show
|
|||||||
421 | |||||||
422 | $allValues = ldap_get_entries($this->ds, $sri); |
||||||
423 | if (!$allValues['count']) return false; |
||||||
424 | |||||||
425 | return ldap_delete($this->ds, $allValues[0]['dn']); |
||||||
426 | } |
||||||
427 | |||||||
428 | /** |
||||||
429 | * Reads the data of one group |
||||||
430 | * |
||||||
431 | * @internal |
||||||
432 | * @param int $account_id numeric account-id (< 0 as it's for a group) |
||||||
433 | * @return array|boolean array with account data (keys: account_id, account_lid, ...) or false if account not found |
||||||
434 | */ |
||||||
435 | protected function _read_group($account_id) |
||||||
436 | { |
||||||
437 | $group = array(); |
||||||
438 | if (!is_object($this->ldapServerInfo)) |
||||||
439 | { |
||||||
440 | $this->ldapServerInfo = $this->ldap->getLDAPServerInfo($this->frontend->config['ldap_host']); |
||||||
0 ignored issues
–
show
The call to
EGroupware\Api\Ldap::getLDAPServerInfo() has too many arguments starting with $this->frontend->config['ldap_host'] .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||||
441 | } |
||||||
442 | foreach(array_keys($this->group_mail_classes) as $objectclass) |
||||||
443 | { |
||||||
444 | if ($this->ldapServerInfo->supportsObjectClass($objectclass)) |
||||||
445 | { |
||||||
446 | $group['mailAllowed'] = $objectclass; |
||||||
447 | break; |
||||||
448 | } |
||||||
449 | } |
||||||
450 | $sri = ldap_search($this->ds, $this->group_context,'(&(objectClass=posixGroup)(gidnumber=' . abs($account_id).'))', |
||||||
451 | array('dn', 'gidnumber', 'cn', 'objectclass', static::MAIL_ATTR, 'memberuid', 'description')); |
||||||
452 | |||||||
453 | $ldap_data = ldap_get_entries($this->ds, $sri); |
||||||
454 | if (!$ldap_data['count']) |
||||||
455 | { |
||||||
456 | return false; // group not found |
||||||
457 | } |
||||||
458 | $data = Api\Translation::convert($ldap_data[0],'utf-8'); |
||||||
459 | unset($data['objectclass']['count']); |
||||||
460 | |||||||
461 | $group += array( |
||||||
462 | 'account_dn' => $data['dn'], |
||||||
463 | 'account_id' => -$data['gidnumber'][0], |
||||||
464 | 'account_lid' => $data['cn'][0], |
||||||
465 | 'account_type' => 'g', |
||||||
466 | 'account_firstname' => $data['cn'][0], |
||||||
467 | 'account_lastname' => lang('Group'), |
||||||
468 | 'account_fullname' => lang('Group').' '.$data['cn'][0], |
||||||
469 | 'objectclass' => array_map('strtolower', $data['objectclass']), |
||||||
470 | 'account_email' => $data[static::MAIL_ATTR][0], |
||||||
471 | 'members' => array(), |
||||||
472 | 'account_description' => $data['description'][0], |
||||||
473 | ); |
||||||
474 | |||||||
475 | if (isset($data['memberuid'])) |
||||||
476 | { |
||||||
477 | unset($data['memberuid']['count']); |
||||||
478 | |||||||
479 | foreach($data['memberuid'] as $lid) |
||||||
480 | { |
||||||
481 | if (($id = $this->name2id($lid, 'account_lid', 'u'))) |
||||||
482 | { |
||||||
483 | $group['members'][$id] = $lid; |
||||||
484 | } |
||||||
485 | } |
||||||
486 | } |
||||||
487 | |||||||
488 | return $group; |
||||||
489 | } |
||||||
490 | |||||||
491 | /** |
||||||
492 | * Reads the data of one user |
||||||
493 | * |
||||||
494 | * @internal |
||||||
495 | * @param int $account_id numeric account-id |
||||||
496 | * @return array|boolean array with account data (keys: account_id, account_lid, ...) or false if account not found |
||||||
497 | */ |
||||||
498 | protected function _read_user($account_id) |
||||||
499 | { |
||||||
500 | $sri = ldap_search($this->ds, $this->user_context, '(&(objectclass=posixAccount)(uidnumber=' . (int)$account_id.'))', |
||||||
501 | array('dn','uidnumber','uid','gidnumber','givenname','sn','cn',static::MAIL_ATTR,'userpassword','telephonenumber', |
||||||
502 | 'shadowexpire','shadowlastchange','homedirectory','loginshell','createtimestamp','modifytimestamp')); |
||||||
503 | |||||||
504 | $ldap_data = ldap_get_entries($this->ds, $sri); |
||||||
505 | if (!$ldap_data['count']) |
||||||
506 | { |
||||||
507 | return false; // user not found |
||||||
508 | } |
||||||
509 | $data = Api\Translation::convert($ldap_data[0],'utf-8'); |
||||||
510 | |||||||
511 | $utc_diff = date('Z'); |
||||||
512 | $user = array( |
||||||
513 | 'account_dn' => $data['dn'], |
||||||
514 | 'account_id' => (int)$data['uidnumber'][0], |
||||||
515 | 'account_lid' => $data['uid'][0], |
||||||
516 | 'account_type' => 'u', |
||||||
517 | 'account_primary_group' => -$data['gidnumber'][0], |
||||||
518 | 'account_firstname' => $data['givenname'][0], |
||||||
519 | 'account_lastname' => $data['sn'][0], |
||||||
520 | 'account_email' => $data[static::MAIL_ATTR][0], |
||||||
521 | 'account_fullname' => $data['cn'][0], |
||||||
522 | 'account_pwd' => $data['userpassword'][0], |
||||||
523 | 'account_phone' => $data['telephonenumber'][0], |
||||||
524 | // both status and expires are encoded in the single shadowexpire value in LDAP |
||||||
525 | // - if it's unset an account is enabled AND does never expire |
||||||
526 | // - if it's set to 0, the account is disabled |
||||||
527 | // - if it's set to > 0, it will or already has expired --> acount is active if it not yet expired |
||||||
528 | // shadowexpire is in days since 1970/01/01 (equivalent to a timestamp (int UTC!) / (24*60*60) |
||||||
529 | 'account_status' => isset($data['shadowexpire']) && $data['shadowexpire'][0]*24*3600+$utc_diff < time() ? false : 'A', |
||||||
530 | 'account_expires' => isset($data['shadowexpire']) && $data['shadowexpire'][0] ? $data['shadowexpire'][0]*24*3600+$utc_diff : -1, // LDAP date is in UTC |
||||||
531 | 'account_lastpwd_change' => isset($data['shadowlastchange']) ? $data['shadowlastchange'][0]*24*3600+($data['shadowlastchange'][0]!=0?$utc_diff:0) : null, |
||||||
532 | // lastlogin and lastlogin from are not availible via the shadowAccount object class |
||||||
533 | // 'account_lastlogin' => $data['phpgwaccountlastlogin'][0], |
||||||
534 | // 'account_lastloginfrom' => $data['phpgwaccountlastloginfrom'][0], |
||||||
535 | 'person_id' => $data['uid'][0], // id of associated contact |
||||||
536 | 'account_created' => isset($data['createtimestamp'][0]) ? self::accounts_ldap2ts($data['createtimestamp'][0]) : null, |
||||||
537 | 'account_modified' => isset($data['modifytimestamp'][0]) ? self::accounts_ldap2ts($data['modifytimestamp'][0]) : null, |
||||||
538 | ); |
||||||
539 | |||||||
540 | if ($this->frontend->config['ldap_extra_attributes']) |
||||||
541 | { |
||||||
542 | $user['homedirectory'] = $data['homedirectory'][0]; |
||||||
543 | $user['loginshell'] = $data['loginshell'][0]; |
||||||
544 | } |
||||||
545 | return $user; |
||||||
546 | } |
||||||
547 | |||||||
548 | /** |
||||||
549 | * Merges the group releavant account data from $data into $to_write |
||||||
550 | * |
||||||
551 | * @internal |
||||||
552 | * @param array $to_write data to write to ldap incl. objectclass ($data is NOT yet merged) |
||||||
553 | * @param array $data array with account-data in utf-8 |
||||||
554 | * @return array merged data |
||||||
555 | */ |
||||||
556 | protected function _merge_group($to_write,$data,$old=null) |
||||||
557 | { |
||||||
558 | $to_write['gidnumber'] = abs($data['account_id']); |
||||||
559 | $to_write['cn'] = $data['account_lid']; |
||||||
560 | // do not overwrite exitsting description, if non is given |
||||||
561 | if (isset($data['account_description'])) |
||||||
562 | { |
||||||
563 | $to_write['description'] = !empty($data['account_description']) ? $data['account_description'] : array(); |
||||||
564 | } |
||||||
565 | // to kope with various dependencies / requirements of objectclasses, simply write everything again |
||||||
566 | foreach($old as $name => $value) |
||||||
567 | { |
||||||
568 | if (!isset($to_write[$name]) && !in_array($name, ['dn', 'objectclass'])) |
||||||
569 | { |
||||||
570 | $to_write[$name] = $value; |
||||||
571 | } |
||||||
572 | } |
||||||
573 | return $to_write; |
||||||
574 | } |
||||||
575 | |||||||
576 | /** |
||||||
577 | * Merges the user releavant account data from $data into $to_write |
||||||
578 | * |
||||||
579 | * @internal |
||||||
580 | * @param array $to_write data to write to ldap incl. objectclass ($data is NOT yet merged) |
||||||
581 | * @param array $data array with account-data in utf-8 |
||||||
582 | * @param boolean $new_entry |
||||||
583 | * @return array merged data |
||||||
584 | */ |
||||||
585 | protected function _merge_user($to_write,$data,$new_entry) |
||||||
586 | { |
||||||
587 | $to_write['uidnumber'] = $data['account_id']; |
||||||
588 | $to_write['uid'] = $data['account_lid']; |
||||||
589 | $to_write['gidnumber'] = abs($data['account_primary_group']); |
||||||
590 | if (!$new_entry || $data['account_firstname']) |
||||||
591 | { |
||||||
592 | $to_write['givenname'] = $data['account_firstname'] ? $data['account_firstname'] : array(); |
||||||
593 | } |
||||||
594 | $to_write['sn'] = $data['account_lastname']; |
||||||
595 | if (!$new_entry || $data['account_email']) |
||||||
596 | { |
||||||
597 | $to_write[static::MAIL_ATTR] = $data['account_email'] ? $data['account_email'] : array(); |
||||||
598 | } |
||||||
599 | $to_write['cn'] = $data['account_fullname'] ? $data['account_fullname'] : $data['account_firstname'].' '.$data['account_lastname']; |
||||||
600 | |||||||
601 | $utc_diff = date('Z'); |
||||||
602 | if (isset($data['account_passwd']) && $data['account_passwd']) |
||||||
603 | { |
||||||
604 | if (preg_match('/^[a-f0-9]{32}$/', $data['account_passwd'])) // md5 --> ldap md5 |
||||||
605 | { |
||||||
606 | $data['account_passwd'] = setup_cmd_ldap::hash_sql2ldap($data['account_passwd']); |
||||||
607 | } |
||||||
608 | elseif (!preg_match('/^\\{[a-z5]{3,5}\\}.+/i',$data['account_passwd'])) // if it's not already entcrypted, do so now |
||||||
609 | { |
||||||
610 | $data['account_passwd'] = Api\Auth::encrypt_ldap($data['account_passwd']); |
||||||
611 | } |
||||||
612 | $to_write['userpassword'] = $data['account_passwd']; |
||||||
613 | $to_write['shadowlastchange'] = round((time()-$utc_diff) / (24*3600)); |
||||||
614 | } |
||||||
615 | // both status and expires are encoded in the single shadowexpire value in LDAP |
||||||
616 | // - if it's unset an account is enabled AND does never expire |
||||||
617 | // - if it's set to 0, the account is disabled |
||||||
618 | // - if it's set to > 0, it will or already has expired --> acount is active if it not yet expired |
||||||
619 | // shadowexpire is in days since 1970/01/01 (equivalent to a timestamp (int UTC!) / (24*60*60) |
||||||
620 | $shadowexpire = ($data['account_expires']-$utc_diff) / (24*3600); |
||||||
621 | |||||||
622 | $to_write['shadowexpire'] = !$data['account_status'] ? |
||||||
623 | ($data['account_expires'] != -1 && $data['account_expires'] < time() ? round($shadowexpire) : 0) : |
||||||
624 | ($data['account_expires'] != -1 ? round($shadowexpire) : array()); // array() = unset value |
||||||
625 | |||||||
626 | if ($new_entry && is_array($to_write['shadowexpire']) && !count($to_write['shadowexpire'])) |
||||||
627 | { |
||||||
628 | unset($to_write['shadowexpire']); // gives protocoll error otherwise |
||||||
629 | } |
||||||
630 | //error_log(__METHOD__.__LINE__.$data['account_lid'].'#'.$data['account_lastpwd_change'].'#'); |
||||||
631 | if ($data['account_lastpwd_change']) $to_write['shadowlastchange'] = round(($data['account_lastpwd_change']-$utc_diff)/(24*3600)); |
||||||
632 | if ($data['mustchangepassword'] == 1 || isset($data['account_lastpwd_change']) && $data['account_lastpwd_change'] == 0) |
||||||
633 | { |
||||||
634 | $to_write['shadowlastchange'] = 0; |
||||||
635 | } |
||||||
636 | // lastlogin and lastlogin from are not availible via the shadowAccount object class |
||||||
637 | // $to_write['phpgwaccountlastlogin'] = $data['lastlogin']; |
||||||
638 | // $to_write['phpgwaccountlastloginfrom'] = $data['lastloginfrom']; |
||||||
639 | |||||||
640 | if ($this->frontend->config['ldap_extra_attributes']) |
||||||
641 | { |
||||||
642 | if (isset($data['homedirectory'])) $to_write['homedirectory'] = $data['homedirectory']; |
||||||
643 | if (isset($data['loginshell'])) $to_write['loginshell'] = $data['loginshell'] ? $data['loginshell'] : array(); |
||||||
644 | } |
||||||
645 | if (($new_entry || isset($to_write['homedirectory'])) && empty($to_write['homedirectory'])) |
||||||
646 | { |
||||||
647 | $to_write['homedirectory'] = '/dev/null'; // is a required attribute of posixAccount |
||||||
648 | } |
||||||
649 | if ($new_entry && empty($to_write['loginshell'])) |
||||||
650 | { |
||||||
651 | unset($to_write['loginshell']); // setting array() for new entry gives "Protocol error", must not set it |
||||||
652 | } |
||||||
653 | return $to_write; |
||||||
654 | } |
||||||
655 | |||||||
656 | /** |
||||||
657 | * Searches / lists accounts: users and/or groups |
||||||
658 | * |
||||||
659 | * @param array with the following keys: |
||||||
660 | * @param $param['type'] string/int 'accounts', 'groups', 'owngroups' (groups the user is a member of), 'both' |
||||||
0 ignored issues
–
show
|
|||||||
661 | * or integer group-id for a list of members of that group |
||||||
662 | * @param $param['start'] int first account to return (returns offset or max_matches entries) or all if not set |
||||||
663 | * @param $param['order'] string column to sort after, default account_lid if unset |
||||||
664 | * @param $param['sort'] string 'ASC' or 'DESC', default 'ASC' if not set |
||||||
665 | * @param $param['query'] string to search for, no search if unset or empty |
||||||
666 | * @param $param['query_type'] string: |
||||||
667 | * 'all' - query all fields for containing $param[query] |
||||||
668 | * 'start' - query all fields starting with $param[query] |
||||||
669 | * 'exact' - query all fields for exact $param[query] |
||||||
670 | * 'lid','firstname','lastname','email' - query only the given field for containing $param[query] |
||||||
671 | * @param $param['offset'] int - number of matches to return if start given, default use the value in the prefs |
||||||
672 | * @param $param['objectclass'] boolean return objectclass(es) under key 'objectclass' in each account |
||||||
673 | * @return array with account_id => data pairs, data is an array with account_id, account_lid, account_firstname, |
||||||
674 | * account_lastname, person_id (id of the linked addressbook entry), account_status, account_expires, account_primary_group |
||||||
675 | */ |
||||||
676 | function search($param) |
||||||
677 | { |
||||||
678 | //error_log(__METHOD__."(".array2string($param).")"); |
||||||
679 | $account_search =& Api\Accounts::$cache['account_search']; |
||||||
680 | |||||||
681 | // check if the query is cached |
||||||
682 | $serial = serialize($param); |
||||||
683 | if (isset($account_search[$serial])) |
||||||
684 | { |
||||||
685 | $this->total = $account_search[$serial]['total']; |
||||||
686 | return $account_search[$serial]['data']; |
||||||
687 | } |
||||||
688 | // if it's a limited query, check if the unlimited query is cached |
||||||
689 | $start = $param['start']; |
||||||
690 | if (!($maxmatchs = $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs'])) $maxmatchs = 15; |
||||||
691 | if (!($offset = $param['offset'])) $offset = $maxmatchs; |
||||||
692 | unset($param['start']); |
||||||
693 | unset($param['offset']); |
||||||
694 | $unl_serial = serialize($param); |
||||||
695 | if (isset($account_search[$unl_serial])) |
||||||
696 | { |
||||||
697 | $this->total = $account_search[$unl_serial]['total']; |
||||||
698 | $sortedAccounts = $account_search[$unl_serial]['data']; |
||||||
699 | } |
||||||
700 | else // we need to run the unlimited query |
||||||
701 | { |
||||||
702 | $query = Api\Ldap::quote(strtolower($param['query'])); |
||||||
703 | |||||||
704 | $accounts = array(); |
||||||
705 | if($param['type'] != 'groups') |
||||||
706 | { |
||||||
707 | $filter = "(&(objectclass=posixaccount)"; |
||||||
708 | if (!empty($query) && $query != '*') |
||||||
709 | { |
||||||
710 | switch($param['query_type']) |
||||||
711 | { |
||||||
712 | case 'all': |
||||||
713 | default: |
||||||
714 | $query = '*'.$query; |
||||||
715 | // fall-through |
||||||
716 | case 'start': |
||||||
717 | $query .= '*'; |
||||||
718 | // use now exact, as otherwise groups have "**pattern**", which dont match anything |
||||||
719 | $param['query_type'] = 'exact'; |
||||||
720 | // fall-through |
||||||
721 | case 'exact': |
||||||
722 | $filter .= "(|(uid=$query)(sn=$query)(cn=$query)(givenname=$query)(mail=$query))"; |
||||||
723 | break; |
||||||
724 | case 'firstname': |
||||||
725 | case 'lastname': |
||||||
726 | case 'lid': |
||||||
727 | case 'email': |
||||||
728 | $to_ldap = array( |
||||||
729 | 'firstname' => 'givenname', |
||||||
730 | 'lastname' => 'sn', |
||||||
731 | 'lid' => 'uid', |
||||||
732 | 'email' => static::MAIL_ATTR, |
||||||
733 | ); |
||||||
734 | $filter .= '('.$to_ldap[$param['query_type']].'=*'.$query.'*)'; |
||||||
735 | break; |
||||||
736 | } |
||||||
737 | } |
||||||
738 | // add account_filter to filter (user has to be '*', as we otherwise only search uid's) |
||||||
739 | $filter .= str_replace(array('%user','%domain'),array('*',$GLOBALS['egw_info']['user']['domain']),$this->account_filter); |
||||||
740 | $filter .= ')'; |
||||||
741 | |||||||
742 | if ($param['type'] != 'both') |
||||||
743 | { |
||||||
744 | // folw: |
||||||
745 | // - first query only few attributes for sorting and throwing away not needed results |
||||||
746 | // - throw away & sort |
||||||
747 | // - fetch relevant accounts with full information |
||||||
748 | // - map and resolve |
||||||
749 | $propertyMap = array( |
||||||
750 | 'account_id' => 'uidnumber', |
||||||
751 | 'account_lid' => 'uid', |
||||||
752 | 'account_firstname' => 'givenname', |
||||||
753 | 'account_lastname' => 'sn', |
||||||
754 | 'account_email' => 'email', |
||||||
755 | 'account_fullname' => 'cn', |
||||||
756 | 'account_primary_group' => 'gidnumber', |
||||||
757 | ); |
||||||
758 | $orders = explode(',',$param['order']); |
||||||
759 | $order = isset($propertyMap[$orders[0]]) ? $propertyMap[$orders[0]] : 'uid'; |
||||||
760 | $sri = ldap_search($this->ds, $this->user_context, $filter,array('uid', $order)); |
||||||
761 | $fullSet = array(); |
||||||
762 | foreach ((array)ldap_get_entries($this->ds, $sri) as $key => $entry) |
||||||
763 | { |
||||||
764 | if ($key !== 'count') $fullSet[$entry['uid'][0]] = $entry[$order][0]; |
||||||
765 | } |
||||||
766 | |||||||
767 | if (is_numeric($param['type'])) // return only group-members |
||||||
768 | { |
||||||
769 | $relevantAccounts = array(); |
||||||
0 ignored issues
–
show
|
|||||||
770 | $sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=" . abs($param['type']) . "))",array('memberuid')); |
||||||
771 | $group = ldap_get_entries($this->ds, $sri); |
||||||
772 | $fullSet = $group[0]['memberuid'] ? array_intersect_key($fullSet, array_flip($group[0]['memberuid'])) : array(); |
||||||
773 | } |
||||||
774 | $totalcount = count($fullSet); |
||||||
775 | |||||||
776 | $sortFn = $param['sort'] == 'DESC' ? 'arsort' : 'asort'; |
||||||
777 | $sortFn($fullSet); |
||||||
778 | $relevantAccounts = is_numeric($start) ? array_slice(array_keys($fullSet), $start, $offset) : array_keys($fullSet); |
||||||
779 | $filter = '(&(objectclass=posixaccount)(|(uid='.implode(')(uid=',$relevantAccounts).'))' . $this->account_filter.')'; |
||||||
780 | $filter = str_replace(array('%user','%domain'),array('*',$GLOBALS['egw_info']['user']['domain']),$filter); |
||||||
781 | } |
||||||
782 | $sri = ldap_search($this->ds, $this->user_context, $filter,array('uid','uidNumber','givenname','sn',static::MAIL_ATTR,'shadowExpire','createtimestamp','modifytimestamp','objectclass','gidNumber')); |
||||||
783 | |||||||
784 | $utc_diff = date('Z'); |
||||||
785 | foreach(ldap_get_entries($this->ds, $sri) as $allVals) |
||||||
786 | { |
||||||
787 | settype($allVals,'array'); |
||||||
788 | $test = @$allVals['uid'][0]; |
||||||
789 | if (!$this->frontend->config['global_denied_users'][$test] && $allVals['uid'][0]) |
||||||
790 | { |
||||||
791 | $account = Array( |
||||||
792 | 'account_id' => $allVals['uidnumber'][0], |
||||||
793 | 'account_lid' => Api\Translation::convert($allVals['uid'][0],'utf-8'), |
||||||
794 | 'account_type' => 'u', |
||||||
795 | 'account_firstname' => Api\Translation::convert($allVals['givenname'][0],'utf-8'), |
||||||
796 | 'account_lastname' => Api\Translation::convert($allVals['sn'][0],'utf-8'), |
||||||
797 | 'account_status' => isset($allVals['shadowexpire'][0]) && $allVals['shadowexpire'][0]*24*3600-$utc_diff < time() ? false : 'A', |
||||||
798 | 'account_expires' => isset($allVals['shadowexpire']) && $allVals['shadowexpire'][0] ? $allVals['shadowexpire'][0]*24*3600+$utc_diff : -1, // LDAP date is in UTC |
||||||
799 | 'account_email' => $allVals[static::MAIL_ATTR][0], |
||||||
800 | 'account_created' => isset($allVals['createtimestamp'][0]) ? self::accounts_ldap2ts($allVals['createtimestamp'][0]) : null, |
||||||
801 | 'account_modified' => isset($allVals['modifytimestamp'][0]) ? self::accounts_ldap2ts($allVals['modifytimestamp'][0]) : null, |
||||||
802 | 'account_primary_group' => (string)-$allVals['gidnumber'][0], |
||||||
803 | ); |
||||||
804 | //error_log(__METHOD__."() ldap=".array2string($allVals)." --> account=".array2string($account)); |
||||||
805 | if ($param['active'] && !$this->frontend->is_active($account)) |
||||||
806 | { |
||||||
807 | if (isset($totalcount)) --$totalcount; |
||||||
808 | continue; |
||||||
809 | } |
||||||
810 | $account['account_fullname'] = Api\Accounts::format_username($account['account_lid'], |
||||||
811 | $account['account_firstname'], $account['account_lastname'], $allVals['uidnumber'][0]); |
||||||
812 | // return objectclass(es) |
||||||
813 | if ($param['objectclass']) |
||||||
814 | { |
||||||
815 | $account['objectclass'] = array_map('strtolower', $allVals['objectclass']); |
||||||
816 | unset($account['objectclass']['count']); |
||||||
817 | } |
||||||
818 | $accounts[$account['account_id']] = $account; |
||||||
819 | } |
||||||
820 | } |
||||||
821 | } |
||||||
822 | if ($param['type'] == 'groups' || $param['type'] == 'both') |
||||||
823 | { |
||||||
824 | if(empty($query) || $query == '*') |
||||||
825 | { |
||||||
826 | $filter = '(objectclass=posixgroup)'; |
||||||
827 | } |
||||||
828 | else |
||||||
829 | { |
||||||
830 | switch($param['query_type']) |
||||||
831 | { |
||||||
832 | case 'all': |
||||||
833 | default: |
||||||
834 | $query = '*'.$query; |
||||||
835 | // fall-through |
||||||
836 | case 'start': |
||||||
837 | $query .= '*'; |
||||||
838 | // fall-through |
||||||
839 | case 'exact': |
||||||
840 | break; |
||||||
841 | } |
||||||
842 | $filter = "(&(objectclass=posixgroup)(cn=$query))"; |
||||||
843 | } |
||||||
844 | $sri = ldap_search($this->ds, $this->group_context, $filter,array('cn','gidNumber')); |
||||||
845 | foreach((array)ldap_get_entries($this->ds, $sri) as $allVals) |
||||||
846 | { |
||||||
847 | settype($allVals,'array'); |
||||||
848 | $test = $allVals['cn'][0]; |
||||||
849 | if (!$this->frontend->config['global_denied_groups'][$test] && $allVals['cn'][0]) |
||||||
850 | { |
||||||
851 | $accounts[(string)-$allVals['gidnumber'][0]] = Array( |
||||||
852 | 'account_id' => -$allVals['gidnumber'][0], |
||||||
853 | 'account_lid' => Api\Translation::convert($allVals['cn'][0],'utf-8'), |
||||||
854 | 'account_type' => 'g', |
||||||
855 | 'account_firstname' => Api\Translation::convert($allVals['cn'][0],'utf-8'), |
||||||
856 | 'account_lastname' => lang('Group'), |
||||||
857 | 'account_status' => 'A', |
||||||
858 | 'account_fullname' => Api\Translation::convert($allVals['cn'][0],'utf-8'), |
||||||
859 | ); |
||||||
860 | if (isset($totalcount)) ++$totalcount; |
||||||
861 | } |
||||||
862 | } |
||||||
863 | } |
||||||
864 | // sort the array |
||||||
865 | $this->_callback_sort = strtoupper($param['sort']); |
||||||
866 | $this->_callback_order = empty($param['order']) ? array('account_lid') : explode(',',$param['order']); |
||||||
867 | foreach($this->_callback_order as &$col) |
||||||
868 | { |
||||||
869 | if (substr($col, 0, 8) !== 'account_') $col = 'account_'.$col; |
||||||
870 | } |
||||||
871 | $sortedAccounts = $accounts; |
||||||
872 | uasort($sortedAccounts,array($this,'_sort_callback')); |
||||||
873 | $this->total = isset($totalcount) ? $totalcount : count($accounts); |
||||||
874 | |||||||
875 | // if totalcount is set, $sortedAccounts is NOT the full set, but already a limited set! |
||||||
876 | if (!isset($totalcount)) |
||||||
877 | { |
||||||
878 | $account_search[$unl_serial]['data'] = $sortedAccounts; |
||||||
879 | $account_search[$unl_serial]['total'] = $this->total; |
||||||
880 | } |
||||||
881 | } |
||||||
882 | // return only the wanted accounts |
||||||
883 | reset($sortedAccounts); |
||||||
884 | if(is_numeric($start) && is_numeric($offset)) |
||||||
885 | { |
||||||
886 | $account_search[$serial]['total'] = $this->total; |
||||||
887 | return $account_search[$serial]['data'] = isset($totalcount) ? $sortedAccounts : array_slice($sortedAccounts, $start, $offset); |
||||||
888 | } |
||||||
889 | return $sortedAccounts; |
||||||
890 | } |
||||||
891 | |||||||
892 | /** |
||||||
893 | * DESC or ASC |
||||||
894 | * |
||||||
895 | * @var string |
||||||
896 | */ |
||||||
897 | private $_callback_sort = 'ASC'; |
||||||
898 | /** |
||||||
899 | * column_names to sort by |
||||||
900 | * |
||||||
901 | * @var array |
||||||
902 | */ |
||||||
903 | private $_callback_order = array('account_lid'); |
||||||
904 | |||||||
905 | /** |
||||||
906 | * Sort callback for uasort |
||||||
907 | * |
||||||
908 | * @param array $a |
||||||
909 | * @param array $b |
||||||
910 | * @return int |
||||||
911 | */ |
||||||
912 | function _sort_callback($a,$b) |
||||||
913 | { |
||||||
914 | foreach($this->_callback_order as $col ) |
||||||
915 | { |
||||||
916 | if($this->_callback_sort != 'DESC') |
||||||
917 | { |
||||||
918 | $cmp = strcasecmp( $a[$col], $b[$col] ); |
||||||
919 | } |
||||||
920 | else |
||||||
921 | { |
||||||
922 | $cmp = strcasecmp( $b[$col], $a[$col] ); |
||||||
923 | } |
||||||
924 | if ( $cmp != 0 ) |
||||||
925 | { |
||||||
926 | return $cmp; |
||||||
927 | } |
||||||
928 | } |
||||||
929 | return 0; |
||||||
930 | } |
||||||
931 | |||||||
932 | /** |
||||||
933 | * Creates a timestamp from the date returned by the ldap server |
||||||
934 | * |
||||||
935 | * @internal |
||||||
936 | * @param string $date YYYYmmddHHiiss |
||||||
937 | * @return int |
||||||
938 | */ |
||||||
939 | protected static function accounts_ldap2ts($date) |
||||||
940 | { |
||||||
941 | if (!empty($date)) |
||||||
942 | { |
||||||
943 | return gmmktime(substr($date,8,2),substr($date,10,2),substr($date,12,2), |
||||||
944 | substr($date,4,2),substr($date,6,2),substr($date,0,4)); |
||||||
945 | } |
||||||
946 | return NULL; |
||||||
947 | } |
||||||
948 | |||||||
949 | /** |
||||||
950 | * convert an alphanumeric account-value (account_lid, account_email) to the account_id |
||||||
951 | * |
||||||
952 | * Please note: |
||||||
953 | * - if a group and an user have the same account_lid the group will be returned (LDAP only) |
||||||
954 | * - if multiple user have the same email address, the returned user is undefined |
||||||
955 | * |
||||||
956 | * @param string $_name value to convert |
||||||
957 | * @param string $which ='account_lid' type of $name: account_lid (default), account_email, person_id, account_fullname |
||||||
958 | * @param string $account_type u = user, g = group, default null = try both |
||||||
959 | * @return int|false numeric account_id or false on error ($name not found) |
||||||
960 | */ |
||||||
961 | function name2id($_name,$which='account_lid',$account_type=null) |
||||||
962 | { |
||||||
963 | $name = Api\Ldap::quote(Api\Translation::convert($_name,Api\Translation::charset(),'utf-8')); |
||||||
964 | |||||||
965 | if (in_array($which, array('account_lid','account_email')) && $account_type !== 'u') // groups only support account_(lid|email) |
||||||
966 | { |
||||||
967 | $attr = $which == 'account_lid' ? 'cn' : static::MAIL_ATTR; |
||||||
968 | $sri = ldap_search($this->ds, $this->group_context, '(&('.$attr.'=' . $name . ')(objectclass=posixgroup))', array('gidNumber')); |
||||||
969 | $allValues = ldap_get_entries($this->ds, $sri); |
||||||
970 | |||||||
971 | if (@$allValues[0]['gidnumber'][0]) |
||||||
972 | { |
||||||
973 | return -$allValues[0]['gidnumber'][0]; |
||||||
974 | } |
||||||
975 | } |
||||||
976 | $to_ldap = array( |
||||||
977 | 'account_lid' => 'uid', |
||||||
978 | 'account_email' => static::MAIL_ATTR, |
||||||
979 | 'account_fullname' => 'cn', |
||||||
980 | ); |
||||||
981 | if (!isset($to_ldap[$which]) || $account_type === 'g') |
||||||
982 | { |
||||||
983 | return False; |
||||||
984 | } |
||||||
985 | |||||||
986 | $sri = ldap_search($this->ds, $this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))', array('uidNumber')); |
||||||
987 | |||||||
988 | $allValues = ldap_get_entries($this->ds, $sri); |
||||||
989 | |||||||
990 | if (@$allValues[0]['uidnumber'][0]) |
||||||
991 | { |
||||||
992 | return (int)$allValues[0]['uidnumber'][0]; |
||||||
993 | } |
||||||
994 | return False; |
||||||
995 | } |
||||||
996 | |||||||
997 | /** |
||||||
998 | * Convert an numeric account_id to any other value of that account (account_lid, account_email, ...) |
||||||
999 | * |
||||||
1000 | * Uses the read method to fetch all data. |
||||||
1001 | * |
||||||
1002 | * @param int $account_id numerica account_id |
||||||
1003 | * @param string $which ='account_lid' type to convert to: account_lid (default), account_email, ... |
||||||
1004 | * @return string|false converted value or false on error ($account_id not found) |
||||||
1005 | */ |
||||||
1006 | function id2name($account_id,$which='account_lid') |
||||||
1007 | { |
||||||
1008 | return $this->frontend->id2name($account_id,$which); |
||||||
1009 | } |
||||||
1010 | |||||||
1011 | /** |
||||||
1012 | * Update the last login timestamps and the IP |
||||||
1013 | * |
||||||
1014 | * @param int $_account_id |
||||||
1015 | * @param string $ip |
||||||
1016 | * @return int lastlogin time |
||||||
1017 | */ |
||||||
1018 | function update_lastlogin($_account_id, $ip) |
||||||
1019 | { |
||||||
1020 | unset($_account_id, $ip); |
||||||
1021 | return false; // not longer supported |
||||||
1022 | } |
||||||
1023 | |||||||
1024 | /** |
||||||
1025 | * Query memberships of a given account |
||||||
1026 | * |
||||||
1027 | * @param int $account_id |
||||||
1028 | * @return array|boolean array with account_id => account_lid pairs or false if account not found |
||||||
1029 | */ |
||||||
1030 | function memberships($account_id) |
||||||
1031 | { |
||||||
1032 | if (!(int) $account_id || !($account_lid = $this->id2name($account_id))) return false; |
||||||
1033 | |||||||
1034 | $sri = ldap_search($this->ds,$this->group_context,'(&(objectClass=posixGroup)(memberuid='.Api\Ldap::quote($account_lid).'))',array('cn','gidnumber')); |
||||||
1035 | $memberships = array(); |
||||||
1036 | foreach((array)ldap_get_entries($this->ds, $sri) as $key => $data) |
||||||
1037 | { |
||||||
1038 | if ($key === 'count') continue; |
||||||
1039 | |||||||
1040 | $memberships[(string) -$data['gidnumber'][0]] = $data['cn'][0]; |
||||||
1041 | } |
||||||
1042 | return $memberships; |
||||||
1043 | } |
||||||
1044 | |||||||
1045 | /** |
||||||
1046 | * Query the members of a group |
||||||
1047 | * |
||||||
1048 | * @param int $_gid |
||||||
1049 | * @return array|boolean array with uidnumber => uid pairs, |
||||||
1050 | * false if $_git is not nummeric and can't be resolved to a nummeric gid |
||||||
1051 | */ |
||||||
1052 | function members($_gid) |
||||||
1053 | { |
||||||
1054 | if (!is_numeric($_gid)) |
||||||
0 ignored issues
–
show
|
|||||||
1055 | { |
||||||
1056 | // try to recover |
||||||
1057 | $_gid = $this->name2id($_gid,'account_lid','g'); |
||||||
1058 | if (!is_numeric($_gid)) return false; |
||||||
1059 | } |
||||||
1060 | |||||||
1061 | $gid = abs($_gid); // our gid is negative! |
||||||
1062 | |||||||
1063 | $sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=$gid))",array('memberuid')); |
||||||
1064 | $group = ldap_get_entries($this->ds, $sri); |
||||||
1065 | |||||||
1066 | $members = array(); |
||||||
1067 | if (isset($group[0]['memberuid'])) |
||||||
1068 | { |
||||||
1069 | unset($group[0]['memberuid']['count']); |
||||||
1070 | |||||||
1071 | foreach($group[0]['memberuid'] as $lid) |
||||||
1072 | { |
||||||
1073 | if (($id = $this->name2id($lid, 'account_lid'))) // also return groups! |
||||||
1074 | { |
||||||
1075 | $members[$id] = $lid; |
||||||
1076 | } |
||||||
1077 | } |
||||||
1078 | } |
||||||
1079 | return $members; |
||||||
1080 | } |
||||||
1081 | |||||||
1082 | /** |
||||||
1083 | * Sets the memberships of the given account |
||||||
1084 | * |
||||||
1085 | * @param array $groups array with gidnumbers |
||||||
1086 | * @param int $account_id uidnumber |
||||||
1087 | */ |
||||||
1088 | function set_memberships($groups,$account_id) |
||||||
1089 | { |
||||||
1090 | // remove not longer existing memberships |
||||||
1091 | if (($old_memberships = $this->memberships($account_id))) |
||||||
1092 | { |
||||||
1093 | $old_memberships = array_keys($old_memberships); |
||||||
1094 | foreach(array_diff($old_memberships,$groups) as $gid) |
||||||
1095 | { |
||||||
1096 | if (($members = $this->members($gid))) |
||||||
1097 | { |
||||||
1098 | unset($members[$account_id]); |
||||||
1099 | $this->set_members($members,$gid); |
||||||
1100 | } |
||||||
1101 | } |
||||||
1102 | } |
||||||
1103 | // adding new memberships |
||||||
1104 | foreach($old_memberships ? array_diff($groups,$old_memberships) : $groups as $gid) |
||||||
1105 | { |
||||||
1106 | if (!($members = $this->members($gid))) $members = array(); |
||||||
1107 | $members[$account_id] = $this->id2name($account_id); |
||||||
1108 | $this->set_members($members,$gid); |
||||||
1109 | } |
||||||
1110 | } |
||||||
1111 | |||||||
1112 | /** |
||||||
1113 | * Set the members of a group |
||||||
1114 | * |
||||||
1115 | * @param array $members array with uidnumber or uid's |
||||||
1116 | * @param int $gid gidnumber of group to set |
||||||
1117 | * @param array $objectclass =null should we set the member and uniqueMember attributes (groupOf(Unique)Names|univentionGroup) (default detect it) |
||||||
1118 | * @param string $use_cn =null if set $cn is used instead $gid and the attributes are returned, not written to ldap |
||||||
1119 | * @return boolean/array false on failure, array or true otherwise |
||||||
0 ignored issues
–
show
|
|||||||
1120 | */ |
||||||
1121 | function set_members($members, $gid, array $objectclass=null, $use_cn=null) |
||||||
1122 | { |
||||||
1123 | if (!($cn = $use_cn) && !($cn = $this->id2name($gid))) return false; |
||||||
1124 | |||||||
1125 | // do that group is a groupOf(Unique)Names or univentionGroup? |
||||||
1126 | if (!isset($objectclass)) |
||||||
1127 | { |
||||||
1128 | $objectclass = $this->id2name($gid, 'objectclass'); |
||||||
1129 | // if we cant find objectclass, we might ge in the middle of a migration |
||||||
1130 | if (!isset($objectclass)) |
||||||
1131 | { |
||||||
1132 | Api\Accounts::cache_invalidate($gid); |
||||||
1133 | if (!($objectclass = $this->id2name($gid, 'objectclass'))) |
||||||
1134 | { |
||||||
1135 | // group does not yet exist --> return false |
||||||
1136 | return false; |
||||||
1137 | } |
||||||
1138 | } |
||||||
1139 | } |
||||||
1140 | |||||||
1141 | $to_write = array('memberuid' => array()); |
||||||
1142 | foreach((array)$members as $member) |
||||||
1143 | { |
||||||
1144 | if (!$member) continue; |
||||||
1145 | |||||||
1146 | $member_dn = $this->id2name($member, 'account_dn'); |
||||||
1147 | if (is_numeric($member)) $member = $this->id2name($member); |
||||||
1148 | |||||||
1149 | // only add a member, if we have the neccessary info / he already exists in migration |
||||||
1150 | if ($member && ($member_dn || !array_intersect(array('groupofnames','groupofuniquenames','univentiongroup'), $objectclass))) |
||||||
1151 | { |
||||||
1152 | $to_write['memberuid'][] = $member; |
||||||
1153 | if (in_array('groupofnames', $objectclass)) |
||||||
1154 | { |
||||||
1155 | $to_write['member'][] = $member_dn; |
||||||
1156 | } |
||||||
1157 | if (array_intersect(array('groupofuniquenames','univentiongroup'), $objectclass)) |
||||||
1158 | { |
||||||
1159 | $to_write['uniquemember'][] = $member_dn; |
||||||
1160 | } |
||||||
1161 | } |
||||||
1162 | } |
||||||
1163 | // hack as groupOfNames requires the member attribute |
||||||
1164 | if (in_array('groupofnames', $objectclass) && !$to_write['member']) |
||||||
1165 | { |
||||||
1166 | $to_write['member'][] = 'uid=dummy'.','.$this->user_context; |
||||||
1167 | } |
||||||
1168 | if (array_intersect(array('groupofuniquenames','univentiongroup'), $objectclass) && !$to_write['uniquemember']) |
||||||
1169 | { |
||||||
1170 | $to_write['uniquemember'][] = 'uid=dummy'.','.$this->user_context; |
||||||
1171 | } |
||||||
1172 | if ($use_cn) return $to_write; |
||||||
1173 | |||||||
1174 | // set the member email addresses as forwards |
||||||
1175 | if ($this->id2name($gid,'account_email') && ($objectclass = $this->id2name($gid,'mailAllowed'))) |
||||||
1176 | { |
||||||
1177 | $forward = $this->group_mail_classes[$objectclass]; |
||||||
1178 | if (is_array($forward)) list($forward,$extra_attr) = $forward; |
||||||
1179 | if ($extra_attr && ($uid = $this->id2name($gid))) $to_write[$extra_attr] = $uid; |
||||||
1180 | |||||||
1181 | if ($forward) |
||||||
1182 | { |
||||||
1183 | $to_write[$forward] = array(); |
||||||
1184 | foreach($members as $member) |
||||||
1185 | { |
||||||
1186 | if (($email = $this->id2name($member,'account_email'))) $to_write[$forward][] = $email; |
||||||
1187 | } |
||||||
1188 | } |
||||||
1189 | } |
||||||
1190 | if (!ldap_modify($this->ds,'cn='.Api\Ldap::quote($cn).','.$this->group_context,$to_write)) |
||||||
1191 | { |
||||||
1192 | error_log(__METHOD__."(members=".array2string($members).", gid=$gid, objectclass=".array2string($objectclass).", use_cn=$use_cn) !ldap_modify(,'cn=$cn,$this->group_context', ".array2string($to_write).") --> ldap_error()=".ldap_error($this->ds)); |
||||||
1193 | return false; |
||||||
1194 | } |
||||||
1195 | return true; |
||||||
1196 | } |
||||||
1197 | |||||||
1198 | /** |
||||||
1199 | * Using the common functions next_id and last_id, find the next available account_id |
||||||
1200 | * |
||||||
1201 | * @internal |
||||||
1202 | * @param string $account_type ='u' (optional, default to 'u') |
||||||
1203 | * @return int|boolean integer account_id (negative for groups) or false if none is free anymore |
||||||
1204 | */ |
||||||
1205 | protected function _get_nextid($account_type='u') |
||||||
1206 | { |
||||||
1207 | $min = $this->frontend->config['account_min_id'] ? $this->frontend->config['account_min_id'] : 0; |
||||||
1208 | $max = $this->frontend->config['account_max_id'] ? $this->frontend->config['account_max_id'] : 0; |
||||||
1209 | |||||||
1210 | // prefer ids above 1000 (below reserved for system users under AD or Linux), |
||||||
1211 | // if that's possible within what is configured, or nothing is configured |
||||||
1212 | if ($min < 1000 && (!$max || $max > 1000)) $min = 1000; |
||||||
1213 | |||||||
1214 | if ($account_type == 'g') |
||||||
1215 | { |
||||||
1216 | $type = 'groups'; |
||||||
1217 | $sign = -1; |
||||||
1218 | } |
||||||
1219 | else |
||||||
1220 | { |
||||||
1221 | $type = 'accounts'; |
||||||
1222 | $sign = 1; |
||||||
1223 | } |
||||||
1224 | /* Loop until we find a free id */ |
||||||
1225 | do |
||||||
1226 | { |
||||||
1227 | $account_id = (int) self::next_id($type,$min,$max); |
||||||
1228 | } |
||||||
1229 | while ($account_id && ($this->frontend->exists($sign * $account_id) || // check need to include the sign! |
||||||
1230 | $this->frontend->exists(-1 * $sign * $account_id) || |
||||||
1231 | // if sambaadmin is installed, call it to check there's not yet a relative id (last part of SID) with that number |
||||||
1232 | // to ease migration to AD or Samba4 |
||||||
1233 | file_exists(EGW_SERVER_ROOT.'/sambaadmin') && $GLOBALS['egw_info']['apps']['sambaadmin'] && |
||||||
1234 | ExecMethod2('sambaadmin.sosambaadmin.sidExists', $account_id))); |
||||||
0 ignored issues
–
show
The function
ExecMethod2() has been deprecated: use autoloadable class-names, instanciate and call method or use static methods
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This function has been deprecated. The supplier of the function has supplied an explanatory message. The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead. ![]() |
|||||||
1235 | |||||||
1236 | if (!$account_id || $max && $account_id > $max) |
||||||
1237 | { |
||||||
1238 | return False; |
||||||
1239 | } |
||||||
1240 | return $sign * $account_id; |
||||||
1241 | } |
||||||
1242 | |||||||
1243 | /** |
||||||
1244 | * Return a value for the next id an app/class may need to insert values into LDAP |
||||||
1245 | * |
||||||
1246 | * @param string $location name for id eg. "groups" or "accounts" |
||||||
1247 | * @param int $min =0 if != 0 minimum id |
||||||
1248 | * @param int $max =0 if != 0 maximum id allowed, if it would be exceeded we return false |
||||||
1249 | * @return int|boolean the next id or false if $max given and exceeded |
||||||
1250 | */ |
||||||
1251 | static function next_id($location,$min=0,$max=0) |
||||||
1252 | { |
||||||
1253 | if (!$location) |
||||||
1254 | { |
||||||
1255 | return -1; |
||||||
1256 | } |
||||||
1257 | |||||||
1258 | $id = (int)$GLOBALS['egw_info']['server'][$key='last_id_'.$location]; |
||||||
1259 | |||||||
1260 | if ($max && $id >= $max) |
||||||
1261 | { |
||||||
1262 | return False; |
||||||
1263 | } |
||||||
1264 | ++$id; |
||||||
1265 | |||||||
1266 | if($id < $min) $id = $min; |
||||||
1267 | |||||||
1268 | Api\Config::save_value($key, $id, 'phpgwapi', true); |
||||||
1269 | $GLOBALS['egw_info']['server'][$key='last_id_'.$location] = $id; |
||||||
0 ignored issues
–
show
|
|||||||
1270 | |||||||
1271 | return (int)$id; |
||||||
1272 | } |
||||||
1273 | |||||||
1274 | /** |
||||||
1275 | * Return a value for the last id entered, which an app may need to check values for LDAP |
||||||
1276 | * |
||||||
1277 | * @param string $location name for id eg. "groups" or "accounts" |
||||||
1278 | * @param int $min =0 if != 0 minimum id |
||||||
1279 | * @param int $max =0 if != 0 maximum id allowed, if it would be exceeded we return false |
||||||
1280 | * @return int|boolean current id in the next_id table for a particular app/class or -1 for no app and false if $max is exceeded. |
||||||
1281 | */ |
||||||
1282 | static function last_id($location,$min=0,$max=0) |
||||||
1283 | { |
||||||
1284 | if (!$location) |
||||||
1285 | { |
||||||
1286 | return -1; |
||||||
1287 | } |
||||||
1288 | |||||||
1289 | $id = (int)$GLOBALS['egw_info']['server'][$key='last_id_'.$location]; |
||||||
0 ignored issues
–
show
|
|||||||
1290 | |||||||
1291 | if (!$id || $id < $min) |
||||||
1292 | { |
||||||
1293 | return self::next_id($location,$min,$max); |
||||||
1294 | } |
||||||
1295 | if ($max && $id > $max) |
||||||
1296 | { |
||||||
1297 | return False; |
||||||
1298 | } |
||||||
1299 | return $id; |
||||||
1300 | } |
||||||
1301 | |||||||
1302 | /** |
||||||
1303 | * __wakeup function gets called by php while unserializing the object to reconnect with the ldap server |
||||||
1304 | */ |
||||||
1305 | function __wakeup() |
||||||
1306 | { |
||||||
1307 | $this->ds = Api\Ldap::factory(true, $this->frontend->config['ldap_host'], |
||||||
0 ignored issues
–
show
It seems like
EGroupware\Api\Ldap::fac...config['ldap_root_pw']) can also be of type EGroupware\Api\Ldap . However, the property $ds is declared as type resource . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||||||
1308 | $this->frontend->config['ldap_root_dn'],$this->frontend->config['ldap_root_pw']); |
||||||
1309 | } |
||||||
1310 | } |
||||||
1311 |