improved emulation of old contact class

This commit is contained in:
Ralf Becker 2006-06-18 05:07:10 +00:00
parent d7fee19ad7
commit 93c8753b73
3 changed files with 386 additions and 21 deletions

View File

@ -5,4 +5,25 @@ eGroupWare needs no more special LDAP schemas since version 1.3.007:
- valid eGroupWare users have a posixAccount and shadowAccount object class.
- valid Groups have a posixGroup object class and store there members in the memberuid attribute.
Ralf
If you want to use group-addressbooks in LDAP, the ACL requires that groups get expanded by the LDAP server.
To do so, we need to use groupOfNames together with posixGroup (groupOfNames stores the dn, posixGroup only the uid).
If your LDAP uses the original nis.schema, posixGroup is a structural object and can NOT be used together!
Newer SuSE distributions use a rfc2307bis schema, which can be used on other distributions too
(instead of the nis.schema, NOT together). The schema is in the same directory as this README.
To change to the rfc2307bis.schema (not needed with newer SuSE distros!):
----------------------------------
- create an ldif from your ldap: slapcat > my.ldif
- add objectclass groupOfNames to every group (only the groups!)
- edit your slapd.conf:
+ remove the include of the nis.schema
+ include the rfc2307bis.schema in this dir
- stoping ldap
- empty the ldap database (eg. by removing the content of /var/lib/ldap)
- add the edited ldif file
- start ldap again
eGroupWare detects if it can use groupOfNames together with posixGroup and fills the member attribute,
if you edit the group or changes the members.
Ralf

View File

