summaryrefslogtreecommitdiffstats
path: root/ipalib/plugins
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2012-06-20 16:08:33 +0300
committerMartin Kosek <mkosek@redhat.com>2012-06-28 16:53:33 +0200
commita6ff85f425d5c38dd89fcd8999e0d62eadb969a1 (patch)
tree04ae9b01be916209b2156e915da7ebddff065fc0 /ipalib/plugins
parent52f69aaa8ab4d633bbeb96799bf96e8a715d0ae0 (diff)
downloadfreeipa-a6ff85f425d5c38dd89fcd8999e0d62eadb969a1.tar.gz
freeipa-a6ff85f425d5c38dd89fcd8999e0d62eadb969a1.tar.xz
freeipa-a6ff85f425d5c38dd89fcd8999e0d62eadb969a1.zip
Add support for external group members
When using ipaExternalGroup/ipaExternalMember attributes it is possible to add group members which don't exist in IPA database. This is primarily is required for AD trusts support and therefore validation is accepting only secure identifier (SID) format. https://fedorahosted.org/freeipa/ticket/2664
Diffstat (limited to 'ipalib/plugins')
-rw-r--r--ipalib/plugins/baseldap.py23
-rw-r--r--ipalib/plugins/group.py108
-rw-r--r--ipalib/plugins/trust.py4
3 files changed, 124 insertions, 11 deletions
diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py
index 475222a6..b841435f 100644
--- a/ipalib/plugins/baseldap.py
+++ b/ipalib/plugins/baseldap.py
@@ -362,23 +362,29 @@ def add_external_post_callback(memberattr, membertype, externalattr, ldap, compl
externalattr is one of externaluser,
"""
completed_external = 0
+ normalize = options.get('external_callback_normalize', True)
# Sift through the failures. We assume that these are all
# entries that aren't stored in IPA, aka external entries.
if memberattr in failed and membertype in failed[memberattr]:
(dn, entry_attrs_) = ldap.get_entry(dn, [externalattr])
members = entry_attrs.get(memberattr, [])
external_entries = entry_attrs_.get(externalattr, [])
+ lc_external_entries = set(e.lower() for e in external_entries)
failed_entries = []
for entry in failed[memberattr][membertype]:
membername = entry[0].lower()
member_dn = api.Object[membertype].get_dn(membername)
- if membername not in external_entries and \
- member_dn not in members:
+ if (membername not in lc_external_entries and
+ member_dn not in members):
# Not an IPA entry, assume external
- external_entries.append(membername)
+ if normalize:
+ external_entries.append(membername)
+ else:
+ external_entries.append(entry[0])
+ lc_external_entries.add(membername)
completed_external += 1
- elif membername in external_entries and \
- member_dn not in members:
+ elif (membername in lc_external_entries and
+ member_dn not in members):
# Already an external member, reset the error message
msg = unicode(errors.AlreadyGroupMember().message)
newerror = (entry[0], msg)
@@ -409,8 +415,11 @@ def remove_external_post_callback(memberattr, membertype, externalattr, ldap, co
completed_external = 0
for entry in failed[memberattr][membertype]:
membername = entry[0].lower()
- if membername in external_entries:
- external_entries.remove(membername)
+ if membername in external_entries or entry[0] in external_entries:
+ try:
+ external_entries.remove(membername)
+ except ValueError:
+ external_entries.remove(entry[0])
completed_external += 1
else:
failed_entries.append(membername)
diff --git a/ipalib/plugins/group.py b/ipalib/plugins/group.py
index 65657363..74bea170 100644
--- a/ipalib/plugins/group.py
+++ b/ipalib/plugins/group.py
@@ -22,6 +22,12 @@ from ipalib import api
from ipalib import Int, Str
from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
+if api.env.in_server and api.env.context in ['lite', 'server']:
+ try:
+ import ipaserver.dcerpc
+ _dcerpc_bindings_installed = True
+ except Exception, e:
+ _dcerpc_bindings_installed = False
__doc__ = _("""
Groups of users
@@ -83,11 +89,11 @@ class group(LDAPObject):
object_name_plural = _('groups')
object_class = ['ipausergroup']
object_class_config = 'ipagroupobjectclasses'
- possible_objectclasses = ['posixGroup', 'mepManagedEntry']
+ possible_objectclasses = ['posixGroup', 'mepManagedEntry', 'ipaExternalGroup']
search_attributes_config = 'ipagroupsearchfields'
default_attributes = [
'cn', 'description', 'gidnumber', 'member', 'memberof',
- 'memberindirect', 'memberofindirect',
+ 'memberindirect', 'memberofindirect', 'ipaexternalmember',
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
@@ -139,10 +145,22 @@ class group_add(LDAPCreate):
doc=_('Create as a non-POSIX group'),
default=False,
),
+ Flag('external',
+ cli_name='external',
+ doc=_('Allow adding external non-IPA members from trusted domains'),
+ default=False,
+ ),
)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
- if not options['nonposix']:
+ # As both 'external' and 'nonposix' options have default= set for
+ # them, they will always be present in options dict, thus we can
+ # safely reference the values
+ if options['external']:
+ entry_attrs['objectclass'].append('ipaexternalgroup')
+ if 'gidnumber' in options:
+ raise errors.RequirementError(name='gid')
+ elif not options['nonposix']:
entry_attrs['objectclass'].append('posixgroup')
if not 'gidnumber' in options:
entry_attrs['gidnumber'] = 999
@@ -194,11 +212,18 @@ class group_mod(LDAPUpdate):
cli_name='posix',
doc=_('change to a POSIX group'),
),
+ Flag('external',
+ cli_name='external',
+ doc=_('change to support external non-IPA members from trusted domains'),
+ default=False,
+ ),
)
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
- if options['posix'] or 'gidnumber' in options:
+ if ('posix' in options and options['posix']) or 'gidnumber' in options:
(dn, old_entry_attrs) = ldap.get_entry(dn, ['objectclass'])
+ if 'ipaexternalgroup' in old_entry_attrs['objectclass']:
+ raise errors.ExternalGroupViolation()
if 'posixgroup' in old_entry_attrs['objectclass']:
if options['posix']:
raise errors.AlreadyPosixGroup()
@@ -207,6 +232,15 @@ class group_mod(LDAPUpdate):
entry_attrs['objectclass'] = old_entry_attrs['objectclass']
if not 'gidnumber' in options:
entry_attrs['gidnumber'] = 999
+ if options['external']:
+ (dn, old_entry_attrs) = ldap.get_entry(dn, ['objectclass'])
+ if 'posixgroup' in old_entry_attrs['objectclass']:
+ raise errors.PosixGroupViolation()
+ if 'ipaexternalgroup' in old_entry_attrs['objectclass']:
+ raise errors.AlreadyExternalGroup()
+ else:
+ old_entry_attrs['objectclass'].append('ipaexternalgroup')
+ entry_attrs['objectclass'] = old_entry_attrs['objectclass']
# Can't check for this in a validator because we lack context
if 'gidnumber' in options and options['gidnumber'] is None:
raise errors.RequirementError(name='gid')
@@ -274,12 +308,64 @@ api.register(group_show)
class group_add_member(LDAPAddMember):
__doc__ = _('Add members to a group.')
+ takes_options = (
+ Str('ipaexternalmember*',
+ cli_name='external',
+ label=_('External member'),
+ doc=_('comma-separated SIDs of members of a trusted domain'),
+ csv=True,
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
+ )
+
+ def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ result = (completed, dn)
+ if 'ipaexternalmember' in options:
+ if not _dcerpc_bindings_installed:
+ raise errors.NotFound(name=_('AD Trust'),
+ reason=_('''Cannot perform external member validation without Samba 4 support installed.
+ Make sure you have installed server-trust-ad sub-package of IPA on the server'''))
+ domain_validator = ipaserver.dcerpc.DomainValidator(self.api)
+ if not domain_validator.is_configured():
+ raise errors.NotFound(name=_('AD Trust setup'),
+ reason=_('''Cannot perform join operation without own domain configured.
+ Make sure you have run ipa-adtrust-install on the IPA server first'''))
+ sids = []
+ failed_sids = []
+ for sid in options['ipaexternalmember']:
+ if domain_validator.is_trusted_sid_valid(sid):
+ sids.append(sid)
+ else:
+ failed_sids.append((sid, 'Not a trusted domain SID'))
+ if len(sids) == 0:
+ raise errors.ValidationError(name=_('external member'),
+ error=_('values are not recognized as valid SIDs from trusted domain'))
+ restore = []
+ if 'member' in failed and 'group' in failed['member']:
+ restore = failed['member']['group']
+ failed['member']['group'] = list((id,id) for id in sids)
+ result = add_external_post_callback('member', 'group', 'ipaexternalmember',
+ ldap, completed, failed, dn, entry_attrs,
+ keys, options, external_callback_normalize=False)
+ failed['member']['group'] = restore + failed_sids
+ return result
+
api.register(group_add_member)
class group_remove_member(LDAPRemoveMember):
__doc__ = _('Remove members from a group.')
+ takes_options = (
+ Str('ipaexternalmember*',
+ cli_name='external',
+ label=_('External member'),
+ doc=_('comma-separated SIDs of members of a trusted domain'),
+ csv=True,
+ flags=['no_create', 'no_update', 'no_search'],
+ ),
+ )
+
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
if keys[0] == protected_group_name:
result = api.Command.group_show(protected_group_name)
@@ -290,6 +376,20 @@ class group_remove_member(LDAPRemoveMember):
label=_(u'group'), container=protected_group_name)
return dn
+ def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+ result = (completed, dn)
+ if 'ipaexternalmember' in options:
+ sids = options['ipaexternalmember']
+ restore = list()
+ if 'member' in failed and 'group' in failed['member']:
+ restore = failed['member']['group']
+ failed['member']['group'] = list((id,id) for id in sids)
+ result = remove_external_post_callback('member', 'group', 'ipaexternalmember',
+ ldap, completed, failed, dn, entry_attrs,
+ keys, options)
+ failed['member']['group'] = restore
+ return result
+
api.register(group_remove_member)
diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
index 71ca23ab..40bd93e6 100644
--- a/ipalib/plugins/trust.py
+++ b/ipalib/plugins/trust.py
@@ -171,6 +171,10 @@ class trust_add(LDAPCreate):
realm_server = options['realm_server']
trustinstance = ipaserver.dcerpc.TrustDomainJoins(self.api)
+ if not trustinstance.configured:
+ raise errors.NotFound(name=_('AD Trust setup'),
+ reason=_('''Cannot perform join operation without own domain configured.
+ Make sure you have run ipa-adtrust-install on the IPA server first'''))
# 1. Full access to the remote domain. Use admin credentials and
# generate random trustdom password to do work on both sides