mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-23 07:33:27 -06:00
Add support for User-Private Groups
This uses a new 389-ds plugin, Managed Entries, to automatically create a group entry when a user is created. The DNA plugin ensures that the group has a gidNumber that matches the users uidNumber. When the user is removed the group is automatically removed as well. If the managed entries plugin is not available or if a specific, separate range for gidNumber is passed in at install time then User-Private Groups will not be configured. The code checking for the Managed Entries plugin may be removed at some point. This is there because this plugin is only available in a 389-ds alpha release currently (1.2.6-a4).
This commit is contained in:
parent
83fd9ef7cc
commit
ba59d9d648
@ -31,6 +31,7 @@ app_DATA = \
|
||||
preferences.html.template \
|
||||
referint-conf.ldif \
|
||||
dna-posix.ldif \
|
||||
dna-upg.ldif \
|
||||
master-entry.ldif \
|
||||
memberof-task.ldif \
|
||||
memberof-conf.ldif \
|
||||
@ -39,6 +40,7 @@ app_DATA = \
|
||||
schema_compat.uldif \
|
||||
ldapi.ldif \
|
||||
wsgi.py \
|
||||
user_private_groups.ldif \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
16
install/share/dna-upg.ldif
Normal file
16
install/share/dna-upg.ldif
Normal file
@ -0,0 +1,16 @@
|
||||
# add plugin configuration for user private groups
|
||||
|
||||
dn: cn=User Private Groups,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
|
||||
changetype: add
|
||||
objectclass: top
|
||||
objectclass: extensibleObject
|
||||
cn: Posix Accounts
|
||||
dnaType: uidNumber
|
||||
dnaType: gidNumber
|
||||
dnaNextValue: eval($UIDSTART+1)
|
||||
dnaInterval: 1
|
||||
dnaMaxValue: eval($UIDSTART+100000)
|
||||
dnaMagicRegen: 999
|
||||
dnaFilter: (|(objectclass=posixAccount)(objectClass=posixGroup))
|
||||
dnaScope: $SUFFIX
|
||||
|
19
install/share/user_private_groups.ldif
Normal file
19
install/share/user_private_groups.ldif
Normal file
@ -0,0 +1,19 @@
|
||||
dn: cn=UPG Template,$SUFFIX
|
||||
changetype: add
|
||||
objectclass: mepTemplateEntry
|
||||
cn: UPG Template
|
||||
mepRDNAttr: cn
|
||||
mepStaticAttr: objectclass: posixGroup
|
||||
mepMappedAttr: cn: $$uid
|
||||
mepMappedAttr: gidNumber: $$uidNumber
|
||||
mepMappedAttr: description: User private group for $$uid
|
||||
|
||||
dn: cn=UPG Definition,cn=Managed Entries,cn=plugins,cn=config
|
||||
changetype: add
|
||||
objectclass: extensibleObject
|
||||
cn: UPG Definition
|
||||
originScope: cn=users,cn=accounts,$SUFFIX
|
||||
originFilter: objectclass=posixAccount
|
||||
managedBase: cn=groups,cn=accounts,$SUFFIX
|
||||
managedTemplate: cn=UPG Template,$SUFFIX
|
||||
|
@ -145,6 +145,8 @@ class group_add(LDAPCreate):
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
|
||||
if options['posix'] or 'gidnumber' in options:
|
||||
entry_attrs['objectclass'].append('posixgroup')
|
||||
if not 'gidnumber' in options:
|
||||
entry_attrs['gidnumber'] = 999
|
||||
return dn
|
||||
|
||||
|
||||
@ -200,6 +202,8 @@ class group_mod(LDAPUpdate):
|
||||
else:
|
||||
old_entry_attrs['objectclass'].append('posixgroup')
|
||||
entry_attrs['objectclass'] = old_entry_attrs['objectclass']
|
||||
if not 'gidnumber' in options:
|
||||
entry_attrs['gidnumber'] = 999
|
||||
return dn
|
||||
|
||||
api.register(group_mod)
|
||||
|
@ -122,6 +122,8 @@ class user(LDAPObject):
|
||||
cli_name='uid',
|
||||
label=_('UID'),
|
||||
doc=_('User ID Number (system will assign one if not provided)'),
|
||||
autofill=True,
|
||||
default=999,
|
||||
),
|
||||
Str('street?',
|
||||
cli_name='street',
|
||||
@ -169,16 +171,20 @@ class user_add(LDAPCreate):
|
||||
home_dir = home_dir.replace('//', '/').rstrip('/')
|
||||
entry_attrs['homedirectory'] = home_dir
|
||||
|
||||
# we're adding new users to a default group, get its gidNumber
|
||||
# get default group name from config
|
||||
def_primary_group = config.get('ipadefaultprimarygroup')
|
||||
group_dn = self.api.Object['group'].get_dn(def_primary_group)
|
||||
try:
|
||||
(group_dn, group_attrs) = ldap.get_entry(group_dn, ['gidnumber'])
|
||||
except errors.NotFound:
|
||||
error_msg = 'Default group for new users not found.'
|
||||
raise errors.NotFound(reason=error_msg)
|
||||
entry_attrs['gidnumber'] = group_attrs['gidnumber']
|
||||
if ldap.has_upg():
|
||||
# User Private Groups - uidNumber == gidNumber
|
||||
entry_attrs['gidnumber'] = entry_attrs['uidnumber']
|
||||
else:
|
||||
# we're adding new users to a default group, get its gidNumber
|
||||
# get default group name from config
|
||||
def_primary_group = config.get('ipadefaultprimarygroup')
|
||||
group_dn = self.api.Object['group'].get_dn(def_primary_group)
|
||||
try:
|
||||
(group_dn, group_attrs) = ldap.get_entry(group_dn, ['gidnumber'])
|
||||
except errors.NotFound:
|
||||
error_msg = 'Default group for new users not found.'
|
||||
raise errors.NotFound(reason=error_msg)
|
||||
entry_attrs['gidnumber'] = group_attrs['gidnumber']
|
||||
|
||||
return dn
|
||||
|
||||
|
@ -38,7 +38,8 @@ from ldap.dn import escape_dn_chars
|
||||
from ipaserver import ipaldap
|
||||
from ipaserver.install import ldapupdate
|
||||
from ipaserver.install import httpinstance
|
||||
from ipalib import util, uuid
|
||||
from ipalib import util, uuid, errors
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
|
||||
SERVER_ROOT_64 = "/usr/lib64/dirsrv"
|
||||
SERVER_ROOT_32 = "/usr/lib/dirsrv"
|
||||
@ -114,6 +115,25 @@ def is_ds_running():
|
||||
ret = False
|
||||
return ret
|
||||
|
||||
def has_managed_entries(host_name, dm_password):
|
||||
"""Check to see if the Managed Entries plugin is available"""
|
||||
ldapuri = 'ldap://%s' % host_name
|
||||
conn = None
|
||||
try:
|
||||
conn = ldap2(shared_instance=False, ldap_uri=ldapuri, base_dn='cn=config')
|
||||
conn.connect(bind_dn='cn=Directory Manager', bind_pw=dm_password)
|
||||
(dn, attrs) = conn.get_entry('cn=Managed Entries,cn=plugins',
|
||||
['*'])
|
||||
return True
|
||||
except errors.NotFound:
|
||||
return False
|
||||
except errors.ExecutionError, e:
|
||||
logging.critical("Could not connect to the Directory Server on %s" % host_name)
|
||||
raise e
|
||||
finally:
|
||||
if conn:
|
||||
conn.disconnect()
|
||||
|
||||
|
||||
INF_TEMPLATE = """
|
||||
[General]
|
||||
@ -179,6 +199,8 @@ class DsInstance(service.Service):
|
||||
self.step("enabling memberof plugin", self.__add_memberof_module)
|
||||
self.step("enabling referential integrity plugin", self.__add_referint_module)
|
||||
self.step("enabling winsync plugin", self.__add_winsync_module)
|
||||
if self.uidstart == self.gidstart:
|
||||
self.step("configuring user private groups", self.__user_private_groups)
|
||||
self.step("configuring replication version plugin", self.__config_version_module)
|
||||
self.step("enabling IPA enrollment plugin", self.__add_enrollment_module)
|
||||
self.step("enabling ldapi", self.__enable_ldapi)
|
||||
@ -331,7 +353,11 @@ class DsInstance(service.Service):
|
||||
self._ldap_mod("unique-attributes.ldif", self.sub_dict)
|
||||
|
||||
def __config_uidgid_gen_first_master(self):
|
||||
self._ldap_mod("dna-posix.ldif", self.sub_dict)
|
||||
if (self.uidstart == self.gidstart and
|
||||
has_managed_entries(self.host_name, self.dm_password)):
|
||||
self._ldap_mod("dna-upg.ldif", self.sub_dict)
|
||||
else:
|
||||
self._ldap_mod("dna-posix.ldif", self.sub_dict)
|
||||
|
||||
def __add_master_entry_first_master(self):
|
||||
self._ldap_mod("master-entry.ldif", self.sub_dict)
|
||||
@ -342,6 +368,10 @@ class DsInstance(service.Service):
|
||||
def __config_version_module(self):
|
||||
self._ldap_mod("ipa-version-conf.ldif")
|
||||
|
||||
def __user_private_groups(self):
|
||||
if has_managed_entries(self.host_name, self.dm_password):
|
||||
self._ldap_mod("user_private_groups.ldif", self.sub_dict)
|
||||
|
||||
def __add_enrollment_module(self):
|
||||
self._ldap_mod("enrollment-conf.ldif", self.sub_dict)
|
||||
|
||||
|
@ -103,9 +103,12 @@ def _handle_errors(e, **kw):
|
||||
raise errors.DatabaseError(desc=desc, info=info)
|
||||
|
||||
|
||||
def load_schema(url):
|
||||
def global_init(url):
|
||||
"""
|
||||
Retrieve the LDAP schema from the provided url.
|
||||
Perform global initialization when the module is loaded.
|
||||
|
||||
Retrieve the LDAP schema from the provided url and determine if
|
||||
User-Private Groups (upg) are configured.
|
||||
|
||||
Bind using kerberos credentials. If in the context of the
|
||||
in-tree "lite" server then use the current ccache. If in the context of
|
||||
@ -113,10 +116,11 @@ def load_schema(url):
|
||||
principal.
|
||||
"""
|
||||
tmpdir = None
|
||||
upg = False
|
||||
|
||||
if not api.env.in_server or api.env.context not in ['lite', 'server']:
|
||||
# The schema is only needed on the server side
|
||||
return
|
||||
return (None, None)
|
||||
|
||||
try:
|
||||
if api.env.context == 'server':
|
||||
@ -139,9 +143,17 @@ def load_schema(url):
|
||||
'cn=schema', _ldap.SCOPE_BASE,
|
||||
attrlist=['attributetypes', 'objectclasses']
|
||||
)[0]
|
||||
try:
|
||||
upg_entry = conn.search_s(
|
||||
'cn=UPG Template, %s' % api.env.basedn, _ldap.SCOPE_BASE,
|
||||
attrlist=['*']
|
||||
)[0]
|
||||
upg = True
|
||||
except _ldap.NO_SUCH_OBJECT, e:
|
||||
upg = False
|
||||
conn.unbind_s()
|
||||
except _ldap.SERVER_DOWN:
|
||||
return None
|
||||
return (None, upg)
|
||||
except _ldap.LDAPError, e:
|
||||
# TODO: raise a more appropriate exception
|
||||
_handle_errors(e, **{})
|
||||
@ -154,13 +166,14 @@ def load_schema(url):
|
||||
if tmpdir:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
return _ldap.schema.SubSchema(schema_entry[1])
|
||||
return (_ldap.schema.SubSchema(schema_entry[1]), upg)
|
||||
|
||||
# cache schema when importing module
|
||||
# cache schema and User-Private Groups when importing module
|
||||
try:
|
||||
_schema = load_schema(api.env.ldap_uri)
|
||||
(_schema, _upg) = global_init(api.env.ldap_uri)
|
||||
except AttributeError:
|
||||
_schema = None
|
||||
_upg = None
|
||||
|
||||
|
||||
def get_syntax(attr, value):
|
||||
@ -524,6 +537,16 @@ class ldap2(CrudBackend, Encoder):
|
||||
"""Returns a copy of the current LDAP schema."""
|
||||
return copy.deepcopy(self.schema)
|
||||
|
||||
def has_upg(self):
|
||||
"""Returns True/False whether User-Private Groups are enabled.
|
||||
This is determined based on whether the UPG Template exists.
|
||||
We determine this at module load so we don't have to test for
|
||||
it every time.
|
||||
"""
|
||||
global _upg
|
||||
|
||||
return _upg
|
||||
|
||||
@encode_args(1, 2)
|
||||
def get_effective_rights(self, dn, entry_attrs):
|
||||
"""Returns the rights the currently bound user has for the given DN.
|
||||
|
Loading…
Reference in New Issue
Block a user