@ -0,0 +1,296 @@
#
# This schema is a RFC draft and replaces the nis.schema
#
# You can NOT install it together with the nis.schema!
#
# The purpose of it is, to use posixGroup together with groupOfNames
#
# uidNumber and gidNumber might be buildin, in that case you
# need to comment out both (putting a # infront the whole entry)
#
# $Id$
#
attributetype ( 1.3.6.1.1.1.1.0 NAME 'uidNumber'
DESC 'An integer uniquely identifying a user in an administrative domain'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber'
DESC 'An integer uniquely identifying a group in an
administrative domain'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.2 NAME 'gecos'
DESC 'The GECOS field; the common name'
EQUALITY caseIgnoreIA5Match
SUBSTR caseIgnoreIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory'
DESC 'The absolute path to the home directory'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.4 NAME 'loginShell'
DESC 'The path to the login shell'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.6 NAME 'shadowMin'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.7 NAME 'shadowMax'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple'
DESC 'Netgroup triple'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort'
DESC 'Service port number'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol'
DESC 'Service protocol name'
SUP name )
attributetype ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber'
DESC 'IP protocol number'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber'
DESC 'ONC RPC number'
EQUALITY integerMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber'
DESC 'IPv4 addresses as a dotted decimal omitting leading
zeros or IPv6 addresses as defined in RFC2373'
SUP name )
attributetype ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber'
DESC 'IP network as a dotted decimal, eg. 192.168,
omitting leading zeros'
SUP name
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber'
DESC 'IP netmask as a dotted decimal, eg. 255.255.255.0,
omitting leading zeros'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.22 NAME 'macAddress'
DESC 'MAC address in maximal, colon separated hex
notation, eg. 00:00:92:90:ee:e2'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.1.1.1.23 NAME 'bootParameter'
DESC 'rpc.bootparamd parameter'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.1.1.1.24 NAME 'bootFile'
DESC 'Boot image name'
EQUALITY caseExactIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
attributetype ( 1.3.6.1.1.1.1.26 NAME 'nisMapName'
DESC 'Name of a A generic NIS map'
SUP name )
attributetype ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry'
DESC 'A generic NIS entry'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.28 NAME 'nisPublicKey'
DESC 'NIS public key'
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.29 NAME 'nisSecretKey'
DESC 'NIS secret key'
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.30 NAME 'nisDomain'
DESC 'NIS domain'
EQUALITY caseIgnoreIA5Match
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26)
attributetype ( 1.3.6.1.1.1.1.31 NAME 'automountMapName'
DESC 'automount Map Name'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.32 NAME 'automountKey'
DESC 'Automount Key value'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
attributetype ( 1.3.6.1.1.1.1.33 NAME 'automountInformation'
DESC 'Automount information'
EQUALITY caseExactIA5Match
SUBSTR caseExactIA5SubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' SUP top AUXILIARY
DESC 'Abstraction of an account with POSIX attributes'
MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
MAY ( userPassword $ loginShell $ gecos $
description ) )
objectclass ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' SUP top AUXILIARY
DESC 'Additional attributes for shadow passwords'
MUST uid
MAY ( userPassword $ description $
shadowLastChange $ shadowMin $ shadowMax $
shadowWarning $ shadowInactive $
shadowExpire $ shadowFlag ) )
objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' SUP top AUXILIARY
DESC 'Abstraction of a group of accounts'
MUST gidNumber
MAY ( userPassword $ memberUid $
description ) )
objectclass ( 1.3.6.1.1.1.2.3 NAME 'ipService' SUP top STRUCTURAL
DESC 'Abstraction an Internet Protocol service.
Maps an IP port and protocol (such as tcp or udp)
to one or more names; the distinguished value of
the cn attribute denotes the services canonical
name'
MUST ( cn $ ipServicePort $ ipServiceProtocol )
MAY description )
objectclass ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' SUP top STRUCTURAL
DESC 'Abstraction of an IP protocol. Maps a protocol number
to one or more names. The distinguished value of the cn
attribute denotes the protocols canonical name'
MUST ( cn $ ipProtocolNumber )
MAY description )
objectclass ( 1.3.6.1.1.1.2.5 NAME 'oncRpc' SUP top STRUCTURAL
DESC 'Abstraction of an Open Network Computing (ONC)
[RFC1057] Remote Procedure Call (RPC) binding.
This class maps an ONC RPC number to a name.
The distinguished value of the cn attribute denotes
the RPC services canonical name'
MUST ( cn $ oncRpcNumber )
MAY description )
objectclass ( 1.3.6.1.1.1.2.6 NAME 'ipHost' SUP top AUXILIARY
DESC 'Abstraction of a host, an IP device. The distinguished
value of the cn attribute denotes the hosts canonical
name. Device SHOULD be used as a structural class'
MUST ( cn $ ipHostNumber )
MAY ( userPassword $ l $ description $ manager ) )
objectclass ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' SUP top STRUCTURAL
DESC 'Abstraction of a network. The distinguished value of
the cn attribute denotes the networks canonical name'
MUST ipNetworkNumber
MAY ( cn $ ipNetmaskNumber $ l $ description $ manager ) )
objectclass ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' SUP top STRUCTURAL
DESC 'Abstraction of a netgroup. May refer to other netgroups'
MUST cn
MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) )
objectclass ( 1.3.6.1.1.1.2.9 NAME 'nisMap' SUP top STRUCTURAL
DESC 'A generic abstraction of a NIS map'
MUST nisMapName
MAY description )
objectclass ( 1.3.6.1.1.1.2.10 NAME 'nisObject' SUP top STRUCTURAL
DESC 'An entry in a NIS map'
MUST ( cn $ nisMapEntry $ nisMapName )
MAY description )
objectclass ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' SUP top AUXILIARY
DESC 'A device with a MAC address; device SHOULD be
used as a structural class'
MAY macAddress )
objectclass ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' SUP top AUXILIARY
DESC 'A device with boot parameters; device SHOULD be
used as a structural class'
MAY ( bootFile $ bootParameter ) )
objectclass ( 1.3.6.1.1.1.2.14 NAME 'nisKeyObject' SUP top AUXILIARY
DESC 'An object with a public and secret key'
MUST ( cn $ nisPublicKey $ nisSecretKey )
MAY ( uidNumber $ description ) )
objectclass ( 1.3.6.1.1.1.2.15 NAME 'nisDomainObject' SUP top AUXILIARY
DESC 'Associates a NIS domain with a naming context'
MUST nisDomain )
objectclass ( 1.3.6.1.1.1.2.16 NAME 'automountMap' SUP top STRUCTURAL
MUST ( automountMapName )
MAY description )
objectclass ( 1.3.6.1.1.1.2.17 NAME 'automount' SUP top STRUCTURAL
DESC 'Automount information'
MUST ( automountKey $ automountInformation )
MAY description )
## namedObject is needed for groups without members
objectclass ( 1.3.6.1.4.1.5322.13.1.1 NAME 'namedObject' SUP top
STRUCTURAL MAY cn )

View File

