Overview

Packages

  • awl
    • caldav-client-v2
    • RRule
  • davical
    • authentication
      • drivers
    • caldav
    • DAViCalSession
    • DAVTicket
    • external-bind
    • feed
    • HTTPAuthSession
    • iSchedule
    • iSchedule-POST
    • logging
    • metrics
    • Principal
    • propfind
    • PublicSession
    • Request
    • Resource
    • tzservice
  • None
  • PHP

Classes

  • imapPamDriver
  • ldapDriver
  • pwauthPamDriver
  • rimapPamDriver
  • squidPamDriver

Functions

  • array_values_mapping
  • fix_unique_member
  • getStaticLdap
  • IMAP_PAM_check
  • LDAP_check
  • PWAUTH_PAM_check
  • RIMAP_check
  • SQUID_PAM_check
  • sync_LDAP
  • sync_LDAP_groups
  • sync_user_from_LDAP
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
  1: <?php
  2: /**
  3: * Manages LDAP repository connection
  4: *
  5: * @package   davical
  6: * @category  Technical
  7: * @subpackage authentication/drivers
  8: * @author    Maxime Delorme <mdelorme@tennaxia.net>,
  9: *            Andrew McMillan <andrew@mcmillan.net.nz>
 10: * @copyright Maxime Delorme
 11: * @license   http://gnu.org/copyleft/gpl.html GNU GPL v2 or later
 12: */
 13: 
 14: require_once("auth-functions.php");
 15: 
 16: /**
 17:  * Plugin to authenticate and sync with LDAP
 18:  */
 19: class ldapDriver
 20: {
 21:   /**#@+
 22:   * @access private
 23:   */
 24: 
 25:   /**
 26:   * Holds the LDAP connection parameters
 27:   */
 28:   var $connect;
 29: 
 30:   /**#@-*/
 31: 
 32: 
 33:   /**
 34:   * Initializes the LDAP connection
 35:   *
 36:   * @param array $config The configuration data
 37:   */
 38:   function __construct($config)
 39:   {
 40:       global $c;
 41:       $host=$config['host'];
 42:       $port=$config['port'];
 43:       if(!function_exists('ldap_connect')){
 44:           $c->messages[] = i18n("drivers_ldap : function ldap_connect not defined, check your php_ldap module");
 45:           $this->valid=false;
 46:           return ;
 47:       }
 48: 
 49:       //Set LDAP protocol version
 50:       if (isset($config['protocolVersion']))
 51:           ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, $config['protocolVersion']);
 52:       if (isset($config['optReferrals']))
 53:           ldap_set_option($this->connect, LDAP_OPT_REFERRALS, $config['optReferrals']);
 54:       if (isset($config['networkTimeout']))
 55:           ldap_set_option($this->connect, LDAP_OPT_NETWORK_TIMEOUT, $config['networkTimeout']);
 56: 
 57:       if ($port)
 58:           $this->connect=ldap_connect($host, $port);
 59:       else
 60:           $this->connect=ldap_connect($host);
 61: 
 62:       if (! $this->connect){
 63:           $c->messages[] = sprintf(translate( 'drivers_ldap : Unable to connect to LDAP with port %s on host %s'), $port, $host );
 64:           $this->valid=false;
 65:           return ;
 66:       }
 67: 
 68:       dbg_error_log( "LDAP", "drivers_ldap : Connected to LDAP server %s",$host );
 69: 
 70:       // Start TLS if desired (requires protocol version 3)
 71:       if (isset($config['startTLS'])) {
 72:         if (!ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, 3)) {
 73:           $c->messages[] = i18n('drivers_ldap : Failed to set LDAP to use protocol version 3, TLS not supported');
 74:           $this->valid=false;
 75:           return;
 76:         }
 77:         if (!ldap_start_tls($this->connect)) {
 78:           $c->messages[] = i18n('drivers_ldap : Could not start TLS: ldap_start_tls() failed');
 79:           $this->valid=false;
 80:           return;
 81:         }
 82:       }
 83: 
 84:       //Set the search scope to be used, default to subtree.  This sets the functions to be called later.
 85:       if (!isset($config['scope'])) $config['scope'] = 'subtree';
 86:       switch (strtolower($config['scope'])) {
 87:       case "base":
 88:         $this->ldap_query_one = 'ldap_read';
 89:         $this->ldap_query_all = 'ldap_read';
 90:         break;
 91:       case "onelevel":
 92:         $this->ldap_query_one = 'ldap_list';
 93:         $this->ldap_query_all = 'ldap_search';
 94:         break;
 95:       default:
 96:         $this->ldap_query_one = 'ldap_search';
 97:         $this->ldap_query_all = 'ldap_search';
 98:         break;
 99:       }
100: 
101:       //connect as root
102:       if (!ldap_bind($this->connect, (isset($config['bindDN']) ? $config['bindDN'] : null), (isset($config['passDN']) ? $config['passDN'] : null) ) ){
103:           $bindDN = isset($config['bindDN']) ? $config['bindDN'] : 'anonymous';
104:           $passDN = isset($config['passDN']) ? $config['passDN'] : 'anonymous';
105:           dbg_error_log( "LDAP", i18n('drivers_ldap : Failed to bind to host %1$s on port %2$s with bindDN of %3$s'), $host, $port, $bindDN );
106:           $c->messages[] = i18n( 'drivers_ldap : Unable to bind to LDAP - check your configuration for bindDN and passDN, and that your LDAP server is reachable');
107:           $this->valid=false;
108:           return ;
109:       }
110:       $this->valid = true;
111:       //root to start search
112:       $this->baseDNUsers  = is_string($config['baseDNUsers']) ? array($config['baseDNUsers']) : $config['baseDNUsers'];
113:       $this->filterUsers  = (isset($config['filterUsers'])  ? $config['filterUsers']  : null);
114:       $this->baseDNGroups = is_string($config['baseDNGroups']) ? array($config['baseDNGroups']) : $config['baseDNGroups'];
115:       $this->filterGroups = (isset($config['filterGroups']) ? $config['filterGroups'] : null);
116:   }
117: 
118:   /**
119:   * Retrieve all users from the LDAP directory
120:   */
121:   function getAllUsers($attributes){
122:     global $c;
123: 
124:     $query = $this->ldap_query_all;
125:     $ret = array();
126: 
127:     foreach($this->baseDNUsers as $baseDNUsers) {
128:       $entry = $query($this->connect,$baseDNUsers,$this->filterUsers,$attributes);
129: 
130:       if (!ldap_first_entry($this->connect,$entry)) {
131:         $c->messages[] = sprintf(translate('Error NoUserFound with filter >%s<, attributes >%s< , dn >%s<'),
132:                                  $this->filterUsers,
133:                                  join(', ', $attributes),
134:                                  $baseDNUsers);
135:       }
136:       $row = array();
137:       for($i = ldap_first_entry($this->connect,$entry);
138:           $i && $arr = ldap_get_attributes($this->connect,$i);
139:           $i = ldap_next_entry($this->connect,$i) ) {
140:         $row = array();
141:         for ($j=0; $j < $arr['count']; $j++) {
142:           $row[$arr[$j]] = $arr[$arr[$j]][0];
143:         }
144:         $ret[]=$row;
145:       }
146:     }
147:     return $ret;
148:   }
149: 
150:   /**
151:   * Retrieve all groups from the LDAP directory
152:   */
153:   function getAllGroups($attributes){
154:     global $c;
155: 
156:     $query = $this->ldap_query_all;
157:     $ret = array();
158: 
159:     foreach($this->baseDNGroups as $baseDNGroups) {
160:       $entry = $query($this->connect,$baseDNGroups,$this->filterGroups,$attributes);
161: 
162:       if (!ldap_first_entry($this->connect,$entry)) {
163:         $c->messages[] = sprintf(translate('Error NoGroupFound with filter >%s<, attributes >%s< , dn >%s<'),
164:                                  $this->filterGroups,
165:                                  join(', ', $attributes),
166:                                  $baseDNGroups);
167:       }
168:       $row = array();
169:       for($i = ldap_first_entry($this->connect,$entry);
170:           $i && $arr = ldap_get_attributes($this->connect,$i);
171:           $i = ldap_next_entry($this->connect,$i) ) {
172:         for ($j=0; $j < $arr['count']; $j++) {
173:           $row[$arr[$j]] = count($arr[$arr[$j]])>2?$arr[$arr[$j]]:$arr[$arr[$j]][0];
174:         }
175:         $ret[]=$row;
176:         unset($row);
177:       }
178:     }
179:     return $ret;
180:   }
181: 
182:   /**
183:     * Returns the result of the LDAP query
184:     *
185:     * @param string $filter The filter used to search entries
186:     * @param array $attributes Attributes to be returned
187:     * @param string $passwd password to check
188:     * @return array Contains selected attributes from all entries corresponding to the given filter
189:     */
190:   function requestUser( $filter, $attributes=NULL, $username, $passwd) {
191:     global $c;
192: 
193:     $entry=NULL;
194:     // We get the DN of the USER
195:     $query = $this->ldap_query_one;
196: 
197:     foreach($this->baseDNUsers as $baseDNUsers) {
198:       $entry = $query($this->connect, $baseDNUsers, $filter, $attributes);
199: 
200:       if (ldap_first_entry($this->connect,$entry) )
201:         break;
202: 
203:       dbg_error_log( "LDAP", "drivers_ldap : Failed to find user with baseDN: %s", $baseDNUsers );
204:     }
205: 
206:     if ( !ldap_first_entry($this->connect, $entry) ){
207:       dbg_error_log( "ERROR", "drivers_ldap : Unable to find the user with filter %s",$filter );
208:       return false;
209:     } else {
210:       dbg_error_log( "LDAP", "drivers_ldap : Found a user using filter %s",$filter );
211:     }
212: 
213:     $dnUser = ldap_get_dn($this->connect, ldap_first_entry($this->connect,$entry));
214: 
215:     if ( isset($c->authenticate_hook['config']['i_use_mode_kerberos']) && $c->authenticate_hook['config']['i_use_mode_kerberos'] == "i_know_what_i_am_doing") {
216:       if (isset($_SERVER["REMOTE_USER"])) {
217:         dbg_error_log( "LOG", "drivers_ldap : Skipping password Check for user %s which should be the same as %s",$username , $_SERVER["REMOTE_USER"]);
218:         if ($username != $_SERVER["REMOTE_USER"]) {
219:           return false;
220:         }
221:       } else {
222:         dbg_error_log( "LOG", "drivers_ldap : Skipping password Check for user %s which should be the same as %s",$username , $_SERVER["REDIRECT_REMOTE_USER"]);
223:         if ($username != $_SERVER["REDIRECT_REMOTE_USER"]) {
224:           return false;
225:         }
226:       }
227:     }
228:     else if ( empty($passwd) || preg_match('/[\x00-\x19]/',$passwd) ) {
229:       // See http://www.php.net/manual/en/function.ldap-bind.php#73718 for more background
230:       dbg_error_log( 'LDAP', 'drivers_ldap : user %s supplied empty or invalid password: login rejected', $dnUser );
231:       return false;
232:     }
233:     else {
234:       if ( !@ldap_bind($this->connect, $dnUser, $passwd) ) {
235:         dbg_error_log( "LDAP", "drivers_ldap : Failed to bind to user %s ", $dnUser );
236:         return false;
237:       }
238:     }
239: 
240:     dbg_error_log( "LDAP", "drivers_ldap : Bound to user %s using password %s", $dnUser,
241:           (isset($c->dbg['password']) && $c->dbg['password'] ? $passwd : 'another delicious password for the debugging monster!') );
242: 
243:     $i = ldap_first_entry($this->connect,$entry);
244:     $arr = ldap_get_attributes($this->connect,$i);
245:     for( $i=0; $i<$arr['count']; $i++ ) {
246:       $ret[$arr[$i]]=$arr[$arr[$i]][0];
247:     }
248:     return $ret;
249: 
250:   }
251: }
252: 
253: 
254: /**
255: * A generic function to create and fetch static objects
256: */
257: function getStaticLdap() {
258:   global $c;
259:   // Declare a static variable to hold the object instance
260:   static $instance;
261: 
262:   // If the instance is not there, create one
263:   if(!isset($instance)) {
264:     $ldapDriver = new ldapDriver($c->authenticate_hook['config']);
265: 
266:     if ($ldapDriver->valid) {
267:         $instance = $ldapDriver;
268:     }
269:   }
270:   else {
271:       $ldapDriver = $instance;
272:   }
273:   return $ldapDriver;
274: }
275: 
276: 
277: /**
278: * Synchronise a cached user with one from LDAP
279: * @param object $principal A Principal object to be updated (or created)
280: */
281: function sync_user_from_LDAP( Principal &$principal, $mapping, $ldap_values ) {
282:   global $c;
283: 
284:   dbg_error_log( "LDAP", "Going to sync the user from LDAP" );
285: 
286:   $fields_to_set = array();
287:   $updateable_fields = Principal::updateableFields();
288:   foreach( $updateable_fields AS $field ) {
289:     if ( isset($mapping[$field]) ) {
290:       $tab_part_fields = explode(',',$mapping[$field]);
291:       foreach( $tab_part_fields as $part_field ) {
292:         if ( isset($ldap_values[$part_field]) ) {
293:           if (isset($fields_to_set[$field]) ) {
294:             $fields_to_set[$field] .= ' '.$ldap_values[$part_field];
295:           }
296:           else {
297:             $fields_to_set[$field] = $ldap_values[$part_field];
298:           }
299:         }
300:       }
301:       dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $fields_to_set[$field], $mapping[$field] );
302:     }
303:     else if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value'])
304:               && isset($c->authenticate_hook['config']['default_value'][$field] ) ) {
305:       $fields_to_set[$field] = $c->authenticate_hook['config']['default_value'][$field];
306:       dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $c->authenticate_hook['config']['default_value'][$field] );
307:     }
308:   }
309: 
310:   if ( $principal->Exists() ) {
311:     $principal->Update($fields_to_set);
312:   }
313:   else {
314:     $principal->Create($fields_to_set);
315:     CreateHomeCollections($principal->username(), $c->default_timezone);
316:     CreateDefaultRelationships($principal->username());
317:   }
318: }
319: 
320: /**
321: * explode the multipart mapping
322: */
323: function array_values_mapping($mapping){
324:   $attributes=array();
325:   foreach ( $mapping as $field ) {
326:     $tab_part_field = explode(",",$field);
327:     foreach( $tab_part_field as $part_field ) {
328:       $attributes[] = $part_field;
329:     }
330:   }
331:   return $attributes;
332: }
333: 
334: /**
335: * Check the username / password against the LDAP server
336: */
337: function LDAP_check($username, $password ){
338:   global $c;
339: 
340:   $ldapDriver = getStaticLdap();
341:   if ( !$ldapDriver->valid ) {
342:     sleep(1);  // Sleep very briefly to try and survive intermittent issues
343:     $ldapDriver = getStaticLdap();
344:     if ( !$ldapDriver->valid ) {
345:       dbg_error_log( "ERROR", "Couldn't contact LDAP server for authentication" );
346:       foreach($c->messages as $msg) {
347:           dbg_error_log( "ERROR", "-> ".$msg );
348:       }
349:       header( sprintf("HTTP/1.1 %d %s", 503, translate("Authentication server unavailable.")) );
350:       exit(0);
351:     }
352:   }
353: 
354:   $mapping = $c->authenticate_hook['config']['mapping_field'];
355:   if ( isset($mapping['active']) && !isset($mapping['user_active']) ) {
356:     // Backward compatibility: now 'user_active'
357:     $mapping['user_active'] = $mapping['active'];
358:     unset($mapping['active']);
359:   }
360:   if ( isset($mapping['updated']) && !isset($mapping['modified']) ) {
361:     // Backward compatibility: now 'modified'
362:     $mapping['modified'] = $mapping['updated'];
363:     unset($mapping['updated']);
364:   }
365:   $attributes = array_values_mapping($mapping);
366: 
367:   /**
368:   * If the config contains a filter that starts with a ( then believe
369:   * them and don't modify it, otherwise wrap the filter.
370:   */
371:   $filter_munge = "";
372:   if ( preg_match( '/^\(/', $ldapDriver->filterUsers ) ) {
373:     $filter_munge = $ldapDriver->filterUsers;
374:   }
375:   else if ( isset($ldapDriver->filterUsers) && $ldapDriver->filterUsers != '' ) {
376:     $filter_munge = "($ldapDriver->filterUsers)";
377:   }
378: 
379:   $filter = "(&$filter_munge(".$mapping['username']."=$username))";
380:   $valid = $ldapDriver->requestUser( $filter, $attributes, $username, $password );
381: 
382:   // is a valid user or not
383:   if ( !$valid ) {
384:     dbg_error_log( "LDAP", "user %s is not a valid user",$username );
385:     return false;
386:   }
387: 
388:   if ( $mapping['modified'] != "" && array_key_exists($mapping['modified'], $valid)) {
389:     $ldap_timestamp = $valid[$mapping['modified']];
390:   } else {
391:     $ldap_timestamp = '19700101000000';
392:   }
393: 
394:   /**
395:   * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
396:   */
397:   foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
398:     $$k = substr($ldap_timestamp,$v[0],$v[1]);
399: 
400:   $ldap_timestamp = "$Y"."$m"."$d"."$H"."$M"."$S";
401:   if ($mapping['modified'] != "" && array_key_exists($mapping['modified'], $valid)) {
402:     $valid[$mapping['modified']] = "$Y-$m-$d $H:$M:$S";
403:   }
404: 
405:   $principal = new Principal('username',$username);
406:   if ( $principal->Exists() ) {
407:     // should we update it ?
408:     $db_timestamp = $principal->modified;
409:     $db_timestamp = substr(strtr($db_timestamp, array(':' => '',' '=>'','-'=>'')),0,14);
410:     if( $ldap_timestamp <= $db_timestamp ) {
411:         return $principal; // no need to update
412:     }
413:     // we will need to update the user record
414:   }
415:   else {
416:     dbg_error_log( "LDAP", "user %s doesn't exist in local DB, we need to create it",$username );
417:   }
418:   $principal->setUsername($username);
419: 
420:   // The local cached user doesn't exist, or is older, so we create/update their details
421:   sync_user_from_LDAP( $principal, $mapping, $valid );
422: 
423:   return $principal;
424: 
425: }
426: 
427: /**
428: * turn a list of uniqueMember into member strings
429: */
430: function fix_unique_member($list) {
431:   $fixed_list = array();
432:   foreach ( $list as $member ){
433:     list( $mem, $rest ) = explode(",", $member );
434:     $member = str_replace( 'uid=', '', $mem );
435:     array_unshift( $fixed_list, $member );
436:   }
437:   return $fixed_list;
438: }
439: 
440: /**
441: * sync LDAP Groups against the DB
442: */
443: function sync_LDAP_groups(){
444:   global $c;
445:   $ldapDriver = getStaticLdap();
446:   if ( ! $ldapDriver->valid ) return;
447: 
448:   $mapping = $c->authenticate_hook['config']['group_mapping_field'];
449:   //$attributes = array('cn','modifyTimestamp','memberUid');
450:   $attributes = array_values_mapping($mapping);
451:   $ldap_groups_tmp = $ldapDriver->getAllGroups($attributes);
452: 
453:   if ( sizeof($ldap_groups_tmp) == 0 ) return;
454: 
455:   $member_field = $mapping['members'];
456:   $dnfix = isset($c->authenticate_hook['config']['group_member_dnfix']) && $c->authenticate_hook['config']['group_member_dnfix'];
457: 
458:   foreach($ldap_groups_tmp as $key => $ldap_group){
459:     $group_mapping = $ldap_group[$mapping['username']];
460:     $ldap_groups_info[$group_mapping] = $ldap_group;
461:     if ( is_array($ldap_groups_info[$group_mapping][$member_field]) ) {
462:       unset( $ldap_groups_info[$group_mapping][$member_field]['count'] );
463:     }
464:     else {
465:       $ldap_groups_info[$group_mapping][$member_field] = array($ldap_groups_info[$group_mapping][$member_field]);
466:     }
467:     unset($ldap_groups_tmp[$key]);
468:   }
469:   $db_groups = array();
470:   $db_group_members = array();
471:   $qry = new AwlQuery( "SELECT g.username AS group_name, member.username AS member_name FROM dav_principal g LEFT JOIN group_member ON (g.principal_id=group_member.group_id) LEFT JOIN dav_principal member  ON (member.principal_id=group_member.member_id) WHERE g.type_id = 3");
472:   $qry->Exec('sync_LDAP',__LINE__,__FILE__);
473:   while($db_group = $qry->Fetch()) {
474:     $db_groups[$db_group->group_name] = $db_group->group_name;
475:     $db_group_members[$db_group->group_name][] = $db_group->member_name;
476:   }
477: 
478:   $ldap_groups = array_keys($ldap_groups_info);
479:   // users only in ldap
480:   $groups_to_create = array_diff($ldap_groups,$db_groups);
481:   // users only in db
482:   $groups_to_deactivate = array_diff($db_groups,$ldap_groups);
483:   // users present in ldap and in the db
484:   $groups_to_update = array_intersect($db_groups,$ldap_groups);
485: 
486:   if ( sizeof ( $groups_to_create ) ){
487:     $c->messages[] = sprintf(i18n('- creating groups : %s'),join(', ',$groups_to_create));
488:     $validUserFields = awl_get_fields('usr');
489:     foreach ( $groups_to_create as $k => $group ){
490:       $user = (object) array();
491: 
492:       if ( isset($c->authenticate_hook['config']['default_value']) && is_array($c->authenticate_hook['config']['default_value']) ) {
493:         foreach ( $c->authenticate_hook['config']['default_value'] as $field => $value ) {
494:           if ( isset($validUserFields[$field]) ) {
495:             $user->{$field} =  $value;
496:             dbg_error_log( "LDAP", "Setting usr->%s to %s from configured defaults", $field, $value );
497:           }
498:         }
499:       }
500:       $user->user_no = 0;
501:       $ldap_values = $ldap_groups_info[$group];
502:       foreach ( $mapping as $field => $value ) {
503:         dbg_error_log( "LDAP", "Considering copying %s", $field );
504:         if ( isset($validUserFields[$field]) ) {
505:           $user->{$field} =  $ldap_values[$value];
506:           dbg_error_log( "LDAP", "Setting usr->%s to %s from LDAP field %s", $field, $ldap_values[$value], $value );
507:         }
508:       }
509:       if ($user->fullname=="") {
510:         $user->fullname = $group;
511:       }
512:       if ($user->displayname=="") {
513:         $user->displayname = $group;
514:       }
515:       $user->username = $group;
516:       $user->updated = "now";  /** @todo Use the 'updated' timestamp from LDAP for groups too */
517: 
518:       $principal = new Principal('username',$group);
519:       if ( $principal->Exists() ) {
520:         $principal->Update($user);
521:       }
522:       else {
523:         $principal->Create($user);
524:       }
525: 
526:       $qry = new AwlQuery( "UPDATE dav_principal set type_id = 3 WHERE username=:group ",array(':group'=>$group) );
527:       $qry->Exec('sync_LDAP',__LINE__,__FILE__);
528:       Principal::cacheDelete('username', $group);
529:       $c->messages[] = sprintf(i18n('- adding users %s to group : %s'),join(',',$ldap_groups_info[$group][$mapping['members']]),$group);
530:       foreach ( $ldap_groups_info[$group][$mapping['members']] as $member ){
531:         if ( $member_field == 'uniqueMember' || $dnfix ) {
532:           list( $mem, $rest ) = explode(",", $member );
533:           $member = str_replace( 'uid=', '', $mem );
534:         }
535:         $qry = new AwlQuery( "INSERT INTO group_member SELECT g.principal_id AS group_id,u.principal_id AS member_id FROM dav_principal g, dav_principal u WHERE g.username=:group AND u.username=:member;",array (':group'=>$group,':member'=>$member) );
536:         $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
537:         Principal::cacheDelete('username', $member);
538:       }
539:     }
540:   }
541: 
542:   if ( sizeof ( $groups_to_update ) ){
543:     $c->messages[] = sprintf(i18n('- updating groups : %s'),join(', ',$groups_to_update));
544:     foreach ( $groups_to_update as $group ){
545:       $db_members = array_values ( $db_group_members[$group] );
546:       $ldap_members = array_values ( $ldap_groups_info[$group][$member_field] );
547:       if ( $member_field == 'uniqueMember' || $dnfix ) {
548:         $ldap_members = fix_unique_member( $ldap_members );
549:       }
550:       $add_users = array_diff ( $ldap_members, $db_members );
551:       if ( sizeof ( $add_users ) ){
552:         $c->messages[] = sprintf(i18n('- adding %s to group : %s'),join(', ', $add_users ), $group);
553:         foreach ( $add_users as $member ){
554:           $qry = new AwlQuery( "INSERT INTO group_member SELECT g.principal_id AS group_id,u.principal_id AS member_id FROM dav_principal g, dav_principal u WHERE g.username=:group AND u.username=:member",array (':group'=>$group,':member'=>$member) );
555:           $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
556:           Principal::cacheDelete('username', $member);
557:         }
558:       }
559:       $remove_users = @array_flip( @array_flip( array_diff( $db_members, $ldap_members ) ));
560:       if ( sizeof ( $remove_users ) ){
561:         $c->messages[] = sprintf(i18n('- removing %s from group : %s'),join(', ', $remove_users ), $group);
562:         foreach ( $remove_users as $member ){
563:           $qry = new AwlQuery( "DELETE FROM group_member USING dav_principal g,dav_principal m WHERE group_id=g.principal_id AND member_id=m.principal_id AND g.username=:group AND m.username=:member",array (':group'=>$group,':member'=>$member) );
564:           $qry->Exec('sync_LDAP_groups',__LINE__,__FILE__);
565:           Principal::cacheDelete('username', $member);
566:         }
567:       }
568:     }
569:   }
570: 
571:   if ( sizeof ( $groups_to_deactivate ) ){
572:     $c->messages[] = sprintf(i18n('- deactivate groups : %s'),join(', ',$groups_to_deactivate));
573:     foreach ( $groups_to_deactivate as $group ){
574:       $qry = new AwlQuery( 'UPDATE dav_principal SET user_active=FALSE WHERE username=:group AND type_id = 3',array(':group'=>$group) );
575:       $qry->Exec('sync_LDAP',__LINE__,__FILE__);
576:       Principal::cacheFlush('username=:group AND type_id = 3', array(':group'=>$group) );
577:     }
578:   }
579: 
580: }
581: 
582: /**
583: * sync LDAP against the DB
584: */
585: function sync_LDAP(){
586:   global $c;
587:   $ldapDriver = getStaticLdap();
588:   if ( ! $ldapDriver->valid ) return;
589: 
590:   $mapping = $c->authenticate_hook['config']['mapping_field'];
591:   $attributes = array_values_mapping($mapping);
592:   $ldap_users_tmp = $ldapDriver->getAllUsers($attributes);
593: 
594:   if ( sizeof($ldap_users_tmp) == 0 ) return;
595: 
596:   foreach($ldap_users_tmp as $key => $ldap_user){
597:     $ldap_users_info[$ldap_user[$mapping['username']]] = $ldap_user;
598:     unset($ldap_users_tmp[$key]);
599:   }
600:   $qry = new AwlQuery( "SELECT username, user_no, modified as updated FROM dav_principal where type_id=1");
601:   $qry->Exec('sync_LDAP',__LINE__,__FILE__);
602:   while($db_user = $qry->Fetch()) {
603:     $db_users[] = $db_user->username;
604:     $db_users_info[$db_user->username] = array('user_no' => $db_user->user_no, 'updated' => $db_user->updated);
605:   }
606: 
607:   // all users from ldap
608:   $ldap_users = array_keys($ldap_users_info);
609:   // users only in ldap
610:   $users_to_create = array_diff($ldap_users,$db_users);
611:   // users only in db
612:   $users_to_deactivate = array_diff($db_users,$ldap_users);
613:   // users present in ldap and in the db
614:   $users_to_update = array_intersect($db_users,$ldap_users);
615: 
616:   // creation of all users;
617:   if ( sizeof($users_to_create) ) {
618:     $c->messages[] = sprintf(i18n('- creating record for users :  %s'),join(', ',$users_to_create));
619: 
620:     foreach( $users_to_create as $username ) {
621:       $principal = new Principal( 'username', $username );
622:       $valid = $ldap_users_info[$username];
623:       if ( $mapping['modified'] != "" && array_key_exists($mapping['modified'], $valid)) {
624:         $ldap_timestamp = $valid[$mapping['modified']];
625:       } else {
626:         $ldap_timestamp = '19700101000000';
627:       }
628: 
629:       if ( !empty($c->authenticate_hook['config']['format_updated']) ) {
630:         /**
631:         * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
632:         */
633:         foreach($c->authenticate_hook['config']['format_updated'] as $k => $v)
634:             $$k = substr($ldap_timestamp,$v[0],$v[1]);
635:         $ldap_timestamp = $Y.$m.$d.$H.$M.$S;
636:       }
637:       else if ( preg_match('{^(\d{8})(\d{6})(Z)?$', $ldap_timestamp, $matches ) ) {
638:         $ldap_timestamp = $matches[1].'T'.$matches[2].$matches[3];
639:       }
640:       else if ( empty($ldap_timestamp) ) {
641:         $ldap_timestamp = date('c');
642:       }
643:       if ( $mapping['modified'] != "" && array_key_exists($mapping['modified'], $valid)) {
644:         $valid[$mapping['modified']] = $ldap_timestamp;
645:       }
646: 
647:       sync_user_from_LDAP( $principal, $mapping, $valid );
648:     }
649:   }
650: 
651:   // deactivating all users
652:   $params = array();
653:   $i = 0;
654:   $paramstring = '';
655:   foreach( $users_to_deactivate AS $v ) {
656:     if ( isset($c->do_not_sync_from_ldap) && isset($c->do_not_sync_from_ldap[$v]) ) continue;
657:     if ( $i > 0 ) $paramstring .= ',';
658:     $paramstring .= ':u'.$i.'::text';
659:     $params[':u'.$i++] = strtolower($v);
660:   }
661:   if ( count($params) > 0 ) {
662:     $c->messages[] = sprintf(i18n('- deactivating users : %s'),join(', ',$users_to_deactivate));
663:     $qry = new AwlQuery( 'UPDATE usr SET active = FALSE WHERE lower(username) IN ('.$paramstring.')', $params);
664:     $qry->Exec('sync_LDAP',__LINE__,__FILE__);
665: 
666:     Principal::cacheFlush('lower(username) IN ('.$paramstring.')', $params);
667:   }
668: 
669:   // updating all users
670:   if ( sizeof($users_to_update) ) {
671:     foreach ( $users_to_update as $key=> $username ) {
672:       $principal = new Principal( 'username', $username );
673:       $valid=$ldap_users_info[$username];
674:       if ( $mapping['modified'] != "" && array_key_exists($mapping['modified'], $valid)) {
675:         $ldap_timestamp = $valid[$mapping['modified']];
676:       } else {
677:         $ldap_timestamp = '19700101000000';
678:       }
679: 
680:       $valid['user_no'] = $db_users_info[$username]['user_no'];
681:       $mapping['user_no'] = 'user_no';
682: 
683:       /**
684:       * This splits the LDAP timestamp apart and assigns values to $Y $m $d $H $M and $S
685:       */
686:       foreach($c->authenticate_hook['config']['format_updated'] as $k => $v) {
687:         $$k = substr($ldap_timestamp,$v[0],$v[1]);
688:       }
689:       $ldap_timestamp = $Y.$m.$d.$H.$M.$S;
690:       $valid[$mapping['modified']] = "$Y-$m-$d $H:$M:$S";
691: 
692:       $db_timestamp = substr(strtr($db_users_info[$username]['updated'], array(':' => '',' '=>'','-'=>'')),0,14);
693:       if ( $ldap_timestamp > $db_timestamp ) {
694:         sync_user_from_LDAP($principal, $mapping, $valid );
695:       }
696:       else {
697:         unset($users_to_update[$key]);
698:         $users_nothing_done[] = $username;
699:       }
700:     }
701:     if ( sizeof($users_to_update) )
702:       $c->messages[] = sprintf(i18n('- updating user records : %s'),join(', ',$users_to_update));
703:     if ( sizeof($users_nothing_done) )
704:       $c->messages[] = sprintf(i18n('- nothing done on : %s'),join(', ', $users_nothing_done));
705:   }
706: 
707:   $admins = 0;
708:   $qry = new AwlQuery( "SELECT count(*) AS admins FROM usr JOIN role_member USING ( user_no ) JOIN roles USING (role_no) WHERE usr.active=TRUE AND role_name='Admin'");
709:   $qry->Exec('sync_LDAP',__LINE__,__FILE__);
710:   while ( $db_user = $qry->Fetch() ) {
711:     $admins = $db_user->admins;
712:   }
713:   if ( $admins == 0 ) {
714:     $c->messages[] = sprintf(i18n('Warning: there are no active admin users! You should fix this before logging out.  Consider using the $c->do_not_sync_from_ldap configuration setting.'));
715:   }
716: }
717: 
DAViCal API documentation generated by ApiGen 2.8.0