2001-01-11 10:52:33 +01:00
< ? php
2006-06-07 01:42:36 +02:00
/**
* API - accounts LDAP backend
2008-06-05 09:42:21 +02:00
*
2006-06-07 01:42:36 +02:00
* @ link http :// www . egroupware . org
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de > complete rewrite in 6 / 2006
2008-06-05 09:42:21 +02:00
*
* This class replaces the former accounts_ldap class written by
2006-06-07 01:42:36 +02:00
* Joseph Engo < jengo @ phpgroupware . org > , Lars Kneschke < lkneschke @ phpgw . de > ,
* Miles Lott < milos @ groupwhere . org > and Bettina Gille < ceb @ phpgroupware . org >.
* Copyright ( C ) 2000 - 2002 Joseph Engo , Lars Kneschke
* Copyright ( C ) 2003 Lars Kneschke , Bettina Gille
2008-06-05 09:42:21 +02:00
*
2006-06-07 01:42:36 +02:00
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package api
* @ subpackage accounts
* @ version $Id $
*/
/**
* LDAP Backend for accounts
2008-06-05 09:42:21 +02:00
*
2009-08-22 09:13:59 +02:00
* The LDAP backend of the accounts class now stores accounts , groups and the memberships completly in LDAP .
* It does NO longer use the ACL class / table for group membership information .
* Nor does it use the phpgwAcounts schema ( part of that information is stored via shadowAccount now ) .
*
* A user is recogniced by eGW , if he ' s in the user_context tree AND has the posixAccount object class AND
* matches the LDAP search filter specified in setup >> configuration .
* A group is recogniced by eGW , if it ' s in the group_context tree AND has the posixGroup object class .
* The group members are stored as memberuid ' s .
*
* The ( positive ) group - id ' s ( gidnumber ) of LDAP groups are mapped in this class to negative numeric
* account_id 's to not conflict with the user-id' s , as both share in eGW internaly the same numberspace !
*
2006-06-07 01:42:36 +02:00
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ package api
* @ subpackage accounts
* @ access internal only use the interface provided by the accounts class
*/
2007-12-13 03:32:44 +01:00
class accounts_ldap
2006-06-07 01:42:36 +02:00
{
/**
* resource with connection to the ldap server
*
* @ var resource
*/
var $ds ;
/**
* LDAP context for users , eg . ou = account , dc = domain , dc = com
*
* @ var string
*/
var $user_context ;
2006-10-10 09:33:43 +02:00
/**
* LDAP search filter for user accounts , eg . ( uid =% name )
*
* @ var string
*/
var $account_filter ;
2006-06-07 01:42:36 +02:00
/**
* LDAP context for groups , eg . ou = groups , dc = domain , dc = com
*
* @ var string
*/
var $group_context ;
/**
* total number of found entries from get_list method
*
* @ var int
*/
var $total ;
2008-06-05 09:42:21 +02:00
2006-06-18 07:07:10 +02:00
var $ldapServerInfo ;
2006-06-07 01:42:36 +02:00
/**
* required classe for user and groups
*
* @ var array
*/
var $requiredObjectClasses = array (
'user' => array (
'top' , 'person' , 'organizationalperson' , 'inetorgperson' , 'posixaccount' , 'shadowaccount'
),
2009-08-22 09:13:59 +02:00
'user-if-supported' => array ( // these classes get added, only if the server supports them
'mozillaabpersonalpha' , 'mozillaorgperson' , 'evolutionperson'
),
2006-06-07 01:42:36 +02:00
'group' => array (
2006-06-18 07:07:10 +02:00
'top' , 'posixgroup' , 'groupofnames'
2006-06-07 01:42:36 +02:00
)
);
2006-10-11 23:59:43 +02:00
/**
* Classes allowing to set a mail - address for a group and specify the memberaddresses as forwarding addresses
*
2007-12-13 03:32:44 +01:00
* @ var array
2006-10-11 23:59:43 +02:00
*/
var $group_mail_classes = array (
2006-10-12 11:20:22 +02:00
'dbmailforwardingaddress' => 'mailforwardingaddress' ,
2010-04-16 15:30:38 +02:00
'dbmailuser' => array ( 'mailforwardingaddress' , 'uid' ),
'qmailuser' => array ( 'mailforwardingaddress' , 'uid' ),
'mailaccount' => 'mailalias' ,
2006-10-11 23:59:43 +02:00
);
2008-06-05 09:42:21 +02:00
2007-12-13 03:32:44 +01:00
/**
* Reference to our frontend
*
* @ var accounts
*/
private $frontend ;
2008-06-05 09:42:21 +02:00
2007-12-13 03:32:44 +01:00
/**
* Instance of the ldap class
*
* @ var ldap
*/
private $ldap ;
2006-06-07 01:42:36 +02:00
2013-09-02 15:40:40 +02:00
/**
* does backend allow to change account_lid
*/
const CHANGE_ACCOUNT_LID = true ;
/**
* does backend requires password to be set , before allowing to enable an account
*/
const REQUIRE_PASSWORD_FOR_ENABLE = false ;
2006-06-07 01:42:36 +02:00
/**
* Constructor
*
2007-12-13 03:32:44 +01:00
* @ param accounts $frontend reference to the frontend class , to be able to call it ' s methods if needed
* @ return accounts_ldap
2006-06-07 01:42:36 +02:00
*/
2007-12-13 03:32:44 +01:00
function __construct ( accounts $frontend )
2001-03-10 13:27:22 +01:00
{
2007-12-13 03:32:44 +01:00
$this -> frontend = $frontend ;
2006-06-07 01:42:36 +02:00
// enable the caching in the session, done by the accounts class extending this class.
2006-07-12 22:43:38 +02:00
$this -> use_session_cache = true ;
2005-11-24 20:48:21 +01:00
2013-06-23 11:58:08 +02:00
$this -> ldap = new ldap ( true );
2007-12-13 03:32:44 +01:00
$this -> ds = $this -> ldap -> ldapConnect ( $this -> frontend -> config [ 'ldap_host' ],
$this -> frontend -> config [ 'ldap_root_dn' ], $this -> frontend -> config [ 'ldap_root_pw' ]);
$this -> user_context = $this -> frontend -> config [ 'ldap_context' ];
$this -> account_filter = $this -> frontend -> config [ 'ldap_search_filter' ];
2008-06-05 09:42:21 +02:00
$this -> group_context = $this -> frontend -> config [ 'ldap_group_context' ] ?
2007-12-13 03:32:44 +01:00
$this -> frontend -> config [ 'ldap_group_context' ] : $this -> frontend -> config [ 'ldap_context' ];
2006-06-07 01:42:36 +02:00
}
2001-03-10 13:27:22 +01:00
2006-06-07 01:42:36 +02:00
/**
* Reads the data of one account
*
* @ param int $account_id numeric account - id
2009-08-22 09:13:59 +02:00
* @ return array | boolean array with account data ( keys : account_id , account_lid , ... ) or false if account not found
2006-06-07 01:42:36 +02:00
*/
function read ( $account_id )
{
if ( ! ( int ) $account_id ) return false ;
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
if ( $account_id < 0 )
{
return $this -> _read_group ( $account_id );
2001-03-10 13:27:22 +01:00
}
2006-06-07 01:42:36 +02:00
return $this -> _read_user ( $account_id );
}
/**
* Saves / adds the data of one account
2008-06-05 09:42:21 +02:00
*
2006-06-07 01:42:36 +02:00
* If no account_id is set in data the account is added and the new id is set in $data .
*
* @ param array $data array with account - data
2009-08-22 09:13:59 +02:00
* @ return int | boolean the account_id or false on error
2006-06-07 01:42:36 +02:00
*/
function save ( & $data )
{
2006-06-07 07:16:56 +02:00
$is_group = $data [ 'account_id' ] < 0 || $data [ 'account_type' ] === 'g' ;
2001-03-10 13:27:22 +01:00
2009-08-22 09:13:59 +02:00
$data_utf8 = translation :: convert ( $data , translation :: charset (), 'utf-8' );
2006-06-18 07:07:10 +02:00
$members = $data [ 'account_members' ];
2008-06-05 09:42:21 +02:00
2006-06-18 07:07:10 +02:00
if ( ! is_object ( $this -> ldapServerInfo ))
{
2007-12-13 03:32:44 +01:00
$this -> ldapServerInfo = $this -> ldap -> getLDAPServerInfo ( $this -> frontend -> config [ 'ldap_host' ]);
2006-06-18 07:07:10 +02:00
}
2006-06-07 01:42:36 +02:00
// common code for users and groups
// checks if accout_lid (dn) has been changed or required objectclass'es are missing
if ( $data_utf8 [ 'account_id' ] && $data_utf8 [ 'account_lid' ])
2001-03-10 13:27:22 +01:00
{
2006-06-07 01:42:36 +02:00
// read the entry first, to check if the dn (account_lid) has changed
$sri = $is_group ? ldap_search ( $this -> ds , $this -> group_context , 'gidnumber=' . abs ( $data [ 'account_id' ])) :
ldap_search ( $this -> ds , $this -> user_context , 'uidnumber=' . $data [ 'account_id' ]);
$old = ldap_get_entries ( $this -> ds , $sri );
2001-03-10 13:27:22 +01:00
2006-06-07 01:42:36 +02:00
if ( ! $old [ 'count' ])
2001-09-03 04:17:10 +02:00
{
2006-06-07 01:42:36 +02:00
unset ( $old );
2001-09-03 04:17:10 +02:00
}
else
{
2013-06-18 12:45:00 +02:00
$old = ldap :: result2array ( $old [ 0 ]);
2006-06-07 01:42:36 +02:00
foreach ( $old [ 'objectclass' ] as $n => $class )
2003-09-13 19:14:30 +02:00
{
2006-06-07 01:42:36 +02:00
$old [ 'objectclass' ][ $n ] = strtolower ( $class );
2003-09-13 19:14:30 +02:00
}
2006-06-18 07:07:10 +02:00
$key = false ;
if ( $is_group && ( $key = array_search ( 'namedobject' , $old [ 'objectclass' ])) !== false ||
$is_group && ( $old [ 'cn' ] != $data_utf8 [ 'account_lid' ] || substr ( $old [ 'dn' ], 0 , 3 ) != 'cn=' ) ||
2006-06-07 01:42:36 +02:00
! $is_group && ( $old [ 'uid' ] != $data_utf8 [ 'account_lid' ] || substr ( $old [ 'dn' ], 0 , 4 ) != 'uid=' ))
2003-09-13 19:14:30 +02:00
{
2006-06-07 01:42:36 +02:00
// query the memberships to set them again later
2006-06-18 07:07:10 +02:00
if ( ! $is_group )
{
$memberships = $this -> memberships ( $data [ 'account_id' ]);
}
else
{
$members = $old ? $old [ 'memberuid' ] : $this -> members ( $data [ 'account_id' ]);
}
2006-06-07 01:42:36 +02:00
// if dn has changed --> delete the old entry, as we cant rename the dn
2008-06-05 09:42:21 +02:00
$this -> delete ( $data [ 'account_id' ]);
2006-06-07 01:42:36 +02:00
unset ( $old [ 'dn' ]);
2006-06-18 07:07:10 +02:00
// removing the namedObject object-class, if it's included
if ( $key !== false ) unset ( $old [ 'objectclass' ][ $key ]);
2006-06-07 01:42:36 +02:00
$to_write = $old ;
unset ( $old );
2005-11-24 20:48:21 +01:00
}
2003-09-13 19:14:30 +02:00
}
2006-06-07 01:42:36 +02:00
}
2006-06-09 00:04:44 +02:00
if ( ! $data [ 'account_id' ]) // new account
2006-06-07 01:42:36 +02:00
{
if ( ! ( $data [ 'account_id' ] = $data_utf8 [ 'account_id' ] = $this -> _get_nextid ( $is_group ? 'g' : 'u' )))
2001-09-03 04:17:10 +02:00
{
2006-06-07 01:42:36 +02:00
return false ;
2001-09-03 04:17:10 +02:00
}
2006-06-07 01:42:36 +02:00
}
// check if we need to write the objectclass: new entry or required object classes are missing
if ( ! $old || array_diff ( $this -> requiredObjectClasses [ $is_group ? 'group' : 'user' ], $old [ 'objectclass' ]))
{
// additional objectclasse might be already set in $to_write or $old
if ( ! is_array ( $to_write [ 'objectclass' ]))
2001-09-03 04:17:10 +02:00
{
2006-06-07 01:42:36 +02:00
$to_write [ 'objectclass' ] = $old ? $old [ 'objectclass' ] : array ();
2003-09-14 10:29:29 +02:00
}
2009-09-15 10:43:44 +02:00
if ( ! $old && ! $is_group ) // for new accounts add additional addressbook object classes, if supported by server
2009-08-22 09:51:55 +02:00
{ // as setting them later might loose eg. password, if we are not allowed to read them
foreach ( $this -> requiredObjectClasses [ 'user-if-supported' ] as $additional )
{
if ( $this -> ldapServerInfo -> supportsObjectClass ( $additional ))
{
$to_write [ 'objectclass' ][] = $additional ;
}
}
}
2006-06-07 01:42:36 +02:00
$to_write [ 'objectclass' ] = array_values ( array_unique ( array_merge ( $to_write [ 'objectclass' ],
$this -> requiredObjectClasses [ $is_group ? 'group' : 'user' ])));
}
if ( ! ( $dn = $old [ 'dn' ]))
{
if ( ! $data [ 'account_lid' ]) return false ;
2004-01-25 01:04:40 +01:00
2006-06-07 01:42:36 +02:00
$dn = $is_group ? 'cn=' . $data_utf8 [ 'account_lid' ] . ',' . $this -> group_context :
'uid=' . $data_utf8 [ 'account_lid' ] . ',' . $this -> user_context ;
}
// now we merge the user or group data
if ( $is_group )
{
$to_write = $this -> _merge_group ( $to_write , $data_utf8 );
$data [ 'account_type' ] = 'g' ;
2008-06-05 09:42:21 +02:00
2006-06-18 07:07:10 +02:00
$groupOfNames = in_array ( 'groupofnames' , $old ? $old [ 'objectclass' ] : $to_write [ 'objectclass' ]);
if ( ! $old && $groupOfNames || $members )
{
2007-12-13 03:32:44 +01:00
$to_write = array_merge ( $to_write , $this -> set_members ( $members ,
2006-06-18 07:07:10 +02:00
$data [ 'account_id' ], $groupOfNames , $dn ));
}
2006-10-11 23:59:43 +02:00
// check if we should set a mail address and forwards for each member
foreach ( $this -> group_mail_classes as $objectclass => $forward )
{
if ( $this -> ldapServerInfo -> supportsObjectClass ( $objectclass ) &&
( $old && in_array ( $objectclass , $old [ 'objectclass' ]) || $data_utf8 [ 'account_email' ] || $old [ 'mail' ]))
{
2010-04-16 15:30:38 +02:00
$extra_attr = false ;
2010-07-21 16:39:07 +02:00
if ( is_array ( $forward )) list ( $forward , $extra_attr ) = $forward ;
2006-10-11 23:59:43 +02:00
if ( $data_utf8 [ 'account_email' ]) // setting an email
{
if ( ! in_array ( $objectclass , $old ? $old [ 'objectclass' ] : $to_write [ 'objectclass' ]))
{
if ( $old ) $to_write [ 'objectclass' ] = $old [ 'objectclass' ];
$to_write [ 'objectclass' ][] = $objectclass ;
}
2010-04-16 15:30:38 +02:00
if ( $extra_attr ) $to_write [ $extra_attr ] = $data_utf8 [ 'account_lid' ];
2006-10-11 23:59:43 +02:00
$to_write [ 'mail' ] = $data_utf8 [ 'account_email' ];
2008-06-05 09:42:21 +02:00
2006-10-11 23:59:43 +02:00
if ( ! $members ) $members = $this -> members ( $data [ 'account_id' ]);
$to_write [ $forward ] = array ();
2014-10-22 19:10:41 +02:00
foreach ( array_keys ( $members ) as $member )
2006-10-11 23:59:43 +02:00
{
if (( $email = $this -> id2name ( $member , 'account_email' )))
{
$to_write [ $forward ][] = $email ;
}
}
}
elseif ( $old ) // remove the mail and forwards only for existing entries
{
$to_write [ 'mail' ] = $to_write [ $forward ] = array ();
2010-04-16 15:30:38 +02:00
if ( $extra_attr ) $to_write [ $extra_attr ] = array ();
2006-10-11 23:59:43 +02:00
if (( $key = array_search ( $objectclass , $old [ 'objectclass' ])))
{
$to_write [ 'objectclass' ] = $old [ 'objectclass' ];
unset ( $to_write [ 'objectclass' ][ $key ]);
$to_write [ 'objectclass' ] = array_values ( $to_write [ 'objectclass' ]);
}
}
break ;
}
}
2008-06-05 09:42:21 +02:00
2001-09-03 04:17:10 +02:00
}
2006-06-07 01:42:36 +02:00
else
{
$to_write = $this -> _merge_user ( $to_write , $data_utf8 , ! $old );
2006-09-16 12:00:06 +02:00
// make sure multiple email-addresses in the mail attribute "survive"
if ( isset ( $to_write [ 'mail' ]) && count ( $old [ 'mail' ]) > 1 )
{
$mail = $old [ 'mail' ];
$mail [ 0 ] = $to_write [ 'mail' ];
$to_write [ 'mail' ] = array_values ( array_unique ( $mail ));
}
2006-06-07 01:42:36 +02:00
$data [ 'account_type' ] = 'u' ;
2010-03-16 17:33:28 +01:00
// Check if an account already exists as system user, and if it does deny creation
2013-05-23 19:09:59 +02:00
if ( ! $GLOBALS [ 'egw_info' ][ 'server' ][ 'ldap_allow_systemusernames' ] && ! $old &&
2010-03-16 17:33:28 +01:00
function_exists ( 'posix_getpwnam' ) && posix_getpwnam ( $data [ 'account_lid' ]))
{
throw new egw_exception_wrong_userinput ( lang ( 'There already is a system-user with this name. User\'s should not have the same name as a systemuser' ));
}
2006-06-07 01:42:36 +02:00
}
2008-06-05 09:42:21 +02:00
2007-03-20 10:24:22 +01:00
// remove memberuid when adding a group
if ( ! $old && is_array ( $to_write [ 'memberuid' ]) && empty ( $to_write [ 'memberuid' ])) {
unset ( $to_write [ 'memberuid' ]);
}
2006-06-07 07:16:56 +02:00
//echo "<p>ldap_".($old ? 'modify' : 'add')."(,$dn,".print_r($to_write,true).")</p>\n";
2006-06-07 01:42:36 +02:00
// modifying or adding the entry
2006-07-11 04:28:58 +02:00
if ( $old && !@ ldap_modify ( $this -> ds , $dn , $to_write ) ||
2006-06-09 00:04:44 +02:00
! $old && !@ ldap_add ( $this -> ds , $dn , $to_write ))
2006-06-07 01:42:36 +02:00
{
2006-06-08 05:29:07 +02:00
$err = true ;
2006-07-11 04:28:58 +02:00
if ( $is_group && ( $key = array_search ( 'groupofnames' , $to_write [ 'objectclass' ])) !== false )
2006-06-08 05:29:07 +02:00
{
2006-06-18 07:07:10 +02:00
// try again with removed groupOfNames stuff, as I cant detect if posixGroup is a structural object
unset ( $to_write [ 'objectclass' ][ $key ]);
2006-10-11 23:59:43 +02:00
$to_write [ 'objectclass' ] = array_values ( $to_write [ 'objectclass' ]);
2006-06-18 07:07:10 +02:00
unset ( $to_write [ 'member' ]);
2006-07-11 04:28:58 +02:00
$err = $old ? ! ldap_modify ( $this -> ds , $dn , $to_write ) : ! ldap_add ( $this -> ds , $dn , $to_write );
2006-06-08 05:29:07 +02:00
}
if ( $err )
{
2010-04-16 15:30:38 +02:00
error_log ( __METHOD__ . " () ldap_ " . ( $old ? 'modify' : 'add' ) . " (,' $dn ', " . array2string ( $to_write ) . " ) --> ldap_error()= " . ldap_error ( $this -> ds ));
2006-06-08 05:29:07 +02:00
echo " ldap_ " . ( $old ? 'modify' : 'add' ) . " (, $dn , " . print_r ( $to_write , true ) . " ) \n " ;
echo ldap_error ( $this -> ds );
return false ;
}
2006-06-07 01:42:36 +02:00
}
2006-06-18 07:07:10 +02:00
if ( $memberships )
2006-06-07 01:42:36 +02:00
{
$this -> set_memberships ( $memberships , $data [ 'account_id' ]);
}
return $data [ 'account_id' ];
}
/**
* Delete one account , deletes also all acl - entries for that account
*
2014-10-22 19:10:41 +02:00
* @ param int $account_id numeric account_id
2006-06-07 01:42:36 +02:00
* @ return boolean true on success , false otherwise
*/
function delete ( $account_id )
{
if ( ! ( int ) $account_id ) return false ;
if ( $account_id < 0 )
{
$sri = ldap_search ( $this -> ds , $this -> group_context , 'gidnumber=' . abs ( $account_id ));
}
else
{
// remove the user's memberships
$this -> set_memberships ( array (), $account_id );
$sri = ldap_search ( $this -> ds , $this -> user_context , 'uidnumber=' . $account_id );
2001-02-13 16:19:19 +01:00
}
2006-06-07 01:42:36 +02:00
if ( ! $sri ) return false ;
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
$allValues = ldap_get_entries ( $this -> ds , $sri );
if ( ! $allValues [ 'count' ]) return false ;
return ldap_delete ( $this -> ds , $allValues [ 0 ][ 'dn' ]);
}
2001-02-14 20:27:37 +01:00
2006-06-07 01:42:36 +02:00
/**
* Reads the data of one group
*
2008-06-05 09:42:21 +02:00
* @ internal
2006-06-07 01:42:36 +02:00
* @ param int $account_id numeric account - id ( < 0 as it ' s for a group )
2009-08-22 09:13:59 +02:00
* @ return array | boolean array with account data ( keys : account_id , account_lid , ... ) or false if account not found
2006-06-07 01:42:36 +02:00
*/
2009-08-22 09:13:59 +02:00
protected function _read_group ( $account_id )
2006-06-07 01:42:36 +02:00
{
2007-06-27 07:26:46 +02:00
$sri = ldap_search ( $this -> ds , $this -> group_context , '(&(objectClass=posixGroup)(gidnumber=' . abs ( $account_id ) . '))' ,
2006-10-11 23:59:43 +02:00
array ( 'dn' , 'gidnumber' , 'cn' , 'objectclass' , 'mail' ));
2008-06-05 09:42:21 +02:00
2014-10-22 19:10:41 +02:00
$ldap_data = ldap_get_entries ( $this -> ds , $sri );
if ( ! $ldap_data [ 'count' ])
2001-03-10 13:27:22 +01:00
{
2006-06-07 01:42:36 +02:00
return false ; // group not found
}
2014-10-22 19:10:41 +02:00
$data = translation :: convert ( $ldap_data [ 0 ], 'utf-8' );
2008-06-05 09:42:21 +02:00
2006-06-18 07:07:10 +02:00
$group = array (
2006-06-07 01:42:36 +02:00
'account_dn' => $data [ 'dn' ],
'account_id' => - $data [ 'gidnumber' ][ 0 ],
'account_lid' => $data [ 'cn' ][ 0 ],
'account_type' => 'g' ,
'account_firstname' => $data [ 'cn' ][ 0 ],
'account_lastname' => lang ( 'Group' ),
2011-01-02 22:53:04 +01:00
'account_fullname' => lang ( 'Group' ) . ' ' . $data [ 'cn' ][ 0 ],
2006-06-18 07:07:10 +02:00
'groupOfNames' => in_array ( 'groupOfNames' , $data [ 'objectclass' ]),
2006-10-11 23:59:43 +02:00
'account_email' => $data [ 'mail' ][ 0 ],
2006-06-07 01:42:36 +02:00
);
2006-06-18 07:07:10 +02:00
if ( ! is_object ( $this -> ldapServerInfo ))
{
2007-12-13 03:32:44 +01:00
$this -> ldapServerInfo = $this -> ldap -> getLDAPServerInfo ( $this -> frontend -> config [ 'ldap_host' ]);
2006-06-18 07:07:10 +02:00
}
2014-10-22 19:10:41 +02:00
foreach ( array_keys ( $this -> group_mail_classes ) as $objectclass )
2006-10-11 23:59:43 +02:00
{
if ( $this -> ldapServerInfo -> supportsObjectClass ( $objectclass ))
{
$group [ 'mailAllowed' ] = $objectclass ;
break ;
}
}
2006-06-18 07:07:10 +02:00
return $group ;
2006-06-07 01:42:36 +02:00
}
2001-09-03 04:17:10 +02:00
2006-06-07 01:42:36 +02:00
/**
* Reads the data of one user
*
2008-06-05 09:42:21 +02:00
* @ internal
2006-06-07 01:42:36 +02:00
* @ param int $account_id numeric account - id
2009-08-22 09:13:59 +02:00
* @ return array | boolean array with account data ( keys : account_id , account_lid , ... ) or false if account not found
2006-06-07 01:42:36 +02:00
*/
2009-08-22 09:13:59 +02:00
protected function _read_user ( $account_id )
2006-06-07 01:42:36 +02:00
{
2007-06-27 07:26:46 +02:00
$sri = ldap_search ( $this -> ds , $this -> user_context , '(&(objectclass=posixAccount)(uidnumber=' . ( int ) $account_id . '))' ,
2011-08-31 14:17:34 +02:00
array ( 'dn' , 'uidnumber' , 'uid' , 'gidnumber' , 'givenname' , 'sn' , 'cn' , 'mail' , 'userpassword' , 'telephonenumber' ,
2008-04-10 11:06:24 +02:00
'shadowexpire' , 'shadowlastchange' , 'homedirectory' , 'loginshell' , 'createtimestamp' , 'modifytimestamp' ));
2008-06-05 09:42:21 +02:00
2014-10-22 19:10:41 +02:00
$ldap_data = ldap_get_entries ( $this -> ds , $sri );
if ( ! $ldap_data [ 'count' ])
2006-06-07 01:42:36 +02:00
{
return false ; // user not found
}
2014-10-22 19:10:41 +02:00
$data = translation :: convert ( $ldap_data [ 0 ], 'utf-8' );
2008-06-05 09:42:21 +02:00
2006-08-24 08:27:03 +02:00
$utc_diff = date ( 'Z' );
2006-06-07 01:42:36 +02:00
$user = array (
'account_dn' => $data [ 'dn' ],
'account_id' => ( int ) $data [ 'uidnumber' ][ 0 ],
'account_lid' => $data [ 'uid' ][ 0 ],
'account_type' => 'u' ,
'account_primary_group' => - $data [ 'gidnumber' ][ 0 ],
'account_firstname' => $data [ 'givenname' ][ 0 ],
'account_lastname' => $data [ 'sn' ][ 0 ],
'account_email' => $data [ 'mail' ][ 0 ],
'account_fullname' => $data [ 'cn' ][ 0 ],
2006-06-08 01:21:30 +02:00
'account_pwd' => $data [ 'userpassword' ][ 0 ],
2011-08-31 14:17:34 +02:00
'account_phone' => $data [ 'telephonenumber' ][ 0 ],
2006-06-07 01:42:36 +02:00
// both status and expires are encoded in the single shadowexpire value in LDAP
// - if it's unset an account is enabled AND does never expire
// - if it's set to 0, the account is disabled
// - if it's set to > 0, it will or already has expired --> acount is active if it not yet expired
2006-08-24 08:27:03 +02:00
// shadowexpire is in days since 1970/01/01 (equivalent to a timestamp (int UTC!) / (24*60*60)
'account_status' => isset ( $data [ 'shadowexpire' ]) && $data [ 'shadowexpire' ][ 0 ] * 24 * 3600 + $utc_diff < time () ? false : 'A' ,
'account_expires' => isset ( $data [ 'shadowexpire' ]) && $data [ 'shadowexpire' ][ 0 ] ? $data [ 'shadowexpire' ][ 0 ] * 24 * 3600 + $utc_diff : - 1 , // LDAP date is in UTC
2010-09-22 15:13:27 +02:00
'account_lastpwd_change' => isset ( $data [ 'shadowlastchange' ]) ? $data [ 'shadowlastchange' ][ 0 ] * 24 * 3600 + ( $data [ 'shadowlastchange' ][ 0 ] != 0 ? $utc_diff : 0 ) : null ,
2006-06-07 01:42:36 +02:00
// lastlogin and lastlogin from are not availible via the shadowAccount object class
// 'account_lastlogin' => $data['phpgwaccountlastlogin'][0],
// 'account_lastloginfrom' => $data['phpgwaccountlastloginfrom'][0],
2006-06-22 01:14:18 +02:00
'person_id' => $data [ 'uid' ][ 0 ], // id of associated contact
2010-03-20 14:24:01 +01:00
'account_created' => isset ( $data [ 'createtimestamp' ][ 0 ]) ? self :: accounts_ldap2ts ( $data [ 'createtimestamp' ][ 0 ]) : null ,
'account_modified' => isset ( $data [ 'modifytimestamp' ][ 0 ]) ? self :: accounts_ldap2ts ( $data [ 'modifytimestamp' ][ 0 ]) : null ,
2006-06-07 01:42:36 +02:00
);
2006-08-24 08:27:03 +02:00
//echo "<p align=right>accounts_ldap::_read_user($account_id): shadowexpire={$data['shadowexpire'][0]} --> account_expires=$user[account_expires]=".date('Y-m-d H:i',$user['account_expires'])."</p>\n";
2007-12-13 03:32:44 +01:00
if ( $this -> frontend -> config [ 'ldap_extra_attributes' ])
2006-06-07 01:42:36 +02:00
{
$user [ 'homedirectory' ] = $data [ 'homedirectory' ][ 0 ];
$user [ 'loginshell' ] = $data [ 'loginshell' ][ 0 ];
}
return $user ;
}
2004-06-13 22:11:31 +02:00
2006-06-07 01:42:36 +02:00
/**
* Merges the group releavant account data from $data into $to_write
*
2008-06-05 09:42:21 +02:00
* @ internal
2006-06-07 01:42:36 +02:00
* @ param array $to_write data to write to ldap incl . objectclass ( $data is NOT yet merged )
* @ param array $data array with account - data in utf - 8
* @ return array merged data
*/
2009-08-22 09:13:59 +02:00
protected function _merge_group ( $to_write , $data )
2006-06-07 01:42:36 +02:00
{
$to_write [ 'gidnumber' ] = abs ( $data [ 'account_id' ]);
$to_write [ 'cn' ] = $data [ 'account_lid' ];
2006-10-11 23:59:43 +02:00
2006-06-07 01:42:36 +02:00
return $to_write ;
}
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
/**
* Merges the user releavant account data from $data into $to_write
*
2008-06-05 09:42:21 +02:00
* @ internal
2006-06-07 01:42:36 +02:00
* @ param array $to_write data to write to ldap incl . objectclass ( $data is NOT yet merged )
* @ param array $data array with account - data in utf - 8
* @ param boolean $new_entry
* @ return array merged data
*/
2009-08-22 09:13:59 +02:00
protected function _merge_user ( $to_write , $data , $new_entry )
2006-06-07 01:42:36 +02:00
{
//echo "<p>accounts_ldap::_merge_user(".print_r($to_write,true).','.print_r($data,true).",$new_entry)</p>\n";
$to_write [ 'uidnumber' ] = $data [ 'account_id' ];
$to_write [ 'uid' ] = $data [ 'account_lid' ];
$to_write [ 'gidnumber' ] = abs ( $data [ 'account_primary_group' ]);
2006-06-07 19:58:20 +02:00
if ( ! $new_entry || $data [ 'account_firstname' ])
{
$to_write [ 'givenname' ] = $data [ 'account_firstname' ] ? $data [ 'account_firstname' ] : array ();
}
2006-06-07 01:42:36 +02:00
$to_write [ 'sn' ] = $data [ 'account_lastname' ];
2006-06-07 19:58:20 +02:00
if ( ! $new_entry || $data [ 'account_email' ])
{
$to_write [ 'mail' ] = $data [ 'account_email' ] ? $data [ 'account_email' ] : array ();
}
2006-06-07 01:42:36 +02:00
$to_write [ 'cn' ] = $data [ 'account_fullname' ] ? $data [ 'account_fullname' ] : $data [ 'account_firstname' ] . ' ' . $data [ 'account_lastname' ];
2008-06-05 09:42:21 +02:00
2009-04-09 08:41:41 +02:00
$utc_diff = date ( 'Z' );
2006-06-07 01:42:36 +02:00
if ( isset ( $data [ 'account_passwd' ]) && $data [ 'account_passwd' ])
{
2012-03-29 20:33:33 +02:00
if ( preg_match ( '/^[a-f0-9]{32}$/' , $data [ 'account_passwd' ])) // md5 --> ldap md5
{
$data [ 'account_passwd' ] = setup_cmd_ldap :: hash_sql2ldap ( $data [ 'account_passwd' ]);
}
elseif ( ! preg_match ( '/^\\{[a-z5]{3,5}\\}.+/i' , $data [ 'account_passwd' ])) // if it's not already entcrypted, do so now
2002-12-18 01:20:46 +01:00
{
2008-03-25 18:02:09 +01:00
$data [ 'account_passwd' ] = auth :: encrypt_ldap ( $data [ 'account_passwd' ]);
2002-12-18 01:20:46 +01:00
}
2006-06-07 01:42:36 +02:00
$to_write [ 'userpassword' ] = $data [ 'account_passwd' ];
2010-11-17 15:39:12 +01:00
$to_write [ 'shadowlastchange' ] = round (( time () - $utc_diff ) / ( 24 * 3600 ));
2001-02-14 20:27:37 +01:00
}
2006-06-07 01:42:36 +02:00
// both status and expires are encoded in the single shadowexpire value in LDAP
// - if it's unset an account is enabled AND does never expire
// - if it's set to 0, the account is disabled
// - if it's set to > 0, it will or already has expired --> acount is active if it not yet expired
2006-08-24 08:27:03 +02:00
// shadowexpire is in days since 1970/01/01 (equivalent to a timestamp (int UTC!) / (24*60*60)
$shadowexpire = ( $data [ 'account_expires' ] - $utc_diff ) / ( 24 * 3600 );
//echo "<p align=right>account_expires=".date('Y-m-d H:i',$data['account_expires'])." --> $shadowexpire --> ".date('Y-m-d H:i',$account_expire)."</p>\n";
2008-06-05 09:42:21 +02:00
$to_write [ 'shadowexpire' ] = ! $data [ 'account_status' ] ?
2006-08-24 08:27:03 +02:00
( $data [ 'account_expires' ] != - 1 && $data [ 'account_expires' ] < time () ? round ( $shadowexpire ) : 0 ) :
( $data [ 'account_expires' ] != - 1 ? round ( $shadowexpire ) : array ()); // array() = unset value
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
if ( $new_entry && is_array ( $to_write [ 'shadowexpire' ]) && ! count ( $to_write [ 'shadowexpire' ]))
2001-03-10 13:27:22 +01:00
{
2006-06-07 01:42:36 +02:00
unset ( $to_write [ 'shadowexpire' ]); // gives protocoll error otherwise
}
2010-09-24 10:20:14 +02:00
//error_log(__METHOD__.__LINE__.$data['account_lid'].'#'.$data['account_lastpwd_change'].'#');
if ( $data [ 'account_lastpwd_change' ]) $to_write [ 'shadowlastchange' ] = round (( $data [ 'account_lastpwd_change' ] - $utc_diff ) / ( 24 * 3600 ));
2010-11-17 15:39:12 +01:00
if ( isset ( $data [ 'account_lastpwd_change' ]) && $data [ 'account_lastpwd_change' ] == 0 ) $to_write [ 'shadowlastchange' ] = 0 ;
2006-06-07 01:42:36 +02:00
// lastlogin and lastlogin from are not availible via the shadowAccount object class
// $to_write['phpgwaccountlastlogin'] = $data['lastlogin'];
// $to_write['phpgwaccountlastloginfrom'] = $data['lastloginfrom'];
2001-09-03 04:17:10 +02:00
2007-12-13 03:32:44 +01:00
if ( $this -> frontend -> config [ 'ldap_extra_attributes' ])
2006-06-07 01:42:36 +02:00
{
if ( isset ( $data [ 'homedirectory' ])) $to_write [ 'homedirectory' ] = $data [ 'homedirectory' ];
if ( isset ( $data [ 'loginshell' ])) $to_write [ 'loginshell' ] = $data [ 'loginshell' ] ? $data [ 'loginshell' ] : array ();
}
if ( $new_entry && ! isset ( $to_write [ 'homedirectory' ]))
{
$to_write [ 'homedirectory' ] = '/dev/null' ; // is a required attribute of posixAccount
2001-03-10 13:27:22 +01:00
}
2006-06-07 01:42:36 +02:00
return $to_write ;
}
2001-03-10 13:27:22 +01:00
2006-06-07 01:42:36 +02:00
/**
2006-09-15 18:19:39 +02:00
* Searches / lists accounts : users and / or groups
2006-06-07 01:42:36 +02:00
*
2006-09-15 18:19:39 +02:00
* @ param array with the following keys :
* @ param $param [ 'type' ] string / int 'accounts' , 'groups' , 'owngroups' ( groups the user is a member of ), 'both'
* or integer group - id for a list of members of that group
* @ param $param [ 'start' ] int first account to return ( returns offset or max_matches entries ) or all if not set
2007-01-08 09:58:48 +01:00
* @ param $param [ 'order' ] string column to sort after , default account_lid if unset
* @ param $param [ 'sort' ] string 'ASC' or 'DESC' , default 'DESC' if not set
2006-09-15 18:19:39 +02:00
* @ param $param [ 'query' ] string to search for , no search if unset or empty
* @ param $param [ 'query_type' ] string :
* 'all' - query all fields for containing $param [ query ]
* 'start' - query all fields starting with $param [ query ]
* 'exact' - query all fields for exact $param [ query ]
* 'lid' , 'firstname' , 'lastname' , 'email' - query only the given field for containing $param [ query ]
* @ param $param [ 'offset' ] int - number of matches to return if start given , default use the value in the prefs
2012-11-19 14:19:14 +01:00
* @ param $param [ 'objectclass' ] boolean return objectclass ( es ) under key 'objectclass' in each account
2010-03-20 14:24:01 +01:00
* @ return array with account_id => data pairs , data is an array with account_id , account_lid , account_firstname ,
2006-09-15 18:19:39 +02:00
* account_lastname , person_id ( id of the linked addressbook entry ), account_status , account_expires , account_primary_group
2006-06-07 01:42:36 +02:00
*/
2006-09-15 18:19:39 +02:00
function search ( $param )
2006-06-07 01:42:36 +02:00
{
2007-12-13 03:32:44 +01:00
//echo "<p>accounts_ldap::search(".print_r($param,true)."): ".microtime()."</p>\n";
2013-05-22 19:22:20 +02:00
$account_search =& accounts :: $cache [ 'account_search' ];
2008-06-05 09:42:21 +02:00
2006-09-15 18:19:39 +02:00
// check if the query is cached
$serial = serialize ( $param );
if ( isset ( $account_search [ $serial ]))
{
$this -> total = $account_search [ $serial ][ 'total' ];
return $account_search [ $serial ][ 'data' ];
}
// if it's a limited query, check if the unlimited query is cached
$start = $param [ 'start' ];
if ( ! ( $maxmatchs = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ])) $maxmatchs = 15 ;
2009-04-23 13:49:44 +02:00
if ( ! ( $offset = $param [ 'offset' ])) $offset = $maxmatchs ;
2006-09-15 18:19:39 +02:00
unset ( $param [ 'start' ]);
unset ( $param [ 'offset' ]);
$unl_serial = serialize ( $param );
if ( isset ( $account_search [ $unl_serial ]))
2001-03-10 13:27:22 +01:00
{
2006-09-15 18:19:39 +02:00
$this -> total = $account_search [ $unl_serial ][ 'total' ];
$sortedAccounts = $account_search [ $unl_serial ][ 'data' ];
}
else // we need to run the unlimited query
{
$query = ldap :: quote ( strtolower ( $param [ 'query' ]));
2008-06-05 09:42:21 +02:00
2007-01-04 07:54:33 +01:00
$accounts = array ();
2006-09-15 18:19:39 +02:00
if ( $param [ 'type' ] != 'groups' )
2006-06-07 01:42:36 +02:00
{
2006-09-15 18:19:39 +02:00
$filter = " (&(objectclass=posixaccount) " ;
if ( ! empty ( $query ) && $query != '*' )
{
switch ( $param [ 'query_type' ])
{
case 'all' :
default :
$query = '*' . $query ;
// fall-through
case 'start' :
$query .= '*' ;
// fall-through
case 'exact' :
$filter .= " (|(uid= $query )(sn= $query )(cn= $query )(givenname= $query )(mail= $query )) " ;
break ;
case 'firstname' :
case 'lastname' :
case 'lid' :
case 'email' :
$to_ldap = array (
'firstname' => 'givenname' ,
'lastname' => 'sn' ,
'lid' => 'uid' ,
'email' => 'mail' ,
);
$filter .= '(' . $to_ldap [ $param [ 'query_type' ]] . '=*' . $query . '*)' ;
break ;
}
}
2006-10-28 11:32:04 +02:00
// add account_filter to filter (user has to be '*', as we otherwise only search uid's)
2014-10-22 19:10:41 +02:00
$filter .= str_replace ( array ( '%user' , '%domain' ), array ( '*' , $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ]), $this -> account_filter );
2006-09-15 18:19:39 +02:00
$filter .= ')' ;
2006-10-10 09:33:43 +02:00
2010-04-20 13:07:02 +02:00
if ( $param [ 'type' ] != 'both' )
2008-02-12 16:57:39 +01:00
{
2010-04-20 13:07:02 +02:00
// folw:
// - first query only few attributes for sorting and throwing away not needed results
// - throw away & sort
// - fetch relevant accounts with full information
// - map and resolve
$propertyMap = array (
'account_id' => 'uidnumber' ,
'account_lid' => 'uid' ,
'account_firstname' => 'givenname' ,
'account_lastname' => 'sn' ,
'account_email' => 'email' ,
'account_fullname' => 'cn' ,
2014-10-22 19:10:41 +02:00
'account_primary_group' => 'gidnumber' ,
2010-04-20 13:07:02 +02:00
);
$orders = explode ( ',' , $param [ 'order' ]);
$order = isset ( $propertyMap [ $orders [ 0 ]]) ? $propertyMap [ $orders [ 0 ]] : 'uid' ;
$sri = ldap_search ( $this -> ds , $this -> user_context , $filter , array ( 'uid' , $order ));
$fullSet = array ();
foreach (( array ) ldap_get_entries ( $this -> ds , $sri ) as $key => $entry )
2008-02-12 16:57:39 +01:00
{
2010-04-20 13:07:02 +02:00
if ( $key !== 'count' ) $fullSet [ $entry [ 'uid' ][ 0 ]] = $entry [ $order ][ 0 ];
2008-02-12 16:57:39 +01:00
}
2011-01-02 22:53:04 +01:00
2010-04-20 13:07:02 +02:00
if ( is_numeric ( $param [ 'type' ])) // return only group-members
{
$relevantAccounts = array ();
$sri = ldap_search ( $this -> ds , $this -> group_context , " (&(objectClass=posixGroup)(gidnumber= " . abs ( $param [ 'type' ]) . " )) " , array ( 'memberuid' ));
$group = ldap_get_entries ( $this -> ds , $sri );
2011-01-02 22:53:04 +01:00
2010-04-20 13:07:02 +02:00
if ( isset ( $group [ 0 ][ 'memberuid' ]))
{
$fullSet = array_intersect_key ( $fullSet , array_flip ( $group [ 0 ][ 'memberuid' ]));
}
}
$totalcount = count ( $fullSet );
2011-01-02 22:53:04 +01:00
2010-04-20 13:07:02 +02:00
$sortFn = $param [ 'sort' ] == 'DESC' ? 'arsort' : 'asort' ;
$sortFn ( $fullSet );
$relevantAccounts = is_numeric ( $start ) ? array_slice ( array_keys ( $fullSet ), $start , $offset ) : array_keys ( $fullSet );
$filter = '(&(objectclass=posixaccount)(|(uid=' . implode ( ')(uid=' , $relevantAccounts ) . '))' . $this -> account_filter . ')' ;
$filter = str_replace ( array ( '%user' , '%domain' ), array ( '*' , $GLOBALS [ 'egw_info' ][ 'user' ][ 'domain' ]), $filter );
2008-02-12 16:57:39 +01:00
}
2014-10-22 19:10:41 +02:00
$sri = ldap_search ( $this -> ds , $this -> user_context , $filter , array ( 'uid' , 'uidNumber' , 'givenname' , 'sn' , 'mail' , 'shadowExpire' , 'createtimestamp' , 'modifytimestamp' , 'objectclass' , 'gidNumber' ));
2006-09-15 18:19:39 +02:00
//echo "<p>ldap_search(,$this->user_context,'$filter',) ".($sri ? '' : ldap_error($this->ds)).microtime()."</p>\n";
2008-06-05 09:42:21 +02:00
2006-09-15 18:19:39 +02:00
$utc_diff = date ( 'Z' );
2010-04-20 13:07:02 +02:00
foreach ( ldap_get_entries ( $this -> ds , $sri ) as $allVals )
2006-06-07 01:42:36 +02:00
{
2006-09-15 18:19:39 +02:00
settype ( $allVals , 'array' );
$test = @ $allVals [ 'uid' ][ 0 ];
2007-12-13 03:32:44 +01:00
if ( ! $this -> frontend -> config [ 'global_denied_users' ][ $test ] && $allVals [ 'uid' ][ 0 ])
2006-09-15 18:19:39 +02:00
{
2010-04-20 13:07:02 +02:00
$account = Array (
2006-09-15 18:19:39 +02:00
'account_id' => $allVals [ 'uidnumber' ][ 0 ],
2010-03-20 14:24:01 +01:00
'account_lid' => translation :: convert ( $allVals [ 'uid' ][ 0 ], 'utf-8' ),
2006-09-15 18:19:39 +02:00
'account_type' => 'u' ,
2010-03-20 14:24:01 +01:00
'account_firstname' => translation :: convert ( $allVals [ 'givenname' ][ 0 ], 'utf-8' ),
'account_lastname' => translation :: convert ( $allVals [ 'sn' ][ 0 ], 'utf-8' ),
2006-09-15 18:19:39 +02:00
'account_status' => isset ( $allVals [ 'shadowexpire' ][ 0 ]) && $allVals [ 'shadowexpire' ][ 0 ] * 24 * 3600 - $utc_diff < time () ? false : 'A' ,
2014-10-22 19:10:41 +02:00
'account_expires' => isset ( $allVals [ 'shadowexpire' ]) && $allVals [ 'shadowexpire' ][ 0 ] ? $allVals [ 'shadowexpire' ][ 0 ] * 24 * 3600 + $utc_diff : - 1 , // LDAP date is in UTC
2006-09-15 18:19:39 +02:00
'account_email' => $allVals [ 'mail' ][ 0 ],
2010-03-20 14:24:01 +01:00
'account_created' => isset ( $allVals [ 'createtimestamp' ][ 0 ]) ? self :: accounts_ldap2ts ( $allVals [ 'createtimestamp' ][ 0 ]) : null ,
'account_modified' => isset ( $allVals [ 'modifytimestamp' ][ 0 ]) ? self :: accounts_ldap2ts ( $allVals [ 'modifytimestamp' ][ 0 ]) : null ,
2014-10-22 19:10:41 +02:00
'account_primary_group' => ( string ) - $allVals [ 'gidnumber' ][ 0 ],
2006-06-07 01:42:36 +02:00
);
2014-10-22 19:10:41 +02:00
error_log ( __METHOD__ . " () ldap= " . array2string ( $allVals ) . " --> account= " . array2string ( $account ));
2013-01-25 14:21:31 +01:00
if ( $param [ 'active' ] && ! $this -> frontend -> is_active ( $account ))
{
2013-06-26 10:12:17 +02:00
if ( isset ( $totalcount )) -- $totalcount ;
2013-01-25 14:21:31 +01:00
continue ;
}
$account [ 'account_fullname' ] = common :: display_fullname ( $account [ 'account_lid' ], $account [ 'account_firstname' ], $account [ 'account_lastname' ], $allVals [ 'uidnumber' ][ 0 ]);
2012-11-19 14:19:14 +01:00
// return objectclass(es)
if ( $param [ 'objectclass' ])
{
$account [ 'objectclass' ] = $allVals [ 'objectclass' ];
unset ( $account [ 'objectclass' ][ 'count' ]);
}
2010-04-20 13:07:02 +02:00
$accounts [ $account [ 'account_id' ]] = $account ;
2006-09-15 18:19:39 +02:00
}
2004-07-12 23:25:51 +02:00
}
2001-06-26 11:51:16 +02:00
}
2006-09-15 18:19:39 +02:00
if ( $param [ 'type' ] == 'groups' || $param [ 'type' ] == 'both' )
2006-06-07 01:42:36 +02:00
{
2006-09-15 18:19:39 +02:00
if ( empty ( $query ) || $query == '*' )
2006-06-07 01:42:36 +02:00
{
2006-09-15 18:19:39 +02:00
$filter = '(objectclass=posixgroup)' ;
2006-06-07 01:42:36 +02:00
}
2006-09-15 18:19:39 +02:00
else
2006-06-07 01:42:36 +02:00
{
2009-04-23 08:24:58 +02:00
switch ( $param [ 'query_type' ])
{
case 'all' :
default :
$query = '*' . $query ;
// fall-through
case 'start' :
$query .= '*' ;
// fall-through
case 'exact' :
break ;
}
$filter = " (&(objectclass=posixgroup)(cn= $query )) " ;
2006-09-15 18:19:39 +02:00
}
$sri = ldap_search ( $this -> ds , $this -> group_context , $filter , array ( 'cn' , 'gidNumber' ));
2010-04-20 13:07:02 +02:00
foreach (( array ) ldap_get_entries ( $this -> ds , $sri ) as $allVals )
2006-09-15 18:19:39 +02:00
{
settype ( $allVals , 'array' );
$test = $allVals [ 'cn' ][ 0 ];
2007-12-13 03:32:44 +01:00
if ( ! $this -> frontend -> config [ 'global_denied_groups' ][ $test ] && $allVals [ 'cn' ][ 0 ])
2006-09-15 18:19:39 +02:00
{
2010-03-20 14:24:01 +01:00
$accounts [( string ) - $allVals [ 'gidnumber' ][ 0 ]] = Array (
2006-09-15 18:19:39 +02:00
'account_id' => - $allVals [ 'gidnumber' ][ 0 ],
2010-03-20 14:24:01 +01:00
'account_lid' => translation :: convert ( $allVals [ 'cn' ][ 0 ], 'utf-8' ),
2006-09-15 18:19:39 +02:00
'account_type' => 'g' ,
2010-03-20 14:24:01 +01:00
'account_firstname' => translation :: convert ( $allVals [ 'cn' ][ 0 ], 'utf-8' ),
2006-09-15 18:19:39 +02:00
'account_lastname' => lang ( 'Group' ),
'account_status' => 'A' ,
2010-04-20 13:07:02 +02:00
'account_fullname' => translation :: convert ( $allVals [ 'cn' ][ 0 ], 'utf-8' ),
2006-09-15 18:19:39 +02:00
);
2010-04-20 13:07:02 +02:00
if ( isset ( $totalcount )) ++ $totalcount ;
2006-09-15 18:19:39 +02:00
}
2006-06-07 01:42:36 +02:00
}
2001-09-03 04:17:10 +02:00
}
2006-09-15 18:19:39 +02:00
// sort the array
2010-03-20 14:24:01 +01:00
$this -> _callback_sort = strtoupper ( $param [ 'sort' ]);
$this -> _callback_order = empty ( $param [ 'order' ]) ? array ( 'account_lid' ) : explode ( ',' , $param [ 'order' ]);
2010-03-28 17:01:58 +02:00
$sortedAccounts = $accounts ;
uasort ( $sortedAccounts , array ( $this , '_sort_callback' ));
2013-06-26 10:12:17 +02:00
$this -> total = isset ( $totalcount ) ? $totalcount : count ( $accounts );
2010-03-20 14:24:01 +01:00
2013-06-26 10:12:17 +02:00
// if totalcount is set, $sortedAccounts is NOT the full set, but already a limited set!
if ( ! isset ( $totalcount ))
{
$account_search [ $unl_serial ][ 'data' ] = $sortedAccounts ;
$account_search [ $unl_serial ][ 'total' ] = $this -> total ;
}
2006-06-07 01:42:36 +02:00
}
2007-12-13 03:32:44 +01:00
//echo "<p>accounts_ldap::search() found $this->total: ".microtime()."</p>\n";
2006-06-07 01:42:36 +02:00
// return only the wanted accounts
2007-01-04 07:54:33 +01:00
reset ( $sortedAccounts );
if ( is_numeric ( $start ) && is_numeric ( $offset ))
2006-06-07 01:42:36 +02:00
{
2007-01-04 07:54:33 +01:00
$account_search [ $serial ][ 'total' ] = $this -> total ;
2008-02-12 16:57:39 +01:00
return $account_search [ $serial ][ 'data' ] = isset ( $totalcount ) ? $sortedAccounts : array_slice ( $sortedAccounts , $start , $offset );
2001-03-10 13:27:22 +01:00
}
2007-01-04 07:54:33 +01:00
return $sortedAccounts ;
2006-06-07 01:42:36 +02:00
}
2010-03-20 14:24:01 +01:00
/**
* DESC or ASC
*
* @ var string
*/
private $_callback_sort = 'DESC' ;
/**
* column_names to sort by
*
* @ var array
*/
private $_callback_order = array ( 'account_lid' );
/**
* Sort callback for uasort
*
* @ param array $a
* @ param array $b
* @ return int
*/
function _sort_callback ( $a , $b )
{
foreach ( $this -> _callback_order as $col )
{
if ( $this -> _callback_sort == 'ASC' )
{
$cmp = strcasecmp ( $a [ $col ], $b [ $col ] );
}
else
{
$cmp = strcasecmp ( $b [ $col ], $a [ $col ] );
}
if ( $cmp != 0 )
{
return $cmp ;
}
}
return 0 ;
}
2008-04-10 11:06:24 +02:00
/**
* Creates a timestamp from the date returned by the ldap server
*
* @ internal
* @ param string $date YYYYmmddHHiiss
* @ return int
*/
2010-03-20 14:24:01 +01:00
protected static function accounts_ldap2ts ( $date )
2008-04-10 11:06:24 +02:00
{
2010-03-20 14:24:01 +01:00
if ( ! empty ( $date ))
2008-06-05 09:42:21 +02:00
{
2008-04-10 11:06:24 +02:00
return gmmktime ( substr ( $date , 8 , 2 ), substr ( $date , 10 , 2 ), substr ( $date , 12 , 2 ),
substr ( $date , 4 , 2 ), substr ( $date , 6 , 2 ), substr ( $date , 0 , 4 ));
}
return NULL ;
}
2006-06-07 01:42:36 +02:00
/**
* convert an alphanumeric account - value ( account_lid , account_email ) to the account_id
*
* Please note :
* - if a group and an user have the same account_lid the group will be returned ( LDAP only )
* - if multiple user have the same email address , the returned user is undefined
2008-06-05 09:42:21 +02:00
*
2014-10-22 19:10:41 +02:00
* @ param string $_name value to convert
* @ param string $which = 'account_lid' type of $name : account_lid ( default ), account_email , person_id , account_fullname
2006-06-07 01:42:36 +02:00
* @ param string $account_type u = user , g = group , default null = try both
2009-08-22 09:13:59 +02:00
* @ return int | false numeric account_id or false on error ( $name not found )
2006-06-07 01:42:36 +02:00
*/
2014-10-22 19:10:41 +02:00
function name2id ( $_name , $which = 'account_lid' , $account_type = null )
2006-06-07 01:42:36 +02:00
{
2014-10-22 19:10:41 +02:00
$name = ldap :: quote ( translation :: convert ( $_name , translation :: charset (), 'utf-8' ));
2001-03-10 13:27:22 +01:00
2006-06-07 01:42:36 +02:00
if ( $which == 'account_lid' && $account_type !== 'u' ) // groups only support account_lid
2001-03-10 13:27:22 +01:00
{
2001-06-27 03:31:32 +02:00
2013-05-22 19:22:20 +02:00
$sri = ldap_search ( $this -> ds , $this -> group_context , '(&(cn=' . $name . ')(objectclass=posixgroup))' , array ( 'gidNumber' ));
2001-09-06 02:17:23 +02:00
$allValues = ldap_get_entries ( $this -> ds , $sri );
2001-03-14 19:54:30 +01:00
2006-06-07 01:42:36 +02:00
if ( @ $allValues [ 0 ][ 'gidnumber' ][ 0 ])
2001-05-06 13:32:03 +02:00
{
2006-06-07 01:42:36 +02:00
return - $allValues [ 0 ][ 'gidnumber' ][ 0 ];
2003-09-14 08:02:25 +02:00
}
2006-06-07 01:42:36 +02:00
}
$to_ldap = array (
'account_lid' => 'uid' ,
'account_email' => 'mail' ,
'account_fullname' => 'cn' ,
);
2008-02-08 12:16:40 +01:00
if ( ! isset ( $to_ldap [ $which ]) || $account_type === 'g' ) {
return False ;
}
2001-03-29 05:40:14 +02:00
2013-05-22 19:22:20 +02:00
$sri = ldap_search ( $this -> ds , $this -> user_context , '(&(' . $to_ldap [ $which ] . '=' . $name . ')(objectclass=posixaccount))' , array ( 'uidNumber' ));
2001-04-05 20:55:44 +02:00
2006-06-07 01:42:36 +02:00
$allValues = ldap_get_entries ( $this -> ds , $sri );
2001-09-03 04:17:10 +02:00
2006-06-07 01:42:36 +02:00
if ( @ $allValues [ 0 ][ 'uidnumber' ][ 0 ])
{
return ( int ) $allValues [ 0 ][ 'uidnumber' ][ 0 ];
}
return False ;
}
2008-06-05 09:42:21 +02:00
2007-12-13 03:32:44 +01:00
/**
* Convert an numeric account_id to any other value of that account ( account_lid , account_email , ... )
2008-06-05 09:42:21 +02:00
*
2007-12-13 03:32:44 +01:00
* Uses the read method to fetch all data .
*
* @ param int $account_id numerica account_id
2014-10-22 19:10:41 +02:00
* @ param string $which = 'account_lid' type to convert to : account_lid ( default ), account_email , ...
2007-12-13 03:32:44 +01:00
* @ return string / false converted value or false on error ( $account_id not found )
*/
function id2name ( $account_id , $which = 'account_lid' )
{
return $this -> frontend -> id2name ( $account_id , $which );
}
2004-01-25 01:04:40 +01:00
2006-06-07 01:42:36 +02:00
/**
* Update the last login timestamps and the IP
*
2014-10-22 19:10:41 +02:00
* @ param int $_account_id
2006-06-07 01:42:36 +02:00
* @ param string $ip
* @ return int lastlogin time
*/
function update_lastlogin ( $_account_id , $ip )
{
2014-10-22 19:10:41 +02:00
unset ( $_account_id , $ip );
2006-06-07 01:42:36 +02:00
return false ; // not longer supported
}
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
/**
* Query memberships of a given account
*
* @ param int $account_id
2009-08-22 09:13:59 +02:00
* @ return array | boolean array with account_id => account_lid pairs or false if account not found
2006-06-07 01:42:36 +02:00
*/
function memberships ( $account_id )
{
if ( ! ( int ) $account_id || ! ( $account_lid = $this -> id2name ( $account_id ))) return false ;
2008-06-05 09:42:21 +02:00
2006-06-13 06:23:36 +02:00
$sri = ldap_search ( $this -> ds , $this -> group_context , '(&(objectClass=posixGroup)(memberuid=' . ldap :: quote ( $account_lid ) . '))' , array ( 'cn' , 'gidnumber' ));
2006-06-07 01:42:36 +02:00
$memberships = array ();
2009-11-04 12:17:06 +01:00
foreach (( array ) ldap_get_entries ( $this -> ds , $sri ) as $key => $data )
2006-06-07 01:42:36 +02:00
{
if ( $key === 'count' ) continue ;
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
$memberships [( string ) - $data [ 'gidnumber' ][ 0 ]] = $data [ 'cn' ][ 0 ];
}
//echo "accounts::memberships($account_id)"; _debug_array($memberships);
return $memberships ;
}
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
/**
* Query the members of a group
*
2014-10-22 19:10:41 +02:00
* @ param int $_gid
2006-06-07 01:42:36 +02:00
* @ return array with uidnumber => uid pairs
*/
2014-10-22 19:10:41 +02:00
function members ( $_gid )
2006-06-07 01:42:36 +02:00
{
2014-10-22 19:10:41 +02:00
if ( ! is_numeric ( $_gid ))
2009-11-04 12:17:06 +01:00
{
// try to recover
2014-10-22 19:10:41 +02:00
$_gid = $this -> name2id ( $_gid , 'account_lid' , 'g' );
if ( ! is_numeric ( $_gid )) return false ;
2009-11-04 12:17:06 +01:00
}
2008-06-05 09:42:21 +02:00
2014-10-22 19:10:41 +02:00
$gid = abs ( $_gid ); // our gid is negative!
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
$sri = ldap_search ( $this -> ds , $this -> group_context , " (&(objectClass=posixGroup)(gidnumber= $gid )) " , array ( 'memberuid' ));
$group = ldap_get_entries ( $this -> ds , $sri );
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
$members = array ();
if ( isset ( $group [ 0 ][ 'memberuid' ]))
{
foreach ( $group [ 0 ][ 'memberuid' ] as $lid )
2003-09-14 10:29:29 +02:00
{
2006-06-07 01:42:36 +02:00
if (( $id = $this -> name2id ( $lid )))
2003-09-13 19:14:30 +02:00
{
2006-06-07 01:42:36 +02:00
$members [ $id ] = $lid ;
2003-09-13 19:14:30 +02:00
}
}
2001-03-10 13:27:22 +01:00
}
2006-06-07 01:42:36 +02:00
//echo "accounts_ldap::members($gid)"; _debug_array($members);
return $members ;
}
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
/**
* Sets the memberships of the given account
*
* @ param array $groups array with gidnumbers
2006-06-11 16:27:14 +02:00
* @ param int $account_id uidnumber
2006-06-07 01:42:36 +02:00
*/
2006-06-11 16:27:14 +02:00
function set_memberships ( $groups , $account_id )
2006-06-07 01:42:36 +02:00
{
2006-06-11 16:27:14 +02:00
//echo "<p>accounts_ldap::set_memberships(".print_r($groups,true).",$account_id)</p>\n";
2001-03-10 13:27:22 +01:00
2006-06-07 01:42:36 +02:00
// remove not longer existing memberships
2006-06-11 16:27:14 +02:00
if (( $old_memberships = $this -> memberships ( $account_id )))
2001-03-10 13:27:22 +01:00
{
2006-06-07 01:42:36 +02:00
$old_memberships = array_keys ( $old_memberships );
foreach ( array_diff ( $old_memberships , $groups ) as $gid )
2001-09-01 00:50:30 +02:00
{
2006-06-07 01:42:36 +02:00
if (( $members = $this -> members ( $gid )))
2002-01-05 21:40:44 +01:00
{
2006-06-11 16:27:14 +02:00
unset ( $members [ $account_id ]);
2006-06-07 01:42:36 +02:00
$this -> set_members ( $members , $gid );
2002-01-05 21:40:44 +01:00
}
2001-09-01 00:50:30 +02:00
}
2006-06-07 01:42:36 +02:00
}
// adding new memberships
foreach ( $old_memberships ? array_diff ( $groups , $old_memberships ) : $groups as $gid )
{
$members = $this -> members ( $gid );
2006-06-11 16:27:14 +02:00
$members [ $account_id ] = $this -> id2name ( $account_id );
2006-06-07 01:42:36 +02:00
$this -> set_members ( $members , $gid );
}
}
2008-06-05 09:42:21 +02:00
2006-06-07 01:42:36 +02:00
/**
* Set the members of a group
2008-06-05 09:42:21 +02:00
*
2006-06-07 01:42:36 +02:00
* @ param array $members array with uidnumber or uid ' s
* @ param int $gid gidnumber of group to set
2014-10-22 19:10:41 +02:00
* @ param boolean $groupOfNames = null should we set the member attribute of groupOfNames ( default detect it )
* @ param string $use_cn = null if set $cn is used instead $gid and the attributes are returned , not written to ldap
2006-06-18 07:07:10 +02:00
* @ return boolean / array false on failure , array or true otherwise
2006-06-07 01:42:36 +02:00
*/
2006-06-18 07:07:10 +02:00
function set_members ( $members , $gid , $groupOfNames = null , $use_cn = null )
2006-06-07 01:42:36 +02:00
{
//echo "<p>accounts_ldap::set_members(".print_r($members,true).",$gid)</p>\n";
2006-06-18 07:07:10 +02:00
if ( ! ( $cn = $use_cn ) && ! ( $cn = $this -> id2name ( $gid ))) return false ;
// do that group is a groupOfNames?
if ( is_null ( $groupOfNames )) $groupOfNames = $this -> id2name ( $gid , 'groupOfNames' );
2008-06-05 09:42:21 +02:00
2007-02-05 17:01:29 +01:00
$to_write = array ( 'memberuid' => array ());
2006-06-18 07:07:10 +02:00
foreach (( array ) $members as $key => $member )
2006-06-07 01:42:36 +02:00
{
2006-06-18 07:07:10 +02:00
if ( is_numeric ( $member )) $member = $this -> id2name ( $member );
if ( $member )
2004-08-30 17:16:40 +02:00
{
2006-06-18 07:07:10 +02:00
$to_write [ 'memberuid' ][] = $member ;
if ( $groupOfNames ) $to_write [ 'member' ][] = 'uid=' . $member . ',' . $this -> user_context ;
2001-09-01 00:50:30 +02:00
}
2006-06-07 01:42:36 +02:00
}
2006-06-18 07:07:10 +02:00
if ( $groupOfNames && ! $to_write [ 'member' ])
{
// hack as groupOfNames requires the member attribute
$to_write [ 'member' ][] = 'uid=dummy' . ',' . $this -> user_context ;
}
if ( $use_cn ) return $to_write ;
2006-10-11 23:59:43 +02:00
// set the member email addresses as forwards
if ( $this -> id2name ( $gid , 'account_email' ) && ( $objectclass = $this -> id2name ( $gid , 'mailAllowed' )))
{
$forward = $this -> group_mail_classes [ $objectclass ];
2010-07-21 16:39:07 +02:00
if ( is_array ( $forward )) list ( $forward , $extra_attr ) = $forward ;
if ( $extra_attr && ( $uid = $this -> id2name ( $gid ))) $to_write [ $extra_attr ] = $uid ;
2011-01-02 22:53:04 +01:00
2006-10-11 23:59:43 +02:00
$to_write [ $forward ] = array ();
foreach ( $members as $key => $member )
{
if (( $email = $this -> id2name ( $member , 'account_email' ))) $to_write [ $forward ][] = $email ;
}
}
2006-06-18 07:07:10 +02:00
if ( ! ldap_modify ( $this -> ds , 'cn=' . ldap :: quote ( $cn ) . ',' . $this -> group_context , $to_write ))
2006-06-07 19:58:20 +02:00
{
2006-06-18 07:07:10 +02:00
echo " ldap_modify(,'cn= $cn , $this->group_context ', " . print_r ( $to_write , true ) . " )) \n " ;
return false ;
2006-06-07 19:58:20 +02:00
}
2006-06-18 07:07:10 +02:00
return true ;
2006-06-07 01:42:36 +02:00
}
2004-06-18 11:29:11 +02:00
2006-06-07 01:42:36 +02:00
/**
* Using the common functions next_id and last_id , find the next available account_id
*
2008-06-05 09:42:21 +02:00
* @ internal
2014-10-22 19:10:41 +02:00
* @ param string $account_type = 'u' ( optional , default to 'u' )
2009-08-22 09:13:59 +02:00
* @ return int | boolean integer account_id ( negative for groups ) or false if none is free anymore
2006-06-07 01:42:36 +02:00
*/
2009-08-22 09:13:59 +02:00
protected function _get_nextid ( $account_type = 'u' )
2006-06-07 01:42:36 +02:00
{
2007-12-13 03:32:44 +01:00
$min = $this -> frontend -> config [ 'account_min_id' ] ? $this -> frontend -> config [ 'account_min_id' ] : 0 ;
$max = $this -> frontend -> config [ 'account_max_id' ] ? $this -> frontend -> config [ 'account_max_id' ] : 0 ;
2001-03-10 13:27:22 +01:00
2013-05-25 13:07:38 +02:00
// prefer ids above 1000 (below reserved for system users under AD or Linux),
// if that's possible within what is configured, or nothing is configured
if ( $min < 1000 && ( ! $max || $max > 1000 )) $min = 1000 ;
2006-06-07 01:42:36 +02:00
if ( $account_type == 'g' )
2001-09-06 00:46:47 +02:00
{
2006-06-07 01:42:36 +02:00
$type = 'groups' ;
$sign = - 1 ;
2002-03-14 03:26:21 +01:00
}
2006-06-07 01:42:36 +02:00
else
2001-03-10 13:27:22 +01:00
{
2006-06-07 01:42:36 +02:00
$type = 'accounts' ;
$sign = 1 ;
2001-03-10 13:27:22 +01:00
}
2006-06-07 01:42:36 +02:00
/* Loop until we find a free id */
do
2005-11-02 12:45:52 +01:00
{
2013-05-25 13:07:38 +02:00
$account_id = ( int ) common :: next_id ( $type , $min , $max );
2008-06-05 09:42:21 +02:00
}
2013-05-25 13:07:38 +02:00
while ( $account_id && ( $this -> frontend -> exists ( $sign * $account_id ) || // check need to include the sign!
$this -> frontend -> exists ( - 1 * $sign * $account_id ) ||
// if sambaadmin is installed, call it to check there's not yet a relative id (last part of SID) with that number
// to ease migration to AD or Samba4
$GLOBALS [ 'egw_info' ][ 'apps' ][ 'sambaadmin' ] && ExecMethod2 ( 'sambaadmin.sosambaadmin.sidExists' , $account_id )));
2005-11-02 12:45:52 +01:00
2013-05-25 13:07:38 +02:00
if ( ! $account_id || $max && $account_id > $max )
2006-06-07 01:42:36 +02:00
{
return False ;
2005-11-02 12:45:52 +01:00
}
2006-06-09 00:04:44 +02:00
return $sign * $account_id ;
2002-09-17 04:33:03 +02:00
}
2008-06-05 09:42:21 +02:00
2007-12-20 20:38:26 +01:00
/**
* __wakeup function gets called by php while unserializing the object to reconnect with the ldap server
*/
function __wakeup ()
{
$this -> ds = $this -> ldap -> ldapConnect ( $this -> frontend -> config [ 'ldap_host' ],
$this -> frontend -> config [ 'ldap_root_dn' ], $this -> frontend -> config [ 'ldap_root_pw' ]);
}
2006-06-07 01:42:36 +02:00
}