@ -63,6 +63,8 @@ class accounts_backend
* @var int
*/
var $total;
var $ldapServerInfo;
/**
* required classe for user and groups
@ -74,10 +76,7 @@ class accounts_backend
'top','person','organizationalperson','inetorgperson','posixaccount','shadowaccount'
),
'group' => array(
'top','posixgroup',
// some newer ldap require namedObject here, as none of the above is a structural object there
// this gets now autodetected
//'namedObject'
'top','posixgroup','groupofnames'
)
);
/**
@ -95,7 +94,7 @@ class accounts_backend
function accounts_backend()
{
// enable the caching in the session, done by the accounts class extending this class.
$this->use_session_cache = true;
$this->use_session_cache = true;
$this->ds = $GLOBALS['egw']->common->ldapConnect();
if(!@is_object($GLOBALS['egw']->translation))
@ -139,7 +138,12 @@ class accounts_backend
$is_group = $data['account_id'] < 0 || $data['account_type'] === 'g';
$data_utf8 = $this->translation->convert($data,$this->translation->charset(),'utf-8');
$members = $data['account_members'];
if (!is_object($this->ldapServerInfo))
{
$this->ldapServerInfo = $GLOBALS['egw']->ldap->getLDAPServerInfo($GLOBALS['egw_info']['server']['ldap_host']);
}
// 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'])
@ -160,16 +164,26 @@ class accounts_backend
{
$old['objectclass'][$n] = strtolower($class);
}
if ($is_group && ($old['cn'] != $data_utf8['account_lid'] || substr($old['dn'],0,3) != 'cn=') ||
$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=') ||
!$is_group && ($old['uid'] != $data_utf8['account_lid'] || substr($old['dn'],0,4) != 'uid='))
{
// query the memberships to set them again later
if (!$is_group) $memberships = $this->memberships($data['account_id']);
if (!$is_group)
{
$memberships = $this->memberships($data['account_id']);
}
else
{
$members = $old ? $old['memberuid'] : $this->members($data['account_id']);
}
// if dn has changed --> delete the old entry, as we cant rename the dn
// $this->delete would call accounts::delete, which will delete als ACL of the user too!
accounts_backend::delete($data['account_id']);
unset($old['dn']);
// removing the namedObject object-class, if it's included
if ($key !== false) unset($old['objectclass'][$key]);
$to_write = $old;
unset($old);
}
@ -205,6 +219,13 @@ class accounts_backend
{
$to_write = $this->_merge_group($to_write,$data_utf8);
$data['account_type'] = 'g';
$groupOfNames = in_array('groupofnames',$old ? $old['objectclass'] : $to_write['objectclass']);
if (!$old && $groupOfNames || $members)
{
$to_write = array_merge($to_write,accounts_backend::set_members($members,
$data['account_id'],$groupOfNames,$dn));
}
}
else
{
@ -217,9 +238,11 @@ class accounts_backend
!$old && !@ldap_add($this->ds,$dn,$to_write))
{
$err = true;
if (!$old && $is_group)
if (!$old && $is_group && ($key = array_search('groupofnames',$to_write['objectclass'])) !== false)
{
$to_write['objectclass'][] = 'namedobject';
// try again with removed groupOfNames stuff, as I cant detect if posixGroup is a structural object
unset($to_write['objectclass'][$key]);
unset($to_write['member']);
$err = !ldap_add($this->ds,$dn,$to_write);
}
if ($err)
@ -229,7 +252,7 @@ class accounts_backend
return false;
}
}
if ($memberships) // setting the previous memberships of the renamed account
if ($memberships)
{
$this->set_memberships($memberships,$data['account_id']);
}
@ -305,7 +328,7 @@ class accounts_backend
function _read_group($account_id)
{
$sri = ldap_search($this->ds, $this->group_context, 'gidnumber=' . abs($account_id),
array('dn','gidnumber','cn'));
array('dn','gidnumber','cn','objectclass'));
$data = ldap_get_entries($this->ds, $sri);
if (!$data['count'])
@ -314,14 +337,20 @@ class accounts_backend
}
$data = $this->translation->convert($data[0],'utf-8');
return array(
$group = array(
'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'),
'groupOfNames' => in_array('groupOfNames',$data['objectclass']),
);
if (!is_object($this->ldapServerInfo))
{
$this->ldapServerInfo = $GLOBALS['egw']->ldap->getLDAPServerInfo($GLOBALS['egw_info']['server']['ldap_host']);
}
return $group;
}
/**
@ -748,23 +777,42 @@ class accounts_backend
*
* @param array $members array with uidnumber or uid's
* @param int $gid gidnumber of group to set
* @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
* @return boolean/array false on failure, array or true otherwise
*/
function set_members($members,$gid)
function set_members($members,$gid,$groupOfNames=null,$use_cn=null)
{
//echo "<p>accounts_ldap::set_members(".print_r($members,true).",$gid)</p>\n";
if (!($cn = $this->id2name($gid))) return false;
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');
foreach($members as $key => $member)
$to_write = array();
foreach((array)$members as $key => $member)
{
if (is_numeric($member) && ($member = $this->id2name($member)))
if (is_numeric($member)) $member = $this->id2name($member);
if ($member)
{
$members[$key] = $member;
$to_write['memberuid'][] = $member;
if ($groupOfNames) $to_write['member'][] = 'uid='.$member.','.$this->user_context;
}
}
if (!ldap_modify($this->ds,'cn='.ldap::quote($cn).','.$this->group_context,array('memberUid' => array_values(array_unique($members)))))
if ($groupOfNames && !$to_write['member'])
{
echo "ldap_modify(,'cn=$cn,$this->group_context',array('memberUid' => ".print_r(array_values(array_unique($members)),true)."))\n";
// hack as groupOfNames requires the member attribute
$to_write['member'][] = 'uid=dummy'.','.$this->user_context;
}
if ($use_cn) return $to_write;
if (!ldap_modify($this->ds,'cn='.ldap::quote($cn).','.$this->group_context,$to_write))
{
echo "ldap_modify(,'cn=$cn,$this->group_context',".print_r($to_write,true)."))\n";
return false;
}
return true;
}
/**