├── multi-ldap
├── plugin.php
├── config.php
├── sync_mldap.php
├── class.AuthLdap.php
└── auth.php
├── README.md
└── License.txt
/multi-ldap/plugin.php:
--------------------------------------------------------------------------------
1 | 'multiauth:ldap',
4 | 'version' => '1.10.1',
5 | 'ost_version' => '1.16', # Require osTicket v1.17+
6 | 'name' => 'Multi LDAP Authentication and Lookup',
7 | 'author' => 'Joseph Philbert',
8 | 'description' => 'Provides a configurable authentication backend which works against multiple LDAP servers',
9 | 'url' => 'http://www.vide.vi',
10 | 'plugin' => 'auth.php:LdapMultiAuthPlugin'
11 | );
12 | //'requires' => array("sync/sync_mldap.php" => array("version" => "*","map" => array("sync/sync_mldap.php/src" => 'sync/',),),)
13 | ?>
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Multi LDAP authentication and LDAP Sync v1.10 for osTicket
2 | =====================================
3 | Plugin for OS Ticket that allows for authentication with multiple domains and servers for agents and/or clients on osTicket also syncs user defined attributes from AD LDAP.
4 | Works and tested with version 1.10 to v1.17+ and PHP 8+
5 |
6 | |CURRENTLY DO NOT SUPPORT MULTIPLE INSTANCES|
7 |
8 |
9 | Features
10 | ========
11 | - Multiple domain and server support.
12 | - SSL connection support.
13 | - LDAP login for both agents and clients (can be toggled for neither, either, or both).
14 | - Combines users in all domains into one for seamless searches.
15 | - Creates user accounts and syncs information as needed.
16 | - Sync accounts in LDAP with user defined schedule.
17 | - Disables or Enables Osticket users based on LDAP
18 | - Syncs all attributes only on users that have change via AD time and date.
19 | - Custom defined ldap map attributes
20 | - Keeps track of updated users
21 | - Schedule is activated based on the cron job
22 | - manual sync button
23 | - create users and staff even if the system is private/closed mode.
24 | - Fixed issue with upgrading from 1.5+ to 1.7
25 | - Support for Plugin Instances
26 |
27 | User Lookup
28 |
29 | 
30 |
31 | Sync Email
32 |
33 | 
34 |
35 | Example of sync report in my environment.
36 | 
37 |
38 | Installing
39 | ==========
40 |
41 | ### Prebuilt
42 |
43 | simply create a folder in the "includes\plugins\multi-ldap" on your osticket install
44 |
45 | Configuration
46 | =============
47 | It is pretty stright forward just when adding the second domain make user you put a "," or ";" where needed.
48 | see image below
49 |
50 | 
51 |
52 | Sync Settings
53 |
54 | 
55 |
56 | CRON JOBS required for user syncing to run.
57 |
58 | In my environment we have a Parent and Child domain
59 | Parent domain 8000+ users
60 | Child domain 20,000+ users
61 |
62 | 
63 |
64 | It syncs both the agents and users without issues with about 1000+ users registered automatically.
65 | Made plugin backward compatible with older versions.
66 |
67 | Bug fixes
68 | ===========
69 | Lookup bug corrected.
70 | Syncing bug
71 | Added Instances support
72 | removed "ldap.clinet" references to avoid conflicts.
73 | sync_data table not refrenced or updated properly.
74 | change instance logic
75 | updated and changed debug code.
76 |
77 | Roadmap
78 | ==========
79 | Better Instance support in plugin database.(almost done)
80 | Proper manual Sync button
81 | UTF8 support for languages.
82 | Ldap caching for large LDAP domains
83 | Planning on adding a TAB feature to make setting section easier to manage.
84 | Add users from AD to helpdesk automatically.
85 |
86 | ### From source
87 |
88 | to be updated.........
89 |
--------------------------------------------------------------------------------
/multi-ldap/config.php:
--------------------------------------------------------------------------------
1 | config['sync_data']->ht['value']);
37 | $time_zone = db_result(db_query("SELECT value FROM `" . TABLE_PREFIX . "config` WHERE `key` = 'default_timezone'"));
38 | $scheduletime = LDAPMultiAuthentication::DateFromTimezone(strftime("%Y-%m-%d %H:%M", $sync_val->schedule) , 'UTC', $time_zone, 'F d Y g:i a');
39 | return $scheduletime;
40 | }
41 |
42 | function getlastschedule() {
43 | $sync_val = json_decode($this->config['sync_data']->ht['value']);
44 | $time_zone = db_result(db_query("SELECT value FROM `" . TABLE_PREFIX . "config` WHERE `key` = 'default_timezone'"));
45 | $lastruntime = LDAPMultiAuthentication::DateFromTimezone(strftime("%Y-%m-%d %H:%M", $sync_val->lastrun) , 'UTC', $time_zone, 'F d Y g:i a');
46 | return $lastruntime;
47 | }
48 |
49 | function checkschedule($update = false) {
50 | global $ost;
51 | $current_schedule = $this->config['sync_schedule']
52 | ->ht['value'];
53 | $new_schedule = $this->getForm()
54 | ->getField('sync_schedule')
55 | ->getValue();
56 |
57 | if (($current_schedule != $new_schedule) || $update) {
58 | $id = substr($this->section, -1);
59 | $schedule = json_encode(array("schedule"=>strtotime($current_schedule),"lastrun"=>time()));
60 | $sql = 'INSERT INTO `' . TABLE_PREFIX . 'config` (namespace,`key`,value, updated)
61 | VALUES ("' . $this->config['sync_schedule']->ht['namespace'] . '","sync_data", \''.$schedule.'\', CURRENT_TIMESTAMP)
62 | ON DUPLICATE KEY UPDATE `value` = \''.$schedule.'\', `updated` = CURRENT_TIMESTAMP';
63 |
64 | $query = db_query($sql);
65 | return $query;
66 | }
67 | return false;
68 | }
69 |
70 | //List osticket accounts
71 | function FromMail()
72 | {
73 | $frommail = array();
74 | $sql = 'SELECT email_id,email,name FROM ' . EMAIL_TABLE . ' email ORDER by name';
75 | if (($res = db_query($sql)) && db_num_rows($res)) {
76 | while (list($id, $email, $name, $smtp) = db_fetch_row($res)) {
77 | if ($name) $email = Format::htmlchars("$name <$email>");
78 | if ($smtp) $email .= ' (' . __('SMTP') . ')';
79 | $frommail[$id] = $email;
80 | }
81 | return $frommail;
82 | }
83 | }
84 |
85 | function getOptions() {
86 | global $ost;
87 | $from_choices = $this->FromMail();
88 | foreach ($this->getDeptList() as $id => $name)
89 | $deptlist[$id] = $name;
90 |
91 |
92 | list($__, $_N) = self::translate();
93 | return array(
94 | 'msad' => new SectionBreakField(array(
95 | 'label' => 'LDAP Information',
96 | 'hint' => $__('Enter all required for LDAP settings /.../ use semicolons to seperate values') ,
97 | )) ,
98 | 'basedn' => new TextareaField(array(
99 | 'id' => 'base',
100 | 'label' => $__('BaseDN') ,
101 | 'hint' => $__('The base DN (e.g. "dc=foo,dc=com;dc=doo,dc=com;")') ,
102 | 'configuration' => array(
103 | 'html' => false,
104 | 'rows' => 2,
105 | 'cols' => 40
106 | ) ,
107 | )) ,
108 | 'shortdomain' => new TextboxField(array(
109 | 'id' => 'sd',
110 | 'label' => $__('Short Domain') ,
111 | 'configuration' => array(
112 | 'size' => 40,
113 | 'length' => 60
114 | ) ,
115 | 'hint' => $__('Use your netbios domain seperated by "," FOO;DOO') ,
116 | )) ,
117 | 'servers' => new TextareaField(array(
118 | 'id' => 'servers',
119 | 'label' => $__('LDAP servers') ,
120 | 'configuration' => array(
121 | 'html' => false,
122 | 'rows' => 2,
123 | 'cols' => 40
124 | ) ,
125 | 'hint' => $__('Use "server" or "server:port". Type server seperated by a ";" and carragie return for next entry of LDAP servers') ,
126 | )) ,
127 | 'tls' => new BooleanField(array(
128 | 'id' => 'tls',
129 | 'label' => $__('Use TLS') ,
130 | 'configuration' => array(
131 | 'desc' => $__('Use TLS to communicate with the LDAP server')
132 | )
133 | )) ,
134 | 'conn_info' => new SectionBreakField(array(
135 | 'label' => $__('Useful only for information lookups') ,
136 | 'hint' => $__('NOTE this data is not necessary if your server allows anonymous searches')
137 | )) ,
138 | 'bind_dn' => new TextareaField(array(
139 | 'label' => $__('Search User') ,
140 | 'hint' => $__('Bind DN (distinguished name) to bind to the LDAP
141 | server as in order to perform searches') ,
142 | 'configuration' => array(
143 | 'html' => false,
144 | 'rows' => 2,
145 | 'cols' => 70
146 | ) ,
147 | )) ,
148 | 'bind_pw' => new TextboxField(array(
149 | 'label' => $__('Password') ,
150 | 'hint' => $__("Password associated with the 'Seach User' account") ,
151 | 'configuration' => array(
152 | 'size' => 40,
153 | 'length' => 60
154 | ) ,
155 | )) ,
156 | 'search_base' => new TextboxField(array(
157 | 'label' => $__('Search Filter') ,
158 | 'hint' => $__('Filter used when searching for users "{q}" is replaced by the user input') ,
159 | 'default' => '(&(objectCategory=person)(objectClass=user)(|(sAMAccountName={q}*)(firstName={q}*)(lastName={q}*)(displayName={q}*)))',
160 | 'configuration' => array(
161 | 'size' => 70,
162 | 'length' => 300
163 | ) ,
164 | )) ,
165 | 'sync_check' => new FreeTextField(array(
166 | 'configuration' => array(
167 | 'content' => __('
168 |
197 |
check connection
')
198 | //icon-spin
199 |
200 | )
201 | )) ,
202 | 'auth' => new SectionBreakField(array(
203 | 'label' => $__('Authentication Modes') ,
204 | 'hint' => $__('Authentication modes for clients and staff
205 | members can be enabled independently') ,
206 | )) ,
207 | 'multiauth-staff' => new BooleanField(array(
208 | 'label' => $__('Staff Authentication') ,
209 | 'default' => true,
210 | 'configuration' => array(
211 | 'desc' => $__('Enable authentication of staff members')
212 | )
213 | )) ,
214 | 'multiauth-client' => new BooleanField(array(
215 | 'label' => $__('Client Authentication') ,
216 | 'default' => false,
217 | 'configuration' => array(
218 | 'desc' => $__('Enable authentication of clients')
219 | )
220 | )) ,
221 | 'security-modes' => new SectionBreakField(array(
222 | 'label' => $__('Security Modes') ,
223 | 'hint' => $__('Sets security for clients and staff
224 | members can be enabled and disabled independently (use semicolons for multiple groups)') ,
225 | )) ,
226 |
227 | 'multiauth-admin-group' => new TextboxField(array(
228 | 'id' => 'adminldapgroup',
229 | 'label' => $__('Admin') ,
230 | 'default' => 'Domain Admins',
231 | 'configuration' => array(
232 | 'size' => 40,
233 | 'length' => 60
234 | ) ,
235 | 'hint' => $__('Administrator group membership') ,
236 | )) ,
237 |
238 | 'multiauth-staff-group' => new TextboxField(array(
239 | 'id' => 'staffldapgroup',
240 | 'label' => $__('Staff') ,
241 | 'default' => 'Domain Admins',
242 | 'configuration' => array(
243 | 'size' => 40,
244 | 'length' => 60
245 | ) ,
246 | 'hint' => $__('Staff registration group membership') ,
247 | )) ,
248 | 'multiauth-client-group' => new TextboxField(array(
249 | 'id' => 'clientldapgroup',
250 | 'label' => $__('Client') ,
251 | 'default' => 'Domain Users',
252 | 'configuration' => array(
253 | 'size' => 40,
254 | 'length' => 60
255 | ) ,
256 | 'hint' => $__('Client registration group membership') ,
257 | )) ,
258 | 'reg-modes' => new SectionBreakField(array(
259 | 'label' => $__('Registration Modes') ,
260 | 'hint' => $__('Registration modes for clients and staff
261 | members can be enabled independently') ,
262 | )) ,
263 | 'multiauth-force-register' => new BooleanField(array(
264 | 'label' => $__('Force client registration') ,
265 | 'default' => true,
266 | 'configuration' => array(
267 | 'desc' => $__('This is useful if you have public registration disabled')
268 | )
269 | )) ,
270 | 'multiauth-staff-register' => new BooleanField(array(
271 | 'label' => $__('Enable Staff registration') ,
272 | 'default' => false,
273 | 'configuration' => array(
274 | 'desc' => $__('Register staff member to be registered automatically')
275 | )
276 | )) ,
277 | 'multiauth_staff_dept' => new ChoiceField(array(
278 | 'label' => 'Default Department for Staff',
279 | 'configuration' => array('multiselect' => false),
280 | 'choices' => $deptlist,
281 | 'default' => 1,
282 | 'hint' => $__('Default department assigned to created staff members.')
283 | )),
284 | 'multiauth-sync' => new SectionBreakField(array(
285 | 'label' => $__('Sync Mode') ,
286 | 'hint' => $__('Various options for syncing users with LDAP') ,
287 | )) ,
288 | 'sync-users' => new BooleanField(array(
289 | 'label' => $__('Sync Users') ,
290 | 'default' => false,
291 | 'configuration' => array(
292 | 'desc' => $__('Enable user synchronization')
293 | )
294 | )) ,
295 | 'sync-agents' => new BooleanField(array(
296 | 'label' => $__('Sync Agents') ,
297 | 'default' => false,
298 | 'configuration' => array(
299 | 'desc' => $__('Enable agent synchronization')
300 | )
301 | )) ,
302 | 'sync_schedule' => new TextboxField(array(
303 | 'id' => 'sync_schedule',
304 | 'label' => $__('Sync Schedule') ,
305 | 'hint' => $__('Set the schedule to sync users') ,
306 | 'default' => '1 day 12AM',
307 | 'configuration' => array(
308 | 'size' => 40,
309 | 'length' => 40
310 | ) ,
311 | 'hint' => $__('Set schedule based on string examples: "5 minutes", "1 hour", "1 day",
312 | "next Thursday", "1 week", "weekdays 1AM", "2 weekends", "2 days", "4 hours", "10 September 2000"') ,
313 | )) ,
314 | 'sync_map' => new TextareaField(array(
315 | 'label' => $__('Sync Attribute MAP') ,
316 | 'hint' => $__('Add the values you need to match Osticket to LDAP attributes example:
"name:cn, phone:telephonenumber, notes:info" first is the osticket attibute then ldap varible comma-limited') ,
317 | 'default' => 'name:cn, phone:telephonenumber, notes:info',
318 | 'configuration' => array(
319 | 'html' => false,
320 | 'rows' => 2,
321 | 'cols' => 70
322 | ) ,
323 | )) ,
324 | 'sync_filter' => new TextboxField(array(
325 | 'label' => $__('LDAP Sync Filter') ,
326 | 'hint' => $__('Custom Filtering for syncing users') ,
327 | 'default' => '(&(sAMAccountType=805306368)(mail=*))',
328 | 'configuration' => array(
329 | 'size' => 70,
330 | 'length' => 150
331 | ) ,
332 | )) ,
333 |
334 | 'sync_btn' => new FreeTextField(array(
335 | 'configuration' => array(
336 | 'content' => __('
337 |
362 | ')
363 | //icon-spin
364 |
365 | )
366 | )) ,
367 | 'sync_full' => new BooleanField(array(
368 | 'label' => $__('Full Sync') ,
369 | 'hint' => $__('This does a full sync on the next scheduled time (this happens only once)') ,
370 | 'configuration' => array(
371 | 'desc' => $__('Check this to do a Full Sync')
372 | )
373 | )) ,
374 | 'reset_schedule' => new BooleanField(array(
375 | 'id' => 'reset_schedule',
376 | 'label' => $__('Reset Schedule') ,
377 | 'default' => false,
378 | 'configuration' => array(
379 | 'desc' => $__('Reset/Update schedule on save')
380 | )
381 | )) ,
382 | 'sync_data_info' => new SectionBreakField(array(
383 | 'label' => $__('Next schedule: ' . $this->getschedule()) ,
384 | 'hint' => $__('Last run: ' . $this->getlastschedule()) ,
385 | )) ,
386 | 'sync_reports' => new BooleanField(array(
387 | 'id' => 'sync_reports',
388 | 'label' => $__('Email Reporting') ,
389 | 'default' => false,
390 | 'configuration' => array(
391 | 'hint' => $__('Check if you want reports emailed')
392 | )
393 | )) ,
394 | 'sync_mailfrom' => new ChoiceField(array(
395 | 'id' => 'sync_mailfrom',
396 | 'label' => 'From email address',
397 | 'default' => 1,
398 | 'choices' => $from_choices,
399 | 'configuration' => array(
400 | 'hint' => 'This list the internal email accounts choose one that fits best.'
401 | )
402 | )) ,
403 | 'sync_mailto' => new TextboxField(array(
404 | 'id' => 'sync_mailto',
405 | 'label' => 'Report address',
406 | 'default' => '',
407 | 'hint' => 'Email address that reports will be sent to',
408 | 'configuration' => array(
409 | 'size' => 30,
410 | 'length' => 30
411 | ) ,
412 | )) ,
413 | 'multiauth-debug' => new SectionBreakField(array(
414 | 'label' => $__('Debug Mode') ,
415 | 'hint' => $__('Turns debugging on or off check the "System Logs" for entires') ,
416 | )) ,
417 | 'debug-choice' => new BooleanField(array(
418 | 'label' => $__('Debug') ,
419 | 'default' => false,
420 | 'configuration' => array(
421 | 'desc' => $__('Enable debugging')
422 | )
423 | )) ,
424 | 'debug-verbose' => new BooleanField(array(
425 | 'label' => $__('Verbose') ,
426 | 'default' => false,
427 | 'configuration' => array(
428 | 'desc' => $__('Enable verbose debugging')
429 | )
430 | )) ,
431 | );
432 | $sync_schedule = $this->config['sync_schedule']->ht['value'];
433 | }
434 |
435 | function pre_save(&$config, &$errors) {
436 | require_once ('class.AuthLdap.php');
437 | list($__, $_N) = self::translate();
438 | global $ost;
439 | if ($ost && !extension_loaded('ldap')) {
440 | $ost->setWarning($__('LDAP extension is not available'));
441 | $errors['err'] = $__('LDAP extension is not available. Please
442 | install or enable the `php-ldap` extension on your web
443 | server');
444 | return;
445 | }
446 |
447 | if ($this->getForm()->getField('sync-agents')->getValue() || $this->getForm()->getField('sync-users')->getValue()){
448 |
449 | /*$time_zone = db_result(db_query("SELECT value FROM `" . TABLE_PREFIX . "config` WHERE `key` = 'default_timezone'"));
450 | $schedule = LDAPMultiAuthentication::DateFromTimezone(strftime("%Y-%m-%d %H:%M", $sync_val->schedule) , 'UTC', $time_zone, 'F d Y g:i a');
451 | $sql = "SELECT value FROM `" . TABLE_PREFIX . "config` (`namespace`, `key`,`value`,`updated`)
452 | SELECT '".$this->config['sync_schedule']->ht['namespace']."', 'sync_data', '".$schedule.", `updated` = CURRENT_TIMESTAMP' FROM DUAL
453 | WHERE NOT EXISTS (SELECT * FROM `ost_config` WHERE `namespace`= '".$this->config['sync_schedule']->ht['namespace']."'
454 | AND `key`='sync_data' LIMIT 1)";
455 | db_result(db_query($sql));
456 | echo $sql;*/
457 | }
458 |
459 | if (empty($config['sync_mailto']) && $this->getForm()->getField('sync_reports')->getValue()) {
460 | $ost->setWarning($__('You need to add report email'));
461 | $errors['err'] = $__('Report email address cannot be blank');
462 | $this->getForm()
463 | ->getField('sync_mailto')
464 | ->addError($__("Report email address cannot be blank"));
465 | return;
466 | }
467 | if (!$config['basedn']) {
468 | if (!($servers = LDAPMultiAuthentication::connectcheck($config['servers'])))
469 | $this->getForm()
470 | ->getField('basedn')
471 | ->addError($__("No basedn specified. Example of DN attributes 'dc=foo,dc=com'."));
472 | }
473 | if (!$config['shortdomain']) {
474 | $this->getForm()
475 | ->getField('shortdomain')
476 | ->addError($__("No Domain Netbios names specified."));
477 | }
478 | else {
479 | if (!$config['servers']) $this->getForm()
480 | ->getField('servers')
481 | ->addError($__("No servers specified. Either specify a FQDN
482 | or ip address of servers"));
483 | else {
484 | $servers = array();
485 | foreach (preg_split('/\s+/', $config['servers']) as $server) {
486 | $server = trim($server);
487 | $servers[] = array(
488 | $server
489 | );
490 | }
491 | }
492 | }
493 |
494 | if ($sync_schedule !== $config['sync_schedule']){
495 | $this->checkschedule(true);
496 | }
497 |
498 | if ($config['reset_schedule']){
499 | $config['reset_schedule'] = false;
500 | //echo "test";
501 | }
502 |
503 | $ldapdata = array();
504 | foreach (preg_split('/;/', $config['basedn']) as $i => $dn) {
505 | $dn = trim($dn);
506 |
507 | $servers = preg_split('/\s+/', $config['servers']);
508 |
509 | $sd = preg_split('/;|,/', $config['shortdomain']);
510 |
511 | $ldapdata[] = array(
512 | 'dn' => $dn,
513 | 'sd' => $sd[$i],
514 | 'servers' => $servers[$i]
515 | );
516 | }
517 |
518 | foreach (LDAPMultiAuthentication::connectcheck($ldapdata) as $i => $connerror) {
519 | if (!$connerror['bool']) {
520 | $this->getForm()
521 | ->getField('servers')
522 | ->addError($connerror['msg']);
523 | $errors['err'] = $__('Unable to connect any listed LDAP servers');
524 | }
525 | }
526 |
527 | global $msg;
528 | if (!$errors) $msg = $__('LDAP configuration updated successfully');
529 | return !$errors;
530 | }
531 | }
532 | ?>
533 |
--------------------------------------------------------------------------------
/multi-ldap/sync_mldap.php:
--------------------------------------------------------------------------------
1 | config = self::mlconfig($instance);
25 | $this->config['backend'] = str_replace('.', '' , str_replace('instance', 'i' , str_replace('plugin', 'p' , $instance)));
26 | } else {
27 | $this->instance = $instance;
28 | }
29 | $this->LP = new LDAPMultiAuthentication($this->config);
30 | }
31 |
32 | //** Only used for AJAX
33 | static function _connectcheck($sync) {
34 |
35 | $conninfo = array();
36 | $ldapinfo = array();
37 |
38 | foreach (preg_split('/;/', $sync->config['basedn']) as $i => $dn) {
39 | $dn = trim($dn);
40 | $servers = $sync->config['servers'];
41 | $serversa = preg_split('/\s+/', $servers);
42 |
43 | $sd = $sync->config['shortdomain'];
44 | $sda = preg_split('/;|,/', $sd);
45 |
46 | $bind_dn = $sync->config['bind_dn'];
47 | $bind_dna = preg_split('/;/', $bind_dn) [$i];
48 |
49 | $bind_pw = $sync->config['bind_pw'];
50 | $bind_pwa = preg_split('/;|,/', $bind_pw) [$i];
51 |
52 | $ldapinfo[] = array(
53 | 'dn' => $dn,
54 | 'sd' => $sda[$i],
55 | 'servers' => trim($serversa[$i]) ,
56 | 'bind_dn' => trim($bind_dna) ,
57 | 'bind_pw' => trim($bind_pwa)
58 | );
59 | }
60 |
61 | foreach ($ldapinfo as $data) {
62 | $ldap = new AuthLdap();
63 | $ldap->serverType = 'ActiveDirectory';
64 | $ldap->server = preg_split('/;|,/', $data['servers']);
65 | $ldap->domain = $data['sd'];
66 | $ldap->dn = $data['dn'];
67 | $ldap->useSSL = $data['ssl'];
68 |
69 | if ($ldap->connect()) {
70 | $conninfo[] = array(
71 | 'bool' => "true",
72 | 'msg' => $data['sd'] . ' Connected OK!'
73 | );
74 | }
75 | else {
76 | $conninfo[] = array(
77 | false,
78 | $data['sd'] . " error:" . $ldap->ldapErrorCode . ": " . $ldap->ldapErrorText
79 | );
80 | }
81 | }
82 | echo json_encode($conninfo);
83 | }
84 |
85 | function user_list() {
86 | $userlist = array();
87 | $ldapinfo;
88 | global $ost;
89 | foreach (preg_split('/;/', $this->config['basedn']) as $i => $dn) {
90 | $dn = trim($dn);
91 | $servers = $this->config['servers'];
92 | $serversa = preg_split('/\s+/', $servers);
93 |
94 | $sd = $this->config['shortdomain'];
95 | $sda = preg_split('/;|,/', $sd);
96 |
97 | $bind_dn = $this->config['bind_dn'];
98 | $bind_dna = preg_split('/;/', $bind_dn) [$i];
99 |
100 | $bind_pw = $this->config['bind_pw'];
101 | $bind_pwa = preg_split('/;|,/', $bind_pw) [$i];
102 |
103 | $ldapinfo[] = array(
104 | 'dn' => $dn,
105 | 'sd' => $sda[$i],
106 | 'servers' => trim($serversa[$i]) ,
107 | 'bind_dn' => trim($bind_dna) ,
108 | 'bind_pw' => trim($bind_pwa)
109 | );
110 | }
111 | //$ost->logError('MLA sync ldapinfo', json_encode($ldapinfo), '');
112 | $combined_userlist = array();
113 |
114 | foreach ($ldapinfo as $data) {
115 | $ldap = new AuthLdap();
116 | $ldap->serverType = 'ActiveDirectory';
117 | $ldap->server = preg_split('/;|,/', $data['servers']);
118 | $ldap->dn = $data['dn'];
119 | $ldap->searchUser = $data['bind_dn'];
120 | $ldap->searchPassword = $data['bind_pw'];
121 |
122 | $sync_map = $this->sync_map($this->config['sync_map'])[1]; //Get attr Array
123 |
124 | if ($ldap->connect()) {
125 | $attr = str_getcsv(strtolower("samaccountname,mail,givenname,sn,whenchanged,useraccountcontrol,objectguid," . implode(',', $sync_map)) , ',');
126 | if ($userlist = $ldap->getUsers('', $attr, $this->config['sync_filter'])) {
127 | $combined_userlist = array_merge($combined_userlist, $userlist);
128 | }
129 | }
130 | else {
131 | $conninfo[] = array(
132 | false,
133 | $data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText
134 | );
135 | }
136 | }
137 | return $combined_userlist;
138 | }
139 |
140 | function sendAlertMsg($msg) {
141 | global $ost;
142 | try {
143 | $sync_mail = Email::lookup(key(json_decode($this->config['sync_mailfrom'])));
144 | $set = $sync_mail->send($this->config['sync_mailto'], 'MultiLdap Report', $msg);
145 | if(!$set)
146 | $ost->logError('MLA Email Error', 'Error Mail not sent to' . $sync_mail, '');
147 |
148 | } catch (Exception $e) {
149 | error_log("ERROR : " . $e->getMessage());
150 | $ost->logError('MLA Email Exception', 'Mail alert posting issue! :' . $e->getMessage(), true);
151 | }
152 | }
153 |
154 | function _userobject($values) {
155 | $object = new stdClass();
156 | foreach ($values as $key => $value) {
157 | if (is_string($value)) {
158 | $object->$key = $value;
159 | }
160 | else if (is_array($value)) {
161 | if ($value['count'] > 1) {
162 | unset($value['count']);
163 | $object->$key = $value;
164 | }
165 | else {
166 | $object->$key = $value[0];
167 | }
168 | }
169 | }
170 | return $object;
171 | }
172 |
173 | function sync_map($arr) {
174 | $list = explode(",", $arr);
175 | list( $ostmap, $ldapmap ) = array_reduce( $list,
176 | function( $arrlist, $item ) {
177 | list( $l, $r ) = explode( ':', $item );
178 | $arrlist[0][$l] = $arrlist[1][$r] = true;
179 | return $arrlist;
180 | }, array(array(), array()));
181 |
182 | $info_map[0] = array_keys(array_filter($ostmap));
183 | $info_map[1] = array_keys(array_filter($ldapmap));
184 | return $info_map;
185 | }
186 |
187 | function contains($obj, $str) {
188 | return strpos($obj, $str) !== false;
189 | }
190 |
191 | function changetime($when) {
192 | $time = gmmktime(substr($when, 8, 2) , substr($when, 10, 2) , substr($when, 12, 2) , substr($when, 4, 2) , substr($when, 6, 2) , substr($when, 0, 4));
193 | return date("Y-m-d H:i:s", $time); //2017-04-11 22:53:20 Tue Nov 30 1999, 12:00:00 AM
194 |
195 | }
196 |
197 | function formatmilliseconds($milliseconds) {
198 | $seconds = floor($milliseconds / 1000);
199 | $minutes = floor($seconds / 60);
200 | $hours = floor($minutes / 60);
201 | $milliseconds = $milliseconds % 1000;
202 | $seconds = $seconds % 60;
203 | $minutes = $minutes % 60;
204 |
205 | $format = '%u:%02u:%02u.%03u';
206 | $time = sprintf($format, $hours, $minutes, $seconds, $milliseconds);
207 | return rtrim($time, '0');
208 | }
209 |
210 | public function mlconfig($instance) {
211 | $configvalues;
212 | $sql = "SELECT `key`,`value` FROM " . TABLE_PREFIX . "config WHERE `namespace` = '" . $instance . "';";
213 | $result = db_query($sql);
214 |
215 | while ($row = db_fetch_array($result)) {
216 | $configvalues[$row['key']] = $row['value'];
217 | }
218 | return $configvalues;
219 | }
220 |
221 | //Sanitize Number and add the correct extention format.
222 | function sanitize_phone($phone, $international = false) {
223 | $phone = trim($phone);
224 | $phone = preg_replace('/\s+(#|x|ext(ension)?)\.?:?\s*(\d+)/', 'X\3', $phone);
225 | $us_number = preg_match('/^(\+\s*)?((0{0,2}1{1,3}[^\d]+)?\(?\s*([2-9][0-9]{2})\s*[^\d]?\s*([2-9][0-9]{2})\s*[^\d]?\s*([\d]{4})){1}(\s*([[:alpha:]#][^\d]*\d.*))?$/', $phone, $matches);
226 | if ($us_number) {
227 | return $matches[4] . '-' . $matches[5] . '-' . $matches[6] . (!empty($matches[8]) ? '' . $matches[8] : '');
228 | }
229 | if (!$international) {
230 | /* SET ERROR: The field must be a valid U.S. phone number (e.g. 888-888-8888) */
231 | return false;
232 | }
233 | $valid_number = preg_match('/^(\+\s*)?(?=([.,\s()-]*\d){8})([\d(][\d.,\s()-]*)([[:alpha:]#][^\d]*\d.*)?$/', $phone, $matches) && preg_match('/\d{2}/', $phone);
234 | if ($valid_number) {
235 | return trim($matches[1]) . trim($matches[3]) . (!empty($matches[4]) ? ' ' . $matches[4] : '');
236 | }
237 | /* SET ERROR: The field must be a valid phone number (e.g. 888-888-8888) */
238 | return false;
239 | }
240 |
241 | function update_username($guiduser, $id) {
242 | $sql = "UPDATE " . TABLE_PREFIX . "user_account SET username ='"
243 | . $guiduser ."' WHERE user_id = '" . $id . "'";
244 | $result = db_query($sql);
245 | return $result;
246 | }
247 |
248 | function update_users($users) {
249 | $i;
250 | foreach ($users as $user) {
251 | $i++;
252 |
253 | $user_id = $user->user_id;
254 | $cn = $user->cn;
255 | $mail = $user->mail;
256 | //$office = $user->physicaldeliveryofficename;
257 | $phone = $user->telephonenumber;
258 | $full_name = $user->givenname . ' ' . $user->sn;
259 | $user_name = $user->samaccountname;
260 | $mobile = $user->mobile;
261 | $objectguid = trim($user->objectguid);
262 | $logString = "User information from LDAP: ";
263 | $synckey = $this->sync_info[$user->user_id];
264 |
265 | //Get a list of attributes
266 | $attrs = $this->sync_map($this->config['sync_map'])[1];
267 |
268 | foreach ($attrs as $attr) {
269 | $logString = $logString . "'" . $attr . "'=" . $user->$attr . " ; ";
270 | }
271 |
272 | //Update account if user is disabled to "Administratively Locked" ... enabled users are renabled.
273 | if ($user->useraccountcontrol == 514 || $user->useraccountcontrol == 66050 && $synckey['status'] == 1) {
274 | $lock_user_sql = db_query("UPDATE " . TABLE_PREFIX . "user_account
275 | SET status = '3' WHERE user_id = " . $user->user_id);
276 | $this->log_report['status'] = "(Acct Disabled)";
277 | }
278 | else {
279 | if ($synckey['status'] == 3) {
280 | $lock_user_sql = db_query("UPDATE " . TABLE_PREFIX . "user_account
281 | SET status = '1' WHERE user_id = " . $user->user_id);
282 | $this->log_report['status'] = "(Acct Enabled)";
283 | }
284 | }
285 |
286 | //Update Email
287 | if ($synckey['mail'] != $user->mail) {
288 | $result = db_query("UPDATE " . TABLE_PREFIX . "user_email
289 | SET address = \"" . $user->mail . "\" WHERE " . TABLE_PREFIX . "user_email.user_id = " . $user->user_id);
290 | $changed_attr[] = "email";
291 | }
292 | // Update LDAP Attributes from AD for the osTicket user configured in config.php
293 | preg_match_all('/(.*?):\s?(.*?)(,|$)/', strtolower($this->config['sync_map']) , $matches);
294 | $ost_contact_info_fields = array_combine(array_map('trim', $matches[1]) , $matches[2]);
295 |
296 | foreach ($ost_contact_info_fields as $ost_contact_field => $ost_contact_info_field_ldapattr) {
297 |
298 | $current_field = $ost_contact_field;
299 | $check_duplicate = "SELECT " . TABLE_PREFIX . "form_field.id, " . TABLE_PREFIX . "form_field.name, " . TABLE_PREFIX . "form_entry_values.value FROM " . TABLE_PREFIX . "user
300 | LEFT JOIN " . TABLE_PREFIX . "user_account on " . TABLE_PREFIX . "user.id=" . TABLE_PREFIX . "user_account.user_id
301 | LEFT JOIN " . TABLE_PREFIX . "form_entry on " . TABLE_PREFIX . "user.id=" . TABLE_PREFIX . "form_entry.object_id
302 | LEFT JOIN " . TABLE_PREFIX . "form on " . TABLE_PREFIX . "form.id=" . TABLE_PREFIX . "form_entry.form_id
303 | LEFT JOIN " . TABLE_PREFIX . "form_entry_values on " . TABLE_PREFIX . "form_entry.id=" . TABLE_PREFIX . "form_entry_values.entry_id
304 | LEFT JOIN " . TABLE_PREFIX . "form_field on " . TABLE_PREFIX . "form_entry_values.field_id=" . TABLE_PREFIX . "form_field.id
305 | WHERE " . TABLE_PREFIX . "user_account.user_id =" . $user->user_id . " AND " . TABLE_PREFIX . "form.id = '1' AND " . TABLE_PREFIX . "form_field.name = '$current_field';";
306 | //error_log($check_duplicate);
307 | if ($current_field == 'phone' || $current_field == 'mobile') {
308 | $current_ldap_value = $this->sanitize_phone($user->$ost_contact_info_field_ldapattr);
309 | }
310 | else {
311 | $current_ldap_value = $user->$ost_contact_info_field_ldapattr;
312 | }
313 |
314 | // Check for empty or Duplicates values
315 | $res = db_query($check_duplicate);
316 | if (db_num_rows($res) == 1) {
317 | while ($row = db_fetch_array($res)) {
318 | if ($row['name'] == 'phone' || $row['name'] == 'mobile') {
319 | $sql_value = $this->sanitize_phone($row['value']);
320 | }
321 | else {
322 | $sql_value = $row['value'];
323 | }
324 |
325 | if (empty($row['value'])) $sql_value = NULL;
326 | }
327 | }
328 | else {
329 | $sql_value = NULL;
330 | }
331 |
332 | if ($ost_contact_field == 'name') {
333 | $sql_value = db_result(db_query("SELECT name FROM " . TABLE_PREFIX . "user
334 | WHERE id = " . $user->user_id));
335 | }
336 |
337 | if ($sql_value != $current_ldap_value) {
338 | //if (!empty($current_ldap_value)) {
339 | if ($ost_contact_field != 'name') {
340 | $update_ostuser_sql = "INSERT INTO " . TABLE_PREFIX . "form_entry_values(entry_id, field_id, value)
341 | values (
342 | (SELECT id FROM `" . TABLE_PREFIX . "form_entry` WHERE `object_id` = (SELECT user_id FROM `" . TABLE_PREFIX . "user_account` WHERE `user_id` = '" . $user->user_id . "') AND form_id = 1),
343 | (SELECT id FROM `" . TABLE_PREFIX . "form_field` WHERE `name` = '" . $ost_contact_field . "' AND form_id = 1), \"" . $current_ldap_value . "\")
344 | ON DUPLICATE KEY UPDATE value = \"" . $current_ldap_value . "\";";
345 | }
346 | else {
347 | $default_user = db_result(db_query("SELECT address FROM `" . TABLE_PREFIX . "user_email` WHERE `user_id` = " . $user->user_id));
348 | if ($default_user != $full_name) $update_ostuser_sql = "UPDATE " . TABLE_PREFIX . "user
349 | SET name = \"" . $full_name . "\" WHERE id = " . $user->user_id;
350 | }
351 |
352 | // update changed field
353 | //error_log($update_ostuser_sql);
354 | $result = db_query($update_ostuser_sql);
355 | if (!$result) {
356 | $this->log_report['status'] .= " (Error: [$ost_contact_field])";
357 | $changed_attr = NULL;
358 | continue;
359 | }
360 | //update the field that was changed
361 | $changed_attr[] = $ost_contact_field;
362 | //}
363 | }
364 | }
365 |
366 | if (!empty($changed_attr)) {
367 | $this->log_report['status'] .= "(Updated)";
368 | } else {
369 | $this->log_report['status'] .= "(No Changes)";
370 | }
371 |
372 | //Update user When Change time.
373 | if ($synckey['updated'] != $this->changetime($user->whenchanged) || !$this->contains($this->log_report['status'], 'Error')) $result = db_query("UPDATE " . TABLE_PREFIX . "ldap_sync SET updated =
374 | \"" . $this->changetime($user->whenchanged) . "\" WHERE id = " . $user->user_id);
375 |
376 | $this->log_report['body'] .= "
377 | | $i |
378 | " . $user->samaccountname . " |
379 | " . (!empty($changed_attr) ? implode(", ", $changed_attr) : '0') . " |
380 | " . $this->log_report['status'] . " |
";
381 |
382 | $changed_attr = NULL;
383 | $this->log_report['status'] = NULL;
384 | }
385 | }
386 |
387 | //Check users and send alert
388 | function check_users() {
389 | global $ost;
390 | ini_set('memory_limit', '1024M');
391 | $sync_time_start = microtime(true);
392 | $list = $this->user_list();
393 | $log_header = ("(" . count($list) . ") total ldap entries.
");
394 | $this->sync_results['totalldap'] = count($list);
395 |
396 | $log_table = '
413 |
414 |
415 |
416 | | # |
417 | Username |
418 | Updated |
419 | Status |
420 |
';
421 | //Clean User Array
422 | $ad_users = array();
423 | foreach ($list as $val) {
424 | $ad_users[$val['mail'][0]] = $this->_userobject($val);
425 |
426 | } ksort($ad_users);
427 |
428 | //Create guid Array
429 | $guid_users = array();
430 | foreach ($list as $val) {
431 | $guid_users[$val['objectguid']] = $this->_userobject($val);
432 | } ksort($guid_users);
433 |
434 | //***************Sync Agents************************
435 | // Check if agents shall be updated with LDAP info
436 | if ($this->config['sync-agents']) {
437 | // Select all osTicket Agents
438 | $qry_ostagents = "SELECT " . TABLE_PREFIX . "staff.username, " . TABLE_PREFIX . "staff.email, " . TABLE_PREFIX . "staff.phone, " . TABLE_PREFIX . "staff.phone_ext as ext, " . TABLE_PREFIX . "staff.mobile FROM " . TABLE_PREFIX . "staff WHERE " . TABLE_PREFIX . "staff.username IS NOT NULL";
439 |
440 | $res_ostagents = db_query($qry_ostagents);
441 |
442 | // Update Header - Total of osTicket agents
443 | $log_header .= ("(" . db_num_rows($res_ostagents) . ") total agents
");
444 | $this->sync_results['totalagents'] = db_num_rows($res_ostagents);
445 |
446 | // Go thru every osTicket agent and modify every osTicket agents information
447 | foreach (db_assoc_array($res_ostagents, MYSQLI_ASSOC) as $sql_ostagents) {
448 | $updates = array();
449 | $key = $sql_ostagents['email']; //Key value for sorting
450 | // Check if osTicket agent is also an LDAP user
451 | if ($sql_ostagents['email'] == $ad_users[$key]->mail) {
452 | $split_num = $ad_users[$key]->telephonenumber;
453 | $chk_number = preg_match('/\D*\(?(\d{3})?\)?\D*(\d{3})\D*(\d{4})\D*(\d{1,8})?/', $split_num, $matches);
454 | if ($chk_number) {
455 | $phone = (!empty($matches[1]) ? $matches[1] . '-' : '') . $matches[2] . '-' . $matches[3];
456 | $ext = (!empty($matches[4]) ? $matches[4] : '');
457 | }
458 |
459 | //Update telephone and mobile number for agents and extention if any
460 | if ($ad_users[$key]->telephonenumber != $sql_ostagents['phone']) {
461 | $qry_update_ostagent_telephonenumber = "UPDATE " . TABLE_PREFIX . "staff
462 | SET phone ='" . ($phone) . "', phone_ext = '$ext'
463 | WHERE " . TABLE_PREFIX . "staff.username ='" . $ad_users[$key]->samaccountname . "'";
464 | $updates[] = 'phone';
465 |
466 | $result = db_query($qry_update_ostagent_telephonenumber);
467 | }
468 |
469 | // Mobile Number
470 | if ($ad_users[$key]->mobile != $sql_ostagents['mobile']) {
471 | $qry_update_ostagent_mobile = "UPDATE " . TABLE_PREFIX . "staff
472 | SET mobile='" . $this->sanitize_phone($ad_users[$key]->mobile) . "'
473 | WHERE (" . TABLE_PREFIX . "staff.username='" . $ad_users[$key]->samaccountname . "')";
474 | $updates[] = 'mobile';
475 | $result = db_query($qry_update_ostagent_mobile);
476 |
477 | if (!$result) $this->log_report['status'] = "(Error)";
478 | }
479 |
480 | if (!empty($changed_attr)) $this->log_report['agent'] .= "
481 | | #agent |
482 | " . $ad_users[$key]->samaccountname . " |
483 | " . db_affected_rows() . " |
484 | " . $this->log_report['status'] . " |
";
485 | //contains($result, "ERROR")) echo 'style="background-color: beige;"'echo $result; < / td >
486 |
487 | }
488 | }
489 | }
490 |
491 | //***************Sync Users************************
492 | if ($this->config['sync-users']) {
493 | //Cleanup ID's with empty objectguid
494 | db_query("DELETE FROM " . TABLE_PREFIX . "ldap_sync WHERE guid IS NULL OR guid = ''");
495 |
496 | //Remove objectguid that is not in the ost_user table
497 | db_query("DELETE FROM `" . TABLE_PREFIX . "ldap_sync` WHERE NOT EXISTS (SELECT * FROM `" . TABLE_PREFIX . "user` WHERE id = " . TABLE_PREFIX . "ldap_sync.id);");
498 |
499 | //Update Global Array;
500 | $sql = "SELECT " . TABLE_PREFIX . "user.id as user_id, " . TABLE_PREFIX . "user_email.id as email_id," . TABLE_PREFIX . "user.name, " . TABLE_PREFIX . "user_email.address as mail, " . TABLE_PREFIX . "user_account.username , " . TABLE_PREFIX . "user_account.backend , " . TABLE_PREFIX . "user_account.status , " . TABLE_PREFIX . "ldap_sync.guid, " . TABLE_PREFIX . "ldap_sync.updated
501 | FROM " . TABLE_PREFIX . "user
502 | LEFT JOIN " . TABLE_PREFIX . "user_email on " . TABLE_PREFIX . "user.id=" . TABLE_PREFIX . "user_email.user_id
503 | LEFT JOIN " . TABLE_PREFIX . "user_account on " . TABLE_PREFIX . "user.id = " . TABLE_PREFIX . "user_account.user_id
504 | LEFT JOIN " . TABLE_PREFIX . "ldap_sync on " . TABLE_PREFIX . "user.id = " . TABLE_PREFIX . "ldap_sync.id WHERE " . TABLE_PREFIX . "user_account.backend = 'mldap.client". $this->config['backend'] ."';";
505 | $sync_array = db_query($sql);
506 | $g;
507 |
508 | foreach (db_assoc_array($sync_array, MYSQLI_ASSOC) as $sync) {
509 | $uid = $sync->config["user_id"];
510 | unset($sync->config["user_id"]);
511 | //Update any username changes
512 | if ($guiduser = $guid_users[$sync->config["guid"]]) {
513 | if (!empty($sync->config['username']))
514 | if ($guiduser->samaccountname !== $sync->config['username']){
515 | $this->update_username($guiduser->samaccountname, $uid);
516 | //$log_header .= "GUID: " . $guiduser->samaccountname ."-" . $sync->config['username'] . "";
517 | $g++;
518 | }
519 | }
520 | $this->sync_info[$uid] = $sync;
521 | }
522 |
523 | //Query only users that have no guid.
524 | $qry_ostusers = db_query("SELECT " . TABLE_PREFIX . "user.id as user_id,
525 | " . TABLE_PREFIX . "user_email.id as email_id," . TABLE_PREFIX . "user.name, " . TABLE_PREFIX . "user_email.address as mail
526 | FROM " . TABLE_PREFIX . "user LEFT JOIN " . TABLE_PREFIX . "user_email on " . TABLE_PREFIX . "user.id=" . TABLE_PREFIX . "user_email.user_id
527 | WHERE NOT EXISTS (select " . TABLE_PREFIX . "ldap_sync.id from " . TABLE_PREFIX . "ldap_sync
528 | WHERE user_id = " . TABLE_PREFIX . "ldap_sync.id);");
529 |
530 | // Go thru every osTicket user and add them to the sync table if a match is found
531 | foreach (db_assoc_array($qry_ostusers, MYSQLI_ASSOC) as $sql_ostusers) {
532 | $key = trim(strtolower($sql_ostusers['mail'])); //Key value for matching users
533 | $user_ldap = $ad_users[$key];
534 |
535 | if (strtolower($key == $user_ldap->mail)) {
536 | //Lets check users and add them to the guid table if a match is found
537 | $result = db_query("SELECT id FROM " . TABLE_PREFIX . "ldap_sync WHERE id = '" . $sql_ostusers['user_id'] . "'");
538 | if (db_num_rows($result) == 0 && $key == $ad_users[$key]->mail) {
539 | db_query("INSERT INTO " . TABLE_PREFIX . "ldap_sync(id, guid, updated)
540 | values ('" . $sql_ostusers['user_id'] . "', '" . $ad_users[$key]->objectguid . "', '" . date('Y-m-d H:i:s') . "')
541 | ON DUPLICATE KEY UPDATE id = \"" . $sql_ostusers['user_id'] . "\", guid = \"" . $ad_users[$key]->objectguid . "\", updated = \"" . date('Y-m-d H:i:s') . "\";");
542 | }
543 | } else {
544 | if (!empty($this->config['debug-choice']) && $this->config['debug-choice'])
545 | $this->log_report['debug']['notfound'][] = $sql_ostusers;
546 | }
547 | }
548 |
549 | //Go through and create accounts for new guest users verified in LDAP
550 | $sql_guests = "SELECT id, guid
551 | FROM " . TABLE_PREFIX . "ldap_sync
552 | WHERE NOT EXISTS (SELECT user_id FROM " . TABLE_PREFIX . "user_account WHERE user_id = " . TABLE_PREFIX . "ldap_sync.id);";
553 |
554 | $default_timezone = db_result(db_query("SELECT value FROM `" . TABLE_PREFIX . "config` WHERE `key` = 'default_timezone'"));
555 | $default_lang = db_result(db_query("SELECT value FROM `" . TABLE_PREFIX . "config` WHERE `key` = 'system_language'"));
556 |
557 | $qry_guests = db_query($sql_guests);
558 | foreach (db_assoc_array($qry_guests, MYSQLI_ASSOC) as $guests) {
559 | $key = $guests['guid'];
560 | db_query("INSERT INTO " . TABLE_PREFIX . "user_account(user_id, status, timezone, username, backend, extra, registered)
561 | values ('" . $guests['id'] . "',1, '$default_timezone', '" . $guid_users[$key]->samaccountname . "','mldap.client". $this->config['backend'] ."', '{\"browser_lang\":\"$default_lang\"}', '" . date('Y-m-d H:i:s') . "');");
562 | }
563 |
564 | //Update all users based on the ObjectID
565 | $sql_ostguid = db_query("SELECT * FROM `" . TABLE_PREFIX . "ldap_sync` WHERE guid IS NOT NULL");
566 |
567 | $updateusers;
568 | $sql_guid = db_assoc_array($sql_ostguid, MYSQLI_ASSOC);
569 |
570 | foreach ($sql_guid as $guid) {
571 | $guidkey = $guid['guid']; //objectguid for matching users.
572 | //Get UserID based on objectguid
573 | if (!empty($guid_users["$guidkey"])){
574 | $guid_users["$guidkey"]->user_id = $guid['id'];
575 | if ($_REQUEST['full'] || $this->config['sync_full']) {
576 | $updateusers[] = $guid_users["$guidkey"];
577 | }
578 | elseif ($guid['updated'] !== $this->changetime($guid_users["$guidkey"]->whenchanged)) {
579 | $updateusers[] = $guid_users["$guidkey"];
580 | }
581 | }
582 | }
583 |
584 | $log_header = ("(" . db_num_rows($qry_ostusers) . ') users not in ldap. ');
585 | $log_header = ("(" . (!empty($g) ? $g : '0') . ') users renamed. ');
586 | $log_header = ("(" . (is_countable($this->sync_info) ? count($this->sync_info) : '0') . ') total users. ');
587 | $log_header = ("(" . (is_countable($updateusers) ? count($updateusers) : '0'). ') users synced. ');
588 | $this->sync_results['updatedusers'] = (is_countable($updateusers) ? count($updateusers) : '0');
589 | $this->sync_results['totalusers'] = (is_countable($this->sync_info) ? count($this->sync_info) : '0');
590 |
591 | //update all user attributes.
592 | $this->update_users($updateusers);
593 | }
594 | //$this->logger('debug', '', , false);
595 | //execution time of the script
596 | $execution_time = $this->formatmilliseconds(number_format(microtime(true) - $sync_time_start, 3) * 1000);
597 | $log_footer = ' |
598 |
599 | Total Execution Time: ' . $execution_time . ' secs';
600 |
601 | $this->sync_results['executetime'] = $execution_time;
602 | $msg = $log_header . $log_table . $this->log_report['agent'] . $this->log_report['body'] . $log_footer . $log_debug;
603 |
604 | if ($this->sync_results['updatedusers'] >= 1 && $this->config['sync_reports']){
605 | $this->sendAlertMsg($msg);
606 | }
607 |
608 | return $this->sync_results;
609 | }
610 | }
611 |
612 | if ($_REQUEST['sync']){
613 | //$sync = new SyncLDAPMultiClass(explode('plugin.', $_REQUEST['data'])[1]);
614 | //$results = $sync->check_users();
615 | //return json_encode(array("info" => "done", "result" => $results));
616 | }
617 | if ($_REQUEST['check']){
618 | $sync = new SyncLDAPMultiClass($_REQUEST['instance']);
619 | $results = $sync->_connectcheck($sync);
620 | return json_encode(array("info" => "done", "result" => $results));
621 | }
622 |
623 | ?>
624 |
--------------------------------------------------------------------------------
/multi-ldap/class.AuthLdap.php:
--------------------------------------------------------------------------------
1 |
30 | * Added ObjectSid and ObjectGUID functions
31 | * Uploaded to Github
32 | *
33 | * version 1.0, 10.13.2015, Joseph Philbert
34 | * Updated functions for better integration with Active Directory
35 | * Removed several PHP warnings for unset variables
36 | * Added Timeout updated code to support LDAP PROTOCOL_VERSION # fixed a bind bug
37 | *
38 | * version 0.2, 11.04.2003, Michael Joseph
39 | * - Added switches and workarounds for Active Directory integration
40 | * - Change documentation to phpdoc style (http://phpdocu.sourceforge.net)
41 | * - Added a constructor
42 | * - Added an attribute array parameter to the getUsers method
43 | * Original Author - Mark Round, April 2002 - http://www.markround.com/unix
44 | */
45 |
46 | class AuthLdap {
47 |
48 | // 1.1 Public properties -----------------------------------------------------
49 | /**
50 | * Array of server IP address or hostnames
51 | */
52 | var $server;
53 | /**
54 | * The base DN (e.g. "dc=foo,dc=com")
55 | */
56 | var $dn;
57 | /**
58 | * the directory server, currently supports iPlanet and Active Directory
59 | */
60 | var $serverType;
61 | /**
62 | * Active Directory authenticates using user@domain
63 | */
64 | var $domain;
65 | /**
66 | * The user to authenticate with when searching
67 | * Active Directory doesn't support anonymous access
68 | */
69 | var $searchUser;
70 | /**
71 | * The password to authenticate with when searching
72 | * Active Directory doesn't support anonymous access
73 | */
74 | var $searchPassword;
75 | /**
76 | * Where the user records are kept
77 | */
78 | var $people;
79 | /**
80 | * Where the group definitions are kept
81 | */
82 | var $groups;
83 | /**
84 | * The last error code returned by the LDAP server
85 | */
86 | var $ldapErrorCode;
87 | /**
88 | * Text of the error message
89 | */
90 | var $ldapErrorText;
91 |
92 | // 1.2 Private properties ----------------------------------------------------
93 | /**
94 | * The internal LDAP connection handle
95 | */
96 | var $connection;
97 | /**
98 | * Result of any connections etc.
99 | */
100 | var $result;
101 | /**
102 | * Set default ini memory Limit.
103 | */
104 | var $memory_limit;
105 |
106 | /**
107 | * Constructor- creates a new instance of the authentication class
108 | *
109 | * @param string the ldap server to connect to
110 | * @param string the base dn
111 | * @param string the server type- current supports iPlanet and ActiveDirectory
112 | * @param string the domain to use when authenticating against Active Directory
113 | * @param string the username to authenticate with when searching if anonymous binding is not supported
114 | * @param string the password to authenticate with when searching if anonymous binding is not supported
115 | */
116 | function AuthLdap ($sLdapServer = "", $sBaseDN = "", $sServerType = "", $sDomain = "", $searchUser = "", $searchPassword = "") {
117 | @$this->server = array($sLdapServer);
118 | @$this->dn = $sBaseDN;
119 | @$this->serverType = $sServerType;
120 | $this->domain = $sDomain;
121 | $this->searchUser = $searchUser;
122 | $this->searchPassword = $searchPassword;
123 | $this->memory_limit = '1024M';
124 | }
125 |
126 | // 2.1 Connection handling methods -------------------------------------------
127 |
128 | /**
129 | * 2.1.1 : Connects to the server. Just creates a connection which is used
130 | * in all later access to the LDAP server. If it can't connect and bind
131 | * anonymously, it creates an error code of -1. Returns true if connected,
132 | * false if failed. Takes an array of possible servers - if one doesn't work,
133 | * it tries the next and so on.
134 | */
135 | function connect() {
136 | foreach ($this->server as $key => $host) {
137 | $this->connection = ldap_connect( $host);
138 | ldap_set_option ($this->connection, LDAP_OPT_REFERRALS, 0) or die('Unable to set LDAP opt referrals');
139 | ldap_set_option($this->connection, LDAP_OPT_PROTOCOL_VERSION, 3) or die('Unable to set LDAP protocol version');
140 | ldap_set_option($this->connection, LDAP_OPT_TIMELIMIT, 5) or die('Timelimit reached');
141 | ldap_set_option($this->connection, LDAP_OPT_NETWORK_TIMEOUT, 5) or die('Network timed out');
142 |
143 | if ( $this->connection) {
144 | if ($this->serverType == "ActiveDirectory") {
145 | return true;
146 | } else {
147 | // Connected, now try binding anonymously
148 | $this->result=@ldap_bind( $this->connection);
149 | }
150 | return true;
151 | }
152 | }
153 |
154 | $this->ldapErrorCode = -1;
155 | $this->ldapErrorText = "Unable to connect to any server";
156 | return false;
157 | }
158 |
159 | /**
160 | * 2.1.2 : Simply closes the connection set up earlier.
161 | * Returns true if OK, false if there was an error.
162 | */
163 | function close() {
164 | if ( !@ldap_close( $this->connection)) {
165 | $this->ldapErrorCode = ldap_errno( $this->connection);
166 | $this->ldapErrorText = ldap_error( $this->connection);
167 | return false;
168 | } else {
169 | return true;
170 | }
171 | }
172 |
173 | /**
174 | * 2.1.3 : Anonymously binds to the connection. After this is done,
175 | * queries and searches can be done - but read-only.
176 | */
177 | function bind() {
178 | if ( !$this->result=@ldap_bind( $this->connection)) {
179 | $this->ldapErrorCode = ldap_errno( $this->connection);
180 | $this->ldapErrorText = ldap_error( $this->connection);
181 | return false;
182 | } else {
183 | return true;
184 | }
185 | }
186 |
187 |
188 |
189 | /**
190 | * 2.1.4 : Binds as an authenticated user, which usually allows for write
191 | * access. The FULL dn must be passed. For a directory manager, this is
192 | * "cn=Directory Manager" under iPlanet. For a user, it will be something
193 | * like "uid=jbloggs,ou=People,dc=foo,dc=com".
194 | */
195 | function authBind( $bindDn,$pass) {
196 | if ( !$this->result = @ldap_bind( $this->connection,$bindDn,$pass)) {
197 | $this->ldapErrorCode = ldap_errno( $this->connection);
198 | $this->ldapErrorText = ldap_error( $this->connection);
199 | return false;
200 | } else {
201 | return true;
202 | }
203 | }
204 |
205 | // 2.2 Password methods ------------------------------------------------------
206 |
207 | /**
208 | * 2.2.1 : Checks a username and password - does this by logging on to the
209 | * server as a user - specified in the DN. There are several reasons why
210 | * this login could fail - these are listed below.
211 | */
212 | function checkPass( $uname,$pass) {
213 | /* Construct the full DN, eg:-
214 | ** "uid=username, ou=People, dc=orgname,dc=com"
215 | */
216 | if ($this->serverType == "ActiveDirectory") {
217 | $checkDn = "$uname@$this->domain";
218 | } else {
219 | $checkDn = $this->getUserIdentifier() . "=$uname, " . $this->setDn(true);
220 | }
221 | // Try and connect...
222 | $this->result = @ldap_bind( $this->connection,$checkDn,$pass);
223 | if ( $this->result) {
224 | // Connected OK - login credentials are fine!
225 | return true;
226 | } else {
227 | /* Login failed. Return false, together with the error code and text from
228 | ** the LDAP server. The common error codes and reasons are listed below :
229 | ** (for iPlanet, other servers may differ)
230 | ** 19 - Account locked out (too many invalid login attempts)
231 | ** 32 - User does not exist
232 | ** 49 - Wrong password
233 | ** 53 - Account inactive (manually locked out by administrator)
234 | */
235 | $this->ldapErrorCode = ldap_errno( $this->connection);
236 | $this->ldapErrorText = ldap_error( $this->connection);
237 | return false;
238 | }
239 | }
240 |
241 |
242 | /**
243 | * 2.2.2 : Allows a password to be changed. Note that on most LDAP servers,
244 | * a new ACL must be defined giving users the ability to modify their
245 | * password attribute (userPassword). Otherwise this will fail.
246 | * supports password history
247 | */
248 | function changePass( $uname,$oldPass,$newPass, $history=false) {
249 | // builds the appropriate dn, based on whether $this->people and/or $this->group is set
250 | if ($this->serverType == "ActiveDirectory") {
251 | $checkDn = "$uname@$this->domain";
252 | } else {
253 | $checkDn = $this->getUserIdentifier() . "=$uname, " . $this->setDn(true);
254 | }
255 | $this->result = @ldap_bind( $this->connection,$checkDn,$oldPass);
256 |
257 | if ($history && $this->result) {
258 | $ctrl1 = array(
259 | // LDAP_SERVER_POLICY_HINTS_OID for Windows 2012 and above
260 | "oid" => "1.2.840.113556.1.4.2239",
261 | "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1));
262 |
263 | $ctrl2 = array(
264 | // LDAP_SERVER_POLICY_HINTS_DEPRECATED_OID for Windows 2008 R2 SP1 and above
265 | "oid" => "1.2.840.113556.1.4.2066",
266 | "value" => sprintf("%c%c%c%c%c", 48, 3, 2, 1, 1));
267 |
268 | if (!ldap_set_option($this->connection, LDAP_OPT_SERVER_CONTROLS, array($ctrl1, $ctrl2))) {
269 | //error_log("ERROR: Failed to set server controls");
270 | $this->ldapErrorCode = ldap_errno( $this->connection);
271 | $this->ldapErrorText = ldap_error( $this->connection);
272 | return false;
273 | }
274 |
275 | $this->result = ldap_mod_replace($this->connection, $checkDn, $entry);
276 | if ( $this->result) {
277 | // Change went OK
278 | return true;
279 | } else {
280 | // Couldn't change password...
281 | $this->ldapErrorCode = ldap_errno( $this->connection);
282 | $this->ldapErrorText = ldap_error( $this->connection);
283 | return false;
284 | }
285 |
286 | } else {
287 |
288 | if ( $this->result) {
289 | // Connected OK - Now modify the password...
290 | $info["userPassword"] = $newPass;
291 | $this->result = @ldap_modify( $this->connection, $checkDn, $info);
292 | if ( $this->result) {
293 | // Change went OK
294 | return true;
295 | } else {
296 | // Couldn't change password...
297 | $this->ldapErrorCode = ldap_errno( $this->connection);
298 | $this->ldapErrorText = ldap_error( $this->connection);
299 | return false;
300 | }
301 | } else {
302 | // Login failed - see checkPass method for common error codes
303 | $this->ldapErrorCode = ldap_errno( $this->connection);
304 | $this->ldapErrorText = ldap_error( $this->connection);
305 | return false;
306 | }
307 | }
308 | }
309 |
310 |
311 | /**
312 | * 2.2.3 : Returns days until the password will expire.
313 | * We have to explicitly state this is what we want returned from the
314 | * LDAP server - by default, it will only send back the "basic"
315 | * attributes.
316 | */
317 | function checkPassAge ( $uname) {
318 |
319 | $results[0] = "passwordexpirationtime";
320 | // builds the appropriate dn, based on whether $this->people and/or $this->group is set
321 | $checkDn = $this->setDn(true);
322 | $this->result = @ldap_search( $this->connection,$checkDn,$this->getUserIdentifier()."=$uname",$results);
323 |
324 | if ( !$info=@ldap_get_entries( $this->connection, $this->result)) {
325 | $this->ldapErrorCode = ldap_errno( $this->connection);
326 | $this->ldapErrorText = ldap_error( $this->connection);
327 | return false;
328 | } else {
329 | /* Now work out how many days remaining....
330 | ** Yes, it's very verbose code but I left it like this so it can easily
331 | ** be modified for your needs.
332 | */
333 | $date = $info[0]["passwordexpirationtime"][0];
334 | $year = substr( $date,0,4);
335 | $month = substr( $date,4,2);
336 | $day = substr( $date,6,2);
337 | $hour = substr( $date,8,2);
338 | $min = substr( $date,10,2);
339 | $sec = substr( $date,12,2);
340 |
341 | $timestamp = mktime( $hour,$min,$sec,$month,$day,$year);
342 | $today = mktime();
343 | $diff = $timestamp-$today;
344 | return round( ( ( ( $diff/60)/60)/24));
345 | }
346 | }
347 |
348 | // 2.3 Group methods ---------------------------------------------------------
349 |
350 | /**
351 | * 2.3.1 : Checks to see if a user is in a given group. If so, it returns
352 | * true, and returns false if the user isn't in the group, or any other
353 | * error occurs (eg:- no such user, no group by that name etc.)
354 | */
355 | function checkGroup ( $uname,$group) {
356 | // builds the appropriate dn, based on whether $this->people and/or $this->group is set
357 | $checkDn = $this->setDn(true);
358 |
359 | if (!$this->authBind($this->searchUser, $this->searchPassword))
360 | return false;
361 |
362 | // We need to search for the group in order to get it's entry.
363 | $this->result = ldap_search($this->connection, $checkDn, "(&(sAMAccountName=$uname))", array('memberOf' ));
364 | $entry = $this->cleanEntry(ldap_get_entries($this->connection, $this->result))->memberof;
365 |
366 | foreach ($entry as $val){
367 | $grp = str_replace('CN=', '', explode(",",$val)[0]);
368 | if (strtolower($grp) == strtolower($group))
369 | return true;
370 | }
371 |
372 | if ( !$entry) {
373 | $this->ldapErrorCode = ldap_errno( $this->connection);
374 | $this->ldapErrorText = ldap_error( 'user not in group');
375 | return false; // Couldn't find user in group...
376 | }
377 | }
378 |
379 | /* Groups the user is a member of
380 | *
381 | * @param string $username The username to query
382 | * @param bool $recursive Recursive list of groups
383 | * @param bool $isGUID Is the username passed a GUID or a samAccountName
384 | * @return array
385 | */
386 | public function groups($username, $recursive = NULL, $isGUID = false)
387 | {
388 | if ($username === NULL) { return false; }
389 | if ($recursive === NULL) { $recursive = $this->adldap->getRecursiveGroups(); } // Use the default option if they haven't set it
390 | if (!$this->adldap->getLdapBind()) { return false; }
391 |
392 | // Search the directory for their information
393 | $info = @$this->info($username, array("memberof", "primarygroupid"), $isGUID);
394 | $groups = $this->adldap->utilities()->niceNames($info[0]["memberof"]); // Presuming the entry returned is our guy (unique usernames)
395 |
396 | if ($recursive === true){
397 | foreach ($groups as $id => $groupName){
398 | $extraGroups = $this->adldap->group()->recursiveGroups($groupName);
399 | $groups = array_merge($groups, $extraGroups);
400 | }
401 | }
402 | //Remove duplicate groups
403 | return array_unique ($groups);
404 | }
405 |
406 | // 2.4 Attribute methods -----------------------------------------------------
407 | /**
408 | * 2.4.1 : Returns an array containing a set of attribute values.
409 | * For most searches, this will just be one row, but sometimes multiple
410 | * results are returned (eg:- multiple email addresses)
411 | */
412 | function getAttribute ( $uname,$attribute, $raw = false) {
413 | // builds the appropriate dn, based on whether $this->people and/or $this->group is set
414 | $checkDn = $this->setDn( true);
415 | $results = array($attribute);
416 |
417 | // We need to search for this user in order to get their entry.
418 | $this->result = ldap_search( $this->connection,$checkDn,"(&(sAMAccountName=$uname))",$results);
419 | // Only one entry should ever be returned (no user will have the same uid)
420 | $entry = ldap_get_entries($this->connection, $this->result);
421 |
422 | if ( !$entry) {
423 | $this->ldapErrorCode = -1;
424 | $this->ldapErrorText = "Couldn't find attribute";
425 | return false; // Couldn't find the user...
426 | } else {
427 | if (!$raw) {
428 | $value = $entry[0][$attribute][0];
429 | } else {
430 | $value = $entry[0];
431 | }
432 | }
433 |
434 | // Return attribute.
435 | return $value;
436 | }
437 |
438 | /**
439 | * 2.4.2 : Allows an attribute value to be set.
440 | * This can only usually be done after an authenticated bind as a
441 | * directory manager - otherwise, read/write access will not be granted.
442 | */
443 | function setAttribute( $uname, $attributes) {
444 | $userDn = $this->userDn($uname);
445 | if ($userDn === false) return false;
446 |
447 | if (!$attributes) return (false);
448 |
449 | //Check for NULL values
450 | foreach($attributes as $key => $attribute) {
451 | // Change attribute
452 | if(empty(trim($attribute))){
453 | $this->result = ldap_modify( $this->connection, $userDn, array($key=>array()));
454 | } else {
455 | $this->result = ldap_modify( $this->connection, $userDn, array($key=>$attribute));
456 | }
457 | if (is_array($attribute)){
458 | $this->result = ldap_modify( $this->connection, $userDn, array($key=>$attribute));
459 | }
460 | if ($this->result == false){
461 | $this->ldapErrorCode = ldap_errno( $this->connection);
462 | $this->ldapErrorText = "Could not modify attribute";
463 | return false;
464 | }
465 | }
466 |
467 | return true;
468 | }
469 |
470 | // 2.5 User methods ----------------------------------------------------------
471 | /**
472 | * 2.5.1 : Returns an array containing a details of users, sorted by
473 | * username. The search criteria is a standard LDAP query - * returns all
474 | * users. The $attributeArray variable contains the required user detail field names
475 | */
476 | function getUsers( $search, $attributeArray, $filter = null) {
477 | // builds the appropriate dn, based on whether $this->people and/or $this->group is set
478 | $checkDn = $this->setDn( true);
479 |
480 | // Perform the search and get the entry handles
481 |
482 | // if the directory is AD, then bind first with the search user first if it fails return false
483 | if ($this->serverType == "ActiveDirectory") {
484 | if (!$this->authBind($this->searchUser, $this->searchPassword))
485 | return false;
486 | }
487 |
488 | // Checks for custom filter replaces all {q} with $search string.
489 | if ($filter == null) {
490 | $filter = $this->getUserIdentifier() . "=$search*";
491 | } else {
492 | $filter = str_replace("{q}", "$search", $filter);
493 | }
494 |
495 | $this->result = @ldap_search( $this->connection, $checkDn, $filter, $attributeArray,0,0);
496 | if (empty($this->result)) {
497 | $this->ldapErrorCode = 0;
498 | $this->ldapErrorText = "(" . ldap_error($this->connection).") No users found matching search criteria ".$search;
499 | return false;
500 | }
501 |
502 | $info = @ldap_get_entries( $this->connection, $this->result);
503 |
504 | //Change memory limit for large AD domains
505 | $oldLimit = ini_get( 'memory_limit');
506 | ini_set( 'memory_limit', $this->memory_limit);
507 |
508 | for( $i = 0; $i < $info["count"]; $i++) {
509 | // Get the username, and create an array indexed by it...
510 | // Modify these as you see fit.
511 | $uname = $info[$i][$this->getUserIdentifier()][0];
512 | // add to the array for each attribute in my list
513 | //echo json_encode($attributeArray);
514 | for ( $j = 0; $j < count( $attributeArray); $j++) {
515 | if (strtolower($attributeArray[$j]) == "dn") {
516 | $userslist["$i"]["$attributeArray[$j]"] = $info[$i][strtolower($attributeArray[$j])];
517 | } else if (strtolower($attributeArray[$j]) == "objectsid") {
518 | $userslist["$i"]["$attributeArray[$j]"] = $this->SIDtoString($info[$i][strtolower($attributeArray[$j])][0]);
519 | } else if (strtolower($attributeArray[$j]) == "objectguid") {
520 | $userslist["$i"]["$attributeArray[$j]"] = $this->GUIDtoString($info[$i][strtolower($attributeArray[$j])][0]);
521 | } else {
522 | //Check if value is array
523 | if (is_array($info[$i][strtolower($attributeArray[$j])])){
524 | $userslist["$i"]["$attributeArray[$j]"] = $info[$i][strtolower($attributeArray[$j])];
525 | } else {
526 | $userslist["$i"]["$attributeArray[$j]"] = $info[$i][strtolower($attributeArray[$j])][0];
527 | }
528 | }
529 | }
530 | }
531 | ini_set( 'memory_limit', $oldLimit );
532 | if ( !@is_array( $userslist)) {
533 | /* Sort into alphabetical order. If this fails, it's because there
534 | ** were no results returned (array is empty) - so just return false.
535 | */
536 | $this->ldapErrorCode = -1;
537 | $this->ldapErrorText = "(" . ldap_error($this->connection).") No users found matching search criteria ".$search;
538 | return false;
539 | }
540 | return $userslist;
541 | }
542 |
543 | function pagedUsers ($attributeArray, $filter = null) {
544 | $checkDn = $this->setDn( true);
545 | $filter = "(&(objectClass=user)(objectCategory=person)(sn=*))";
546 | // enable pagination with a page size of 100.
547 | $pageSize = 100;
548 |
549 | $x = '';
550 |
551 | do {
552 | ldap_control_paged_result($this->connection, $pageSize, true, $x);
553 |
554 | $this->result = ldap_search( $this->connection, $checkDn, $filter, $attributeArray,0,0);
555 | $entries = ldap_get_entries($this->connection, $this->result);
556 | echo json_encode($entries);
557 | if(!empty($entries)){
558 | for ($i = 0; $i < $entries["count"]; $i++) {
559 | $data['usersLdap'][] = array(
560 | 'name' => $entries[$i]["cn"][0],
561 | 'username' => $entries[$i]["userprincipalname"][0]
562 | );
563 | }
564 | }
565 | ldap_control_paged_result_response($this->connection, $this->result, $x);
566 | }
567 |
568 | while($x !== null && $x != '');
569 |
570 | return $data;
571 | }
572 |
573 | // 2.6 helper methods
574 | function SIDtoString($ADsid)
575 | {
576 | $sid = "S-";
577 | //$ADguid = $info[0]['objectguid'][0];
578 | $sidinhex = str_split(bin2hex($ADsid), 2);
579 | // Byte 0 = Revision Level
580 | $sid = $sid.hexdec($sidinhex[0])."-";
581 | // Byte 1-7 = 48 Bit Authority
582 | $sid = $sid.hexdec($sidinhex[6].$sidinhex[5].$sidinhex[4].$sidinhex[3].$sidinhex[2].$sidinhex[1]);
583 | // Byte 8 count of sub authorities - Get number of sub-authorities
584 | $subauths = hexdec($sidinhex[7]);
585 | //Loop through Sub Authorities
586 | for($i = 0; $i < $subauths; $i++) {
587 | $start = 8 + (4 * $i);
588 | // X amount of 32Bit (4 Byte) Sub Authorities
589 | $sid = $sid."-".hexdec($sidinhex[$start+3].$sidinhex[$start+2].$sidinhex[$start+1].$sidinhex[$start]);
590 | }
591 | return $sid;
592 | }
593 |
594 | function GUIDtoString($guid)
595 | {
596 | $hex_guid = unpack( "H*hex", $guid );
597 | $hex = $hex_guid["hex"];
598 |
599 | $hex1 = substr( $hex, -26, 2 ) . substr( $hex, -28, 2 ) . substr( $hex, -30, 2 ) . substr( $hex, -32, 2 );
600 | $hex2 = substr( $hex, -22, 2 ) . substr( $hex, -24, 2 );
601 | $hex3 = substr( $hex, -18, 2 ) . substr( $hex, -20, 2 );
602 | $hex4 = substr( $hex, -16, 4 );
603 | $hex5 = substr( $hex, -12, 12 );
604 |
605 | $guid = $hex1 . "-" . $hex2 . "-" . $hex3 . "-" . $hex4 . "-" . $hex5;
606 |
607 | return $guid;
608 | }
609 | /**
610 | * Sets and returns the appropriate dn, based on whether there
611 | * are values in $this->people and $this->groups.
612 | *
613 | * @param boolean specifies whether to build a groups dn or a people dn
614 | * @return string if true ou=$this->people,$this->dn, else ou=$this->groups,$this->dn
615 | */
616 | function setDn($peopleOrGroups) {
617 |
618 | if ($peopleOrGroups) {
619 | if ( isset($this->people) && (strlen($this->people) > 0) ) {
620 | $checkDn = "ou=" .$this->people. ", " .$this->dn;
621 | }
622 | } else {
623 | if ( isset($this->groups) && (strlen($this->groups) > 0) ) {
624 | $checkDn = "ou=" .$this->groups. ", " .$this->dn;
625 | }
626 | }
627 |
628 | if ( !isset($checkDn) ) {
629 | $checkDn = $this->dn;
630 | }
631 | return $checkDn;
632 | }
633 |
634 | /**
635 | * Take an LDAP query and return the clean names, without all the LDAP prefixes (eg. CN, DN)
636 | *
637 | * @param array $groups
638 | * @return array
639 | */
640 | public function cleanNames($groups)
641 | {
642 |
643 | $groupArray = array();
644 | for ($i=0; $i<$groups["count"]; $i++){ // For each group
645 | $line = $groups[$i];
646 |
647 | if (strlen($line)>0) {
648 | // More presumptions, they're all prefixed with CN=
649 | // so we ditch the first three characters and the group
650 | // name goes up to the first comma
651 | $bits=explode(",", $line);
652 | $groupArray[] = substr($bits[0], 3, (strlen($bits[0])-3));
653 | }
654 | }
655 | return $groupArray;
656 | }
657 |
658 | /**
659 | * Obtain the user's distinguished name based on their userid
660 | *
661 | *
662 | * @param string $username The username
663 | * @return string
664 | */
665 | public function userDn($uname)
666 | {
667 | $user = $this->getAttribute($uname, "cn", true);
668 | if ($user["dn"] === NULL) {
669 | return false;
670 | }
671 | return $user["dn"];
672 | }
673 |
674 | /**
675 | * Convert LDAP resulting array to clean entries array with attributes and values
676 | *
677 | * @param $resultArray
678 | * @return array
679 | */
680 | function cleanEntry($values) {
681 | $object = new stdClass();
682 | foreach ($values[0] as $key => $value) {
683 | if(preg_match('/(? 0 || $key == 'count')
684 | continue;
685 | if ($key == 'dn') {
686 | $object->$key = $value;
687 | } else if ($value['count'] > 1){
688 | unset($value['count']);
689 | $object->$key = $value;
690 | } else {
691 | $object->$key = $value[0];
692 | }
693 | }
694 | return $object;
695 | }
696 |
697 | /**
698 | * Get the RootDSE properties from a domain controller
699 | *
700 | * @param array $attributes The attributes you wish to query e.g. defaultnamingcontext
701 | * @return array
702 | */
703 | function getRoot($attributes = array("*", "+")) {
704 | // if (!$this->bind){ return (false); }
705 |
706 | $sr = @ldap_read($this->connection, $this->dn, 'objectClass=*', $attributes);
707 | $entries = @ldap_get_entries($this->connection, $sr);
708 | return $entries;
709 | }
710 |
711 | /**
712 | * Returns the correct user identifier to use, based on the ldap server type
713 | */
714 | function getUserIdentifier() {
715 | if ($this->serverType == "ActiveDirectory") {
716 | return "samaccountname";
717 | } else {
718 | return "uid";
719 | }
720 | }
721 | } // End of class
722 | ?>
723 |
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/multi-ldap/auth.php:
--------------------------------------------------------------------------------
1 | getInstance($id))){
23 | return $i;
24 | }
25 | return $this->getInstances()->first();
26 | }
27 |
28 | public function __construct($id) {
29 | parent::__construct($id);
30 | }
31 |
32 | function bootstrap() {
33 | $this->plugininstance();
34 | if ($this->firstRun()) {
35 | if (!$this->configureFirstRun()) {
36 | return false;
37 | }
38 | }
39 | else if ($this->needUpgrade()) {}
40 |
41 | $this->loadSync();
42 | $config = $this->getConfig();
43 | $id = $this->id;
44 | Signal::connect('cron', array(
45 | $this,
46 | 'onCronProcessed'
47 | ));
48 |
49 | if ($config->get('multiauth-staff')) StaffAuthenticationBackend::register(new StaffLDAPMultiAuthentication($config));
50 | if ($config->get('multiauth-client')) UserAuthenticationBackend::register(new ClientLDAPMultiAuthentication($config));
51 | }
52 |
53 | //Checks osticket instances if any
54 | function plugininstance() {
55 | self::$pluginInstance = self::getPluginInstance(null);
56 | $this->instance = new stdClass();
57 | $ins = self::$pluginInstance->id;
58 | $plugin_id = self::$pluginInstance->plugin_id;
59 | $this->instance->plugin = "plugin.".$plugin_id.".instance.".$ins;
60 | $this->instance->backend = ".p".$plugin_id."i".$ins;
61 | $this->instance->staff = ".p".$plugin_id."i".$ins;
62 | }
63 |
64 | function loadSync() {
65 | $sql = "SELECT * FROM " . PLUGIN_TABLE . " WHERE `isactive`=1 AND `id`='" . $this->id . "'";
66 | if (db_num_rows(db_query($sql))) {
67 | if (!file_exists(ROOT_DIR.'scp/sync_mldap.php') || (md5_file(MULTI_PLUGIN_ROOT.'sync_mldap.php') != @md5_file(ROOT_DIR.'scp/sync_mldap.php'))){
68 | $this->sync_copy();
69 | }
70 | include_once (ROOT_DIR.'scp/sync_mldap.php');
71 | }
72 | }
73 |
74 | function millisecsBetween($dateOne, $dateTwo, $abs = true) {
75 | $func = $abs ? 'abs' : 'intval';
76 | return $func(strtotime($dateOne) - strtotime($dateTwo)) * 1000;
77 | }
78 |
79 | static function DateFromTimezone($date, $gmt, $timezone, $format) {
80 | $date = new DateTime($date, new DateTimeZone($gmt));
81 | $date->setTimezone(new DateTimeZone($timezone));
82 | return $date->format($format);
83 | }
84 |
85 | function onCronProcessed() {
86 | global $ost;
87 | if (!$this->getConfig(self::$pluginInstance)->get('sync-users') || !$this->getConfig(self::$pluginInstance)->get('sync-agents')){
88 | //return;
89 | }
90 | $instance = $this->getConfig(self::$pluginInstance)->config['sync_data']->ht['namespace'];
91 | $this->time_zone = db_result(db_query("SELECT value FROM `" . TABLE_PREFIX . "config` WHERE `key` = 'default_timezone'"));
92 | $this->logger('warning', 'MLA instance - '.$this->getConfig()->get('shortdomain'), $instance, true);
93 |
94 | $sync_info = json_decode(db_fetch_row(db_query('SELECT value FROM ' . TABLE_PREFIX . 'config WHERE namespace = "' . $instance . '" AND `key` = "sync_data";'))[0]);
95 |
96 | $this->logger('warning', "MLA sync info", ($sync_info), true);
97 |
98 | $schedule = $this->DateFromTimezone(strftime("%Y-%m-%d %H:%M:%S", $sync_info->schedule) , 'UTC', $this->time_zone, 'F j, Y, H:i');
99 | $lastrun = $this->DateFromTimezone(strftime("%Y-%m-%d %H:%M:%S", $sync_info->lastrun) , 'UTC', $this->time_zone, 'F j, Y, H:i');
100 | $this->executed = time();
101 | $date = new DateTime('now', new DateTimeZone($this->time_zone));
102 |
103 | $this->crontime = $this->millisecsBetween($schedule, $lastrun, false) / 1000 / 60;
104 |
105 | $this->sync_cron($this->crontime);
106 | $this->loadSync();
107 |
108 | $allowaction = $this->allowAction(); //check to see if Action is allowed.
109 | $this->logger('warning', 'MLA Allow Check - '.$this->getConfig()->get('shortdomain'), 'allow action : '. var_export($allowaction, 1), true);
110 |
111 | if ($allowaction) {
112 | //Load Sync info
113 | $sync = new SyncLDAPMultiClass($instance);
114 | if ($this->getConfig()->get('sync-users') || $this->getConfig()->get('sync-agents')) {
115 | $excu = $this->DateFromTimezone(strftime("%Y-%m-%d %H:%M", $this->lastExec) , 'UTC', $this->time_zone, 'F d Y g:i a');
116 | $nextexcu = $this->DateFromTimezone(strftime("%Y-%m-%d %H:%M", $this->nextExec) , 'UTC', $this->time_zone, 'F d Y g:i a');
117 | $results = $sync->check_users();
118 | $this->logger('warning', 'MLA Check Users', ($results), true);
119 |
120 | if (empty($results)) {
121 | $this->logger('warning', 'MLA LDAP Sync', 'Sync executed on (' . ($excu) . ') next execution in (' . $nextexcu . ')', true);
122 | } else {
123 | $this->logger('warning', 'MLA LDAP Sync', 'Sync executed on (' . ($excu) . ')
124 | Next execution in (' . $nextexcu . ')' . "
125 | Total ldapusers: (" . $results['totalldap'] . ")
126 | Total agents: (" . $results['totalagents'] . ")
127 | Total Updated Users: (" . $results['updatedusers'] . ")
128 | Execute Time: (" . $results['executetime'] . ")", true);
129 | }
130 | }
131 | }
132 | }
133 |
134 | //Sync cron Logic
135 | function sync_cron($minDelay = false) {
136 | //outputs both keys in array
137 | $sync_info = db_assoc_array(db_query('SELECT * FROM ' . TABLE_PREFIX . 'config
138 | WHERE namespace = "' . $this->instance->plugin . '" AND `key` = "sync_schedule" OR `key` = "sync_data";') , MYSQLI_ASSOC);
139 | $this->minDelay = NULL;
140 | if ($minDelay) $this->minDelay = $minDelay;
141 |
142 | $output;
143 | foreach ($sync_info as $info) {
144 | if ($info['key'] == 'sync_schedule') {
145 | $output['schedule'] = $info['value'];
146 | $output['format'] = $info['value'];
147 | }
148 |
149 | if ($info['key'] == 'sync_data') {
150 | $val = json_decode($info['value']);
151 | $output['lastrun'] = $this->DateFromTimezone(strftime("%Y-%m-%d %H:%M", $val->lastrun) , 'UTC', $this->time_zone, 'Y-m-d H:i');
152 | $output['schedule'] = $val->schedule;
153 | $output['updated'] = $info['updated'];
154 | }
155 | }
156 |
157 | $this->cront = $output;
158 | $this->lastExec = 0; // it will contain the UNIXTIME of the last action
159 | $this->nextExec = 0; // it will contain the UNIXTIME of the next action
160 | $this->secToExec = 0; // it will contain the time in seconds until of the next action
161 | if (isset($this->cront)) $this->check = true;
162 | else {
163 | if (!$this->updateLastrun(time())) $this->check = false;
164 | else {
165 | $this->check = true;
166 | }
167 | }
168 | }
169 |
170 | function allowAction() {
171 | $now = time();
172 | if ($this->check) $FT = $this->getEventUpdatedTime();
173 | if ($FT) {
174 | $nextExec = $FT + ($this->minDelay * 60) - $now;
175 | if ($nextExec < 0) {
176 | if (!$this->updateLastrun($now)) return false;
177 | else {
178 | $this->lastExec = $now;
179 | $this->nextExec = $now + ($this->minDelay * 60);
180 | $this->secToExec = $this->minDelay * 60;
181 | $this->updateSchedule();
182 | return true;
183 | }
184 | }
185 | else {
186 | $this->lastExec = $FT;
187 | $this->nextExec = $FT + $nextExec;
188 | $this->secToExec = $nextExec;
189 | return false;
190 | }
191 | }
192 | else return false;
193 | }
194 |
195 | //last modification time.
196 | function getEventUpdatedTime() {
197 | $updated = db_fetch_row(db_query('SELECT UNIX_TIMESTAMP(updated) as updated FROM ' . TABLE_PREFIX . 'config
198 | WHERE namespace = "' . $this->instance->plugin . '" AND `key` = "sync_data";')) [0];
199 | if (isset($updated)) {
200 | $FT = $updated;
201 | }
202 | else {
203 | $FT = false;
204 | }
205 | return $FT;
206 | }
207 |
208 | function sync_data($key, $val) {
209 | $json_str = db_fetch_row(db_query('SELECT value FROM ' . TABLE_PREFIX . 'config
210 | WHERE namespace = "' . $this->instance->plugin . '" AND `key` = "sync_data";')) [0];
211 | $data = @json_decode($json_str, true);
212 | if (!is_object($data)) return json_encode(array(
213 | 'schedule' => strtotime($this->cront['format']) ,
214 | 'lastrun' => time()
215 | ));
216 | $data->$key = $val;
217 | return json_encode($data);
218 | }
219 |
220 | function updateLastrun($tme) {
221 | global $ost;
222 | $data = $this->sync_data('lastrun', $tme);
223 | $sql = 'UPDATE `' . TABLE_PREFIX . 'config` SET `value` = \'' . ($data) . '\' , updated = CURRENT_TIMESTAMP
224 | WHERE `key` = "sync_data" AND `namespace` = "' . $this->instance->plugin . '";';
225 | if (self::getConfig()->get('debug-verbose'))
226 | $ost->logDebug('MLA updateLastrun', ($sql), false);
227 | error_log($sql);
228 | return db_query($sql);
229 | }
230 |
231 | function updateSchedule() {
232 | global $ost;
233 | $data = $this->sync_data('schedule', $this->cront['schedule']);
234 | $sql = 'UPDATE `' . TABLE_PREFIX . 'config` SET `value` = \'' . ($data) . '\', updated = CURRENT_TIMESTAMP
235 | WHERE `key` = "sync_data" AND `namespace` = "' . $this->instance->plugin . '";';
236 | if (self::getConfig()->get('debug-verbose'))
237 | $ost->logDebug('MLA updateSchedule', ($sql), false);
238 | $result = db_query($sql);
239 | return $result;
240 | }
241 |
242 | /**
243 | * Checks if this is the first run of our plugin.
244 | *
245 | * @return boolean
246 | */
247 | function firstRun() {
248 | $sql = "SHOW TABLES LIKE '". TABLE_PREFIX ."ldap_sync'";
249 | $res = db_query($sql);
250 | $rows = db_num_rows($res);
251 |
252 | if ($rows <= 0) {
253 | $this->sync_copy();
254 | $this->createSyncTables();
255 | }
256 | return (db_num_rows($res) == 0);
257 | }
258 |
259 | /**
260 | * Checks to see if plug-in application needs to be upgraded
261 | * @return boolean
262 | */
263 | function needUpgrade() {
264 | $checkclicent = "SELECT id, user_id FROM " . TABLE_PREFIX ."user_account as ua WHERE `backend` LIKE CONCAT('ldap.client', '%') AND ua.user_id IN (SELECT id FROM " . TABLE_PREFIX ."ldap_sync WHERE ua.user_id = id)";
265 |
266 | if (db_num_rows(db_query($checkclicent))){
267 | $this->startUpgrade();
268 | }
269 |
270 | if (!($res = db_query("SELECT version FROM " . PLUGIN_TABLE . " WHERE `id` = '" . $this->id . "';"))) {
271 | return true;
272 | }
273 | else {
274 | $ht = db_fetch_array($res);
275 | if (floatval($ht['version']) < floatval($this->info['version'])) {
276 | //Lets up date the version of the plug-in in old version of OSticket.
277 | $versql = "UPDATE `" . TABLE_PREFIX . "plugin` SET `version` = '" . $this->info['version'] . "' , name = '" . $this->info['name'] . "', installed = CURRENT_TIMESTAMP
278 | WHERE `id` = '" . $this->id . "';";
279 | if (db_num_rows(db_query($versql))) {
280 | $this->logger('warning', 'Update MLA_Version ', 'Version updated to: ' .$this->info['version'], false, true);
281 | return true;
282 | }
283 | }
284 | }
285 | return false;
286 | }
287 |
288 | /**
289 | * Start upgrade of needed tasks for plug-in application
290 | * @return boolean
291 | */
292 | function startUpgrade() {
293 | $clientsql = "UPDATE " . TABLE_PREFIX ."user_account as ua SET `backend` = 'mldap.client".$this->instance->backend."' WHERE `backend` LIKE CONCAT('ldap.client', '%')
294 | AND ua.user_id IN (SELECT Id FROM " . TABLE_PREFIX ."ldap_sync WHERE ua.user_id = id)";
295 |
296 | $staffsql = "UPDATE `" . TABLE_PREFIX ."staff as st SET `backend` = 'mldap".$this->instance->backend."' WHERE `backend` LIKE CONCAT('ldap', '%') AND staff.staff_Id IN
297 | (SELECT Id FROM " . TABLE_PREFIX ."ldap_sync WHERE Id = st.staff_Id);";
298 |
299 | //Update User table for new plug-in instance information.
300 | if (db_query($clientsql))
301 | $this->logger('warning', 'MLA User backend updated', 'Rows affected: ' . db_affected_rows(), false, true);
302 | if (db_query($staffsql))
303 | $this->logger('warning', 'MLA Staff backend updated', 'Rows affected: ' . db_affected_rows(), false, true);
304 | }
305 |
306 | /**
307 | * Necessary functionality to configure first run of plug-in application
308 | */
309 | function configureFirstRun() {
310 | $this->logger('warning', 'MLA_FirstRun', 'config');
311 | return $this->sync_copy();
312 | }
313 |
314 | /**
315 | * Kicks off database installation scripts
316 | *
317 | * @return boolean
318 | */
319 | function createSyncTables() {
320 | db_query("DROP TABLE IF EXISTS " . TABLE_PREFIX . "ldap_sync");
321 | $sqlsync = ("CREATE TABLE " . TABLE_PREFIX . "ldap_sync (
322 | `id` bigint(20) unsigned NOT NULL,
323 | `guid` varchar(40) NOT NULL,
324 | `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
325 | PRIMARY KEY (`id`)
326 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
327 | $result = db_query($sqlsync);
328 | if ($result) {
329 | $this->logger('warning', 'MLA SyncTables', "SUCCESS: ".$sqlsync, false, true);
330 | return true;
331 | } else {
332 | $this->logger('warning', 'MLA SyncTables', "FAILED: ".$sqlsync, false, true);
333 | }
334 | return false;
335 | }
336 |
337 | /**
338 | * Uninstall hook.
339 | *
340 | * @param type $errors
341 | * @return boolean
342 | */
343 | function pre_uninstall(&$errors) {
344 | db_query("DROP TABLE IF EXISTS " . TABLE_PREFIX . "ldap_sync");
345 | $this->logger('warning', 'MLA-uninstall', "removed ldap_sync Table". $errors, false, true);
346 | $result = unlink(ROOT_DIR.'scp/sync_mldap.php');
347 | if ($result) {
348 | return true;
349 | } else {
350 | $this->logger('warning', 'MLA-uninstal', "File removal error " . $errors, false, true);
351 | return false;
352 | }
353 | }
354 |
355 | /**
356 | * Check if this is array or object
357 | *
358 | */
359 | function isRealObject($arrOrObject) {
360 | if (is_object($arrOrObject))
361 | return true;
362 | $keys = array_keys($arrOrObject);
363 | return implode('', $keys) != implode(range(0, count($keys)-1));
364 | }
365 |
366 | /**
367 | * Write information to system LOG
368 | *
369 | */
370 | function logger($priority, $title, $message, $verbose = false, $force = false) {
371 |
372 | if (!is_scalar($message)) {
373 | $message = json_encode($message, JSON_PARTIAL_OUTPUT_ON_ERROR);
374 | }
375 |
376 | //We are providing only 3 levels of logs. Windows style.
377 | switch ($priority) {
378 | case "error":
379 | case LOG_EMERG:
380 | case LOG_ALERT:
381 | case LOG_CRIT:
382 | case LOG_ERR:
383 | $level = 0; //Error
384 |
385 | break;
386 | case "warning":
387 | case LOG_WARN:
388 | case LOG_WARNING:
389 | $level = 1; //Warning
390 |
391 | break;
392 | case "debug":
393 | case LOG_NOTICE:
394 | case LOG_INFO:
395 | case LOG_DEBUG:
396 | default:
397 | $level = 2; //Debug
398 |
399 | }
400 | $loglevel = array('Error', 'Warning','Debug'
401 | );
402 | //Save log based on system log level settings.
403 | $sql = 'INSERT INTO ' . TABLE_PREFIX . "syslog" . ' SET created=NOW(), updated=NOW() ' . ',title=' . db_input(Format::sanitize($title, true)) . ',log_type=' . db_input($loglevel[$level]) . ',log=\'' . $message . '\',ip_address=' . db_input($_SERVER['REMOTE_ADDR']);
404 |
405 | if ($force) {
406 | db_query($sql, false);
407 | return;
408 | }
409 |
410 | switch (self::getConfig()->get('debug-verbose')){
411 | case true:
412 | db_query($sql, false);
413 | break;
414 | case false:
415 | if (self::getConfig()->get('debug-choice') && !$verbose) {
416 | db_query($sql, false);
417 | }
418 | break;
419 | }
420 | }
421 |
422 | function sync_copy() {
423 | $pgfile = MULTI_PLUGIN_ROOT.'sync_mldap.php';
424 | $scpfile = ROOT_DIR.'scp/sync_mldap.php';
425 | if (!file_exists($scpfile)){
426 | if(!copy($pgfile,$scpfile)){
427 | $this->logger('error', 'MLA File Copy (failed)', "Copying new file '" . $pgfile . "' to SCP folder failed",false, true);
428 | return false;
429 | } else {
430 | $this->logger('info', 'MLA File (success)', "Copying new file '" . $pgfile . "' to SCP folder successful",false, true);
431 | return true;
432 | }
433 | } else if (md5_file($pgfile) != @md5_file($scpfile)){
434 | unlink($scpfile);
435 | if(!copy($pgfile,$scpfile)){
436 | $this->logger('error', 'MLA File Updated (failed)', "Replacing file '" . $pgfile . "' to SCP folder failed",false, true);
437 | return false;
438 | } else {
439 | $this->logger('info', 'MLA File Updated (success)', "Replacing file '" . $pgfile . "' to SCP folder successful",false, true);
440 | return true;
441 | }
442 | }
443 | return false;
444 | }
445 | }
446 |
447 | class LDAPMultiAuthentication {
448 |
449 | var $config;
450 | var $type = 'staff';
451 |
452 | function __construct($config, $type = 'staff') {
453 | $this->config = $config;
454 | $this->type = $type;
455 | }
456 |
457 | static function DateFromTimezone($date, $gmt, $timezone, $format) {
458 | $date = new DateTime($date, new DateTimeZone($gmt));
459 | $date->setTimezone(new DateTimeZone($timezone));
460 | return $date->format($format);
461 | }
462 |
463 | function getConfig() {
464 | return $this->config;
465 | }
466 |
467 | function getServers() {
468 | if (!empty($servers = $this->getConfig()
469 | ->get('servers'))) {
470 | return preg_split('/\s+/', $servers);
471 | }
472 | }
473 |
474 | function getDomain() {
475 | if (!empty($shortdomain = $this->getConfig()
476 | ->get('shortdomain'))) {
477 | return preg_split(',', $shortdomain);
478 | }
479 | }
480 |
481 | function multi_re_key(&$array, $old_keys, $new_keys) {
482 | if (!is_array($array)) {
483 | ($array == "") ? $array = array() : false;
484 | return $array;
485 | }
486 | foreach ($array as & $arr) {
487 | if (is_array($old_keys)) {
488 | foreach ($new_keys as $k => $new_key) {
489 | (isset($old_keys[$k])) ? true : $old_keys[$k] = NULL;
490 | $arr[$new_key] = (isset($arr[$old_keys[$k]]) ? $arr[$old_keys[$k]] : null);
491 | unset($arr[$old_keys[$k]]);
492 | }
493 | }
494 | else {
495 | $arr[$new_keys] = (isset($arr[$old_keys]) ? $arr[$old_keys] : null);
496 | unset($arr[$old_keys]);
497 | }
498 | }
499 | return $array;
500 | }
501 |
502 | function keymap($arr) {
503 | $keys = ($this->multi_re_key($arr, array(
504 | 'samaccountname',
505 | 'givenname',
506 | 'sn',
507 | 'displayname',
508 | 'mail',
509 | 'telephonenumber',
510 | 'mobile',
511 | 'distinguishedname'
512 | ) , array(
513 | 'username',
514 | 'first',
515 | 'last',
516 | 'full',
517 | 'email',
518 | 'phone',
519 | 'mobile',
520 | 'dn'
521 | )));
522 | return $keys;
523 | }
524 |
525 | function adschema() {
526 | return array(
527 | 'samaccountname',
528 | 'givenname',
529 | 'sn',
530 | 'displayname',
531 | 'mail',
532 | 'telephonenumber',
533 | 'mobile',
534 | 'distinguishedname'
535 | );
536 | }
537 |
538 | function setConnection() {
539 | $ldap = new AuthLdap();
540 | $ldap->serverType = 'ActiveDirectory';
541 | return $ldap;
542 | }
543 | function ldapenv() {
544 | $ldap = new AuthLdap();
545 | $ldap->serverType = 'ActiveDirectory';
546 | $ldap->server = preg_split('/;|,/', $data['servers']);
547 | $ldap->dn = $data['dn'];
548 | return $ldap;
549 | }
550 |
551 | static function connectcheck($ldapinfo) {
552 | $conninfo = array();
553 | foreach ($ldapinfo as $data) {
554 | $ldap = new AuthLdap();
555 | $ldap->serverType = 'ActiveDirectory';
556 | $ldap->server = preg_split('/;|,/', $data['servers']);
557 | $ldap->domain = $data['sd'];
558 | $ldap->dn = $data['dn'];
559 | $ldap->ssl = $data['ssl'];
560 |
561 | if ($ldap->connect()) {
562 | $conninfo[] = array(
563 | 'bool' => true,
564 | 'msg' => $data['sd'] . ' Connected OK!'
565 | );
566 | }
567 | else {
568 | $conninfo[] = array(
569 | false,
570 | $data['sd'] . " error:" . $ldap->ldapErrorCode . ": " . $ldap->ldapErrorText
571 | );
572 | }
573 | }
574 | return $conninfo;
575 | }
576 |
577 | function flatarray($values) {
578 | global $ost;
579 | $object = array();
580 | foreach ($values as $k => $items) {
581 | foreach ($items as $key => $item) {
582 | $object[$k][$key] = $item["0"];
583 | }
584 | }
585 | //$ost->logWarning('object', json_encode($object), false);
586 | return $object; //remove [0] inclulde multiple arrays
587 | }
588 |
589 | function ldapinfo() {
590 | $ldapinfo;
591 | foreach (preg_split('/;/', $this->getConfig($this->instance->ins)
592 | ->get('basedn')) as $i => $dn) {
593 | $dn = trim($dn);
594 | $servers = $this->getConfig($this->instance->ins)
595 | ->get('servers');
596 | $serversa = preg_split('/\s+/', $servers);
597 |
598 | $sd = $this->getConfig($this->instance->ins)
599 | ->get('shortdomain');
600 | $sda = preg_split('/;|,/', $sd);
601 |
602 | $bind_dn = $this->getConfig($this->instance->ins)
603 | ->get('bind_dn');
604 | $bind_dna = preg_split('/;/', $bind_dn) [$i];
605 |
606 | $bind_pw = $this->getConfig($this->instance->ins)
607 | ->get('bind_pw');
608 | $bind_pwa = preg_split('/;|,/', $bind_pw) [$i];
609 |
610 | $ssl = $this->getConfig($this->instance->ins)->get('tls');
611 |
612 | $ldapinfo[] = array(
613 | 'dn' => $dn,
614 | 'sd' => $sda[$i],
615 | 'servers' => trim($serversa[$i]) ,
616 | 'bind_dn' => trim($bind_dna) ,
617 | 'bind_pw' => trim($bind_pwa),
618 | 'ssl' => $ssl
619 | );
620 | }
621 | return $ldapinfo;
622 | }
623 |
624 | function authenticate($username, $password = null) {
625 | global $ost;
626 | if (!$password) {
627 | $ost->logWarning('MLA auth (' . $username . ')', "password blank or null", false);
628 | return null;
629 | }
630 | //check if they used their email to login.
631 | if (!filter_var($username, FILTER_VALIDATE_EMAIL) === false) {
632 | $username = explode('@', $username) [0];
633 | }
634 |
635 | $chkUser = null;
636 | $ldap = new AuthLdap();
637 | foreach ($this->ldapinfo() as $data) {
638 | $ldap->serverType = 'ActiveDirectory';
639 | $ldap->server = preg_split('/;|,/', $data['servers']);
640 | $ldap->domain = $data['sd'];
641 | $ldap->dn = $data['dn'];
642 | $ldap->ssl = $data['ssl'];
643 |
644 | if ($ldap->connect()) {
645 | $conninfo[] = array(
646 | 'bool' => true,
647 | 'msg' => 'System connected to (' . $data['sd'] . ')'
648 | );
649 | }
650 | else {
651 | $conninfo['bool'] = false;
652 | $conninfo['msg'] = ($data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText);
653 | $ost->logWarning('MLA connect error (' . $username . ')', $conninfo['msg'], false);
654 | continue;
655 | }
656 |
657 | if ($chkUser = $ldap->checkPass($username, $password) != false) {
658 |
659 | $loginfo[] = array(
660 | 'bool' => $chkUser,
661 | 'msg' => 'User authenticated on (' . $data['sd'] . ')'
662 | );
663 | }
664 | else {
665 | $loginfo[] = array(
666 | 'bool' => $chkUser,
667 | 'msg' => ($data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText)
668 | );
669 | continue;
670 | }
671 |
672 | $ldap->searchUser = $data['bind_dn'];
673 | $ldap->searchPassword = $data['bind_pw'];
674 |
675 | if ($chkUser) break; //Break if user authenticated
676 | } //end foreach
677 |
678 | if (($conninfo['bool'] == false || $loginfo['bool'] == false) && !$chkUser) {
679 | $errmsg;
680 | foreach ($loginfo as $err) {
681 | $errmsg .= $err['msg'] . " ";
682 | }
683 |
684 | if (self::getConfig()->get('debug-choice'))
685 | $ost->logWarning('MLA login error (' . $username . ')', trim($errmsg), false);
686 |
687 | }
688 | if ($chkUser) {
689 | if (self::getConfig()->get('debug-choice'))
690 | $ost->logWarning('MLA login success(' . $username . ')', $loginfo[0]['msg'], false);
691 | return $this->authOrCreate($username);
692 | }
693 | else {
694 | return;
695 | }
696 | }
697 |
698 | function authOrCreate($username) {
699 | global $cfg, $ost;
700 | $mode = $cfg->getClientRegistrationMode();
701 | $instance = $this->config->config['sync_data']->ht['namespace'];
702 | $ins = explode("." , $instance);
703 | if (self::getConfig()->get('debug-choice')){
704 | $ost->logDebug('MLA Registraion Mode', 'System set to : '.$mode, false);
705 | $ost->logWarning('MLA Instance (' . $username . ')', $instance, false);
706 | }
707 | switch ($this->type) {
708 | case 'staff':
709 | if (self::getConfig()->get('debug-verbose'))
710 | $ost->logDebug('MLA StaffSession', json_encode(StaffSession::lookup($username)), false);
711 | if (($user = StaffSession::lookup($username)) && $user->getId()) {
712 | if (!$user instanceof StaffSession) {
713 | // osTicket <= v1.9.7 or so
714 | $user = new StaffSession($user->getId());
715 | }
716 | return $user;
717 | } else {
718 |
719 | $staff_groups = preg_split('/;|,/', $this->config->get('multiauth-staff-group'));
720 | if ($this->config->get('debug-verbose'))
721 | $ost->logWarning('MLA ldap staffnotfound (' . $username . ')', json_encode($staff_groups), false);
722 | $chkgroup;
723 | foreach ($this->ldapinfo() as $data) {
724 | $ldap = new AuthLdap();
725 | $ldap->serverType = 'ActiveDirectory';
726 | $ldap->server = preg_split('/;|,/', $data['servers']);
727 | $ldap->dn = $data['dn'];
728 | $ldap->domain = $data['sd'];
729 | $ldap->searchUser = $data['bind_dn'];
730 | $ldap->searchPassword = $data['bind_pw'];
731 | $ldap->ssl = $data['ssl'];
732 |
733 | if ($ldap->connect()) {
734 | foreach ($staff_groups as $staff_group) {
735 | if ($ldap->checkGroup($username, trim($staff_group))) {
736 | $chkgroup = true;
737 | break 2;
738 | } else {
739 | $conninfo[] = array(
740 | false,
741 | $data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText
742 | );
743 | if ($this->config->get('debug-verbose'))
744 | $ost->logWarning('MLA ldap checkgrp (' . $username . ')', json_encode($conninfo[1]), false);
745 | $chkgroup = false;
746 | }
747 | }
748 | } else {
749 | $conninfo[] = array(
750 | false,
751 | $data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText
752 | );
753 |
754 | if (self::getConfig()->get('debug-verbose'))
755 | $ost->logWarning('MLA ldap ConnInfo (' . $username . ')', $conninfo[1], false);
756 | }
757 | }
758 | if ($this->getConfig($this->instance->ins)->get('multiauth-staff-register') && $chkgroup) {
759 | if (!($info = $this->search($username, true)[0])) {
760 | return;
761 | }
762 | $errors = array();
763 | $staff = array();
764 |
765 | $staff['do'] = 'create';
766 | $staff['add'] = 'a';
767 | $staff['id'] = '';
768 | $staff['username'] = $info['username'];
769 | $staff['firstname'] = $info['first'];
770 | $staff['lastname'] = $info['last'];
771 | $staff['email'] = $info['email'];
772 | $staff['isadmin'] = 0;
773 | $staff['isactive'] = 1;
774 | $staff['group_id'] = 1;
775 | $staff['dept_id'] = $this->getConfig($this->instance->ins)->get('multiauth_staff_dept');
776 | $staff['role_id'] = 1;
777 | $staff['backend'] = "mldap.".($ins[0][0].$ins[1].$ins[2][0].$ins[3]);
778 | $staff['assign_use_pri_role'] = "on";
779 | $staff['isvisible'] = 1;
780 | $staff['prems'] = array("visibility.agents", "visibility.departments");
781 |
782 | $staffcreate = Staff::create();
783 | if ($staffcreate->update($staff, $errors)) {
784 | $ost->logWarning('MLA Staff Created (' . $username . ')', json_encode($staff), false, true);
785 | if (($user = StaffSession::lookup($username))) {
786 | if (!$user instanceof StaffSession) {
787 | $user = new StaffSession($user->getId());
788 | }
789 | return $user;
790 | }
791 | } else {
792 | $ost->logWarning('MLA Staff Creation Error (' . $username . ')', json_encode($staff), false);
793 | }
794 | }
795 | }
796 | break;
797 | case 'client':
798 | $client_groups = preg_split('/;|,/', $this->config->get('multiauth-client-group'));
799 | // Lookup all the information on the user. Try to get the email
800 | // addresss as well as the username when looking up the user
801 | // locally.
802 | if (!$info = $this->search($username, true)['0']) {
803 | $ost->logWarning('MLA ldap info (' . $username . ')',json_encode($info), false);
804 | return;
805 | }
806 |
807 | $acct = ClientAccount::lookupByUsername($username);
808 |
809 | if ($acct && $acct->getId()) {
810 | $client = new ClientSession(new EndUser($acct->getUser()));
811 | $ost->logWarning('MLA session (' . $username . ')',json_encode($client), false);
812 | }
813 |
814 | //If client does not exist MLA will create it.
815 | if (!$acct) {
816 | $info['name'] = $info['first'] . " " . $info['last'];
817 | $info['email'] = $info['email'];
818 | $info['full'] = $info['full'];
819 | $info['first'] = $info['first'];
820 | $info['last'] = $info['last'];
821 | $info['username'] = $info['username'];
822 | $info['sendemail'] = false;
823 | $info['backend'] = 'mldap.client.'.($ins[0][0].$ins[1].$ins[2][0].$ins[3]);
824 | $info['timezone'] = $cfg->getTimezone();
825 | switch ($mode) {
826 | case 'public':
827 | if ($client = new ClientCreateRequest($this, $username, $info)) {
828 | $ost->logWarning('MLA user-creation-success (' . $username . ')', 'user was created '.$mode, false);
829 | } else {
830 | $ost->logWarning('MLA user-creation-failed (' . $username . ')', 'user creation failed unknown error '.$mode, false);
831 | }
832 | break;
833 | case 'closed':
834 |
835 | if ($this->getConfig()->get('multiauth-force-register')) {
836 | //only needed if closed
837 | //$info['backend'] = 'mldap.client.'.$ins[0][0].$ins[1]$ins[2][0].$ins[3];
838 | //$info['timezone'] = $cfg->getTimezone();
839 | //$info['lang'] = $cfg->getLanguage(); needs more testing
840 |
841 | $create = User::fromVars($info);
842 | $register = UserAccount::register($create, $info, $errors);
843 | $client = new ClientSession(new EndUser($register->getUser()));
844 | $ost->logWarning('MLA user-creation-success (' . $username . ')', 'user was created', false);
845 | } else {
846 | $ost->logWarning('MLA user-creation-failed (' . $username . ')', 'user creation failed (enable force user creation)', false);
847 | }
848 | break;
849 | }
850 |
851 | }
852 | return $client;
853 | }
854 | return;
855 | }
856 |
857 | function create_account($username, $type) {
858 | }
859 |
860 | function lookup($lookup_dn) {
861 | $lookup_user = array();
862 | preg_match('/(dc=(?:[^C]|C(?!N=))*)(?:;|$)/i', $lookup_dn, $match);
863 | $base_dn = strtolower($match[0]);
864 |
865 | $key = array_search($base_dn, preg_split('/;/', strtolower($this->getConfig($this->instance->ins)
866 | ->get('basedn'))));
867 |
868 | $key = (!isset($key) || is_null($key)) ? 0 : $key;
869 |
870 | $dn = trim($base_dn);
871 |
872 | $servers = $this->getConfig($this->instance->ins)
873 | ->get('servers');
874 | $serversa = preg_split('/\s+/', $servers) [$key];
875 |
876 | $sd = $this->getConfig($this->instance->ins)
877 | ->get('shortdomain');
878 | $sda = preg_split('/;|,/', $sd) [$key];
879 |
880 | $bind_dn = $this->getConfig($this->instance->ins)
881 | ->get('bind_dn');
882 | $bind_dna = preg_split('/;/', $bind_dn) [$key];
883 |
884 | $bind_pw = $this->getConfig($this->instance->ins)
885 | ->get('bind_pw');
886 | $bind_pwa = preg_split('/;|,/', $bind_pw) [$key];
887 |
888 | $ssl = $this->getConfig($this->instance->ins)->get('tls');
889 |
890 | $data = array(
891 | 'dn' => trim($dn) ,
892 | 'sd' => trim($sda) ,
893 | 'servers' => trim($serversa) ,
894 | 'bind_dn' => trim($bind_dna) ,
895 | 'bind_pw' => trim($bind_pwa) ,
896 | 'ssl' => $ssl
897 | );
898 |
899 | $ldap = new AuthLdap();
900 | $ldap->serverType = 'ActiveDirectory';
901 | $ldap->server = preg_split('/;|,/', $data['servers']);
902 | $ldap->dn = $data['dn'];
903 | $ldap->domain = $data['sd'];
904 | $ldap->searchUser = $data['bind_dn'];
905 | $ldap->searchPassword = $data['bind_pw'];
906 | $ldap->ssl = $data['ssl'];
907 |
908 | if ($ldap->connect()) {
909 |
910 | $filter = '(&(objectCategory=person)(distinguishedName={q}))';
911 | if ($temp_user = $ldap->getUsers($lookup_dn, $this->adschema(), $filter)) {
912 | $lookup_user = $this->keymap($temp_user);
913 | }
914 | else {
915 | $conninfo[] = array(
916 | false,
917 | $data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText
918 | );
919 | }
920 | }
921 | else {
922 | $conninfo[] = array(
923 | false,
924 | $data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText
925 | );
926 | LdapMultiAuthPlugin::logger('info', 'MLA ldap-ConnInfo', $conninfo);
927 | }
928 | $lookup_user = self::flatarray($lookup_user);
929 | $lookup_user[0]['name'] = $lookup_user[0]['full'];
930 | return $lookup_user[0];
931 | }
932 |
933 | function search($query, $single = false) {
934 | global $ost;
935 | $userlist = array();
936 | $combined_userlist = array();
937 | $ldapinfo = $this->ldapinfo();
938 |
939 | foreach ($ldapinfo as $data) {
940 | $ldap = new AuthLdap();
941 | $ldap->serverType = 'ActiveDirectory';
942 | $ldap->server = preg_split('/;|,/', $data['servers']);
943 | $ldap->dn = $data['dn'];
944 | $ldap->domain = $data['sd'];
945 | $ldap->searchUser = $data['bind_dn'];
946 | $ldap->searchPassword = $data['bind_pw'];
947 | $ldap->ssl = $data['ssl'];
948 |
949 | if ($ldap->connect()) {
950 | $search = str_replace("((", "(|(", self::getConfig($this->instance->ins)->get('search_base'));
951 | $filter = $search;
952 | if (self::getConfig()->get('debug-choice'))
953 | $ost->logDebug('MLA search filter', $search, false);
954 | if ($single) {
955 | $userlist = $ldap->getUsers($query, $this->adschema() , $filter);
956 | } else {
957 | $userlist = $ldap->getUsers($query, $this->adschema() , $filter);
958 | }
959 |
960 | if ($userlist) {
961 | if (self::getConfig()->get('debug-choice'))
962 | $ost->logDebug('MLA '. strtolower($data['sd']).' ldap userlist (' . $query . ')', json_encode($userlist), false);
963 | $temp_userlist = self::flatarray($this->keymap($userlist));
964 | $combined_userlist = array_merge($combined_userlist, $temp_userlist);
965 | } else {
966 | if (self::getConfig()->get('debug-choice'))
967 | $ost->logError('MLA search-error (' .$query. ')', $data['sd'] . ' #' .$ldap->ldapErrorCode . " - " . $ldap->ldapErrorText, false);
968 | }
969 | } else {
970 | $conninfo[] = array(
971 | false,
972 | $data['sd'] . " error: " . $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText
973 | );
974 | $ost->logWarning('MLA search-info', $ldap->ldapErrorCode . " - " . $ldap->ldapErrorText, false);
975 | }
976 | }
977 | if (self::getConfig()->get('debug-verbose'))
978 | $ost->logDebug('MLA system userlist (' . $query . ')', json_encode($combined_userlist), false);
979 | return $combined_userlist;
980 | }
981 | }
982 |
983 | class StaffLDAPMultiAuthentication extends StaffAuthenticationBackend implements AuthDirectorySearch {
984 | static $name = "Multi LDAP Authentication";
985 | static $id = "mldap";
986 | function __construct($config) {
987 | $this->_ldap = new LDAPMultiAuthentication($config);
988 | $this->config = $config;
989 | }
990 | //queries the user information
991 | function authenticate($username, $password = false, $errors = array()) {
992 | return $this
993 | ->_ldap
994 | ->authenticate($username, $password);
995 | }
996 |
997 | function getName() {
998 | $config = $this->config;
999 | list($__, $_N) = $config::translate();
1000 | return $__(static ::$name);
1001 | }
1002 | //lookup local and remote users
1003 | function lookup($dn) {
1004 | $list = $this
1005 | ->_ldap
1006 | ->lookup($dn);
1007 | if ($list) {
1008 | $list['backend'] = static ::$id;
1009 | $list['id'] = $this->getBkId() . ':' . $list['dn'];
1010 | }
1011 | return $list;
1012 | }
1013 |
1014 | //General searching of users
1015 | function search($query) {
1016 | global $ost;
1017 | if (strlen($query) < 3)
1018 | return array();
1019 |
1020 | $ost->logWarning('MLA search', $query, false);
1021 | $list = array(
1022 | $this
1023 | ->_ldap
1024 | ->search($query))[0];
1025 | foreach ($list as &$l) {
1026 | $l['backend'] = static::$id;
1027 | $l['id'] = $this->getBkId() . ':' . $l['dn'];
1028 | }
1029 | $ost->logWarning('MLA search list', json_encode($list, false));
1030 | return $list;
1031 | }
1032 | }
1033 |
1034 | class ClientLDAPMultiAuthentication extends UserAuthenticationBackend {
1035 | static $name = "Multi LDAP Authentication";
1036 | static $id = "mldap.client";
1037 | function __construct($config) {
1038 | $this->_ldap = new LDAPMultiAuthentication($config, 'client');
1039 | $this->config = $config;
1040 | if ($domain = $config->get('basedn')) self::$name .= sprintf(' (%s)', $domain);
1041 | }
1042 | function getName() {
1043 | $config = $this->config;
1044 | list($__, $_N) = $config::translate();
1045 | return $__(static ::$name);
1046 | }
1047 | function authenticate($username, $password = false, $errors = array()) {
1048 | $object = $this
1049 | ->_ldap
1050 | ->authenticate($username, $password);
1051 | if ($object instanceof ClientCreateRequest) $object->setBackend($this);
1052 | return $object;
1053 | }
1054 | }
1055 |
--------------------------------------------------------------------------------