summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Kosek <mkosek@redhat.com>2013-01-25 10:10:17 +0100
committerMartin Kosek <mkosek@redhat.com>2013-02-11 15:38:22 +0100
commit67d8b434c5272fd47d2e168c2b97077c70c016c2 (patch)
treeeba3725e946445b2f5acc5eceea2fa666a5e730b
parentf7e27b547547be06f511a3ddfaff8db7d0b7898f (diff)
downloadfreeipa-67d8b434c5272fd47d2e168c2b97077c70c016c2.tar.gz
freeipa-67d8b434c5272fd47d2e168c2b97077c70c016c2.tar.xz
freeipa-67d8b434c5272fd47d2e168c2b97077c70c016c2.zip
Add trusconfig-show and trustconfig-mod commands
Global trust configuration is generated ipa-adtrust-install script is run. Add convenience commands to show auto-generated options like SID or GUID or options chosen by user (NetBIOS). Most of these options are not modifiable via trustconfig-mod command as it would break current trusts. Unit test file covering these new commands was added. https://fedorahosted.org/freeipa/ticket/3333
-rw-r--r--API.txt24
-rw-r--r--VERSION2
-rw-r--r--ipalib/plugins/trust.py190
-rw-r--r--tests/test_xmlrpc/test_trust_plugin.py159
-rw-r--r--tests/test_xmlrpc/xmlrpc_test.py10
5 files changed, 377 insertions, 8 deletions
diff --git a/API.txt b/API.txt
index 8fbfe6f5..6e5c8c58 100644
--- a/API.txt
+++ b/API.txt
@@ -3262,6 +3262,30 @@ option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None)
+command: trustconfig_mod
+args: 0,9,3
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('delattr*', cli_name='delattr', exclude='webui')
+option: Str('ipantfallbackprimarygroup', attribute=True, autofill=False, cli_name='fallback_primary_group', multivalue=False, required=False)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: StrEnum('trust_type', autofill=True, cli_name='type', default=u'ad', values=(u'ad',))
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('value', <type 'unicode'>, None)
+command: trustconfig_show
+args: 0,5,3
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Flag('rights', autofill=True, default=False)
+option: StrEnum('trust_type', autofill=True, cli_name='type', default=u'ad', values=(u'ad',))
+option: Str('version?', exclude='webui')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('value', <type 'unicode'>, None)
command: user_add
args: 1,34,3
arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, required=True)
diff --git a/VERSION b/VERSION
index 61f578db..37af5ef7 100644
--- a/VERSION
+++ b/VERSION
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=47
+IPA_API_VERSION_MINOR=48
diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
index 2019d910..a5211bfa 100644
--- a/ipalib/plugins/trust.py
+++ b/ipalib/plugins/trust.py
@@ -1,5 +1,6 @@
# Authors:
# Alexander Bokovoy <abokovoy@redhat.com>
+# Martin Kosek <mkosek@redhat.com>
#
# Copyright (C) 2011 Red Hat
# see file 'COPYING' for use and warranty information
@@ -95,6 +96,30 @@ Example:
4. List members of external members of ad_admins_external group to see their SIDs:
ipa group-show ad_admins_external
+
+
+GLOBAL TRUST CONFIGURATION
+
+When IPA AD trust subpackage is installed and ipa-adtrust-install is run,
+a local domain configuration (SID, GUID, NetBIOS name) is generated. These
+identifiers are then used when communicating with a trusted domain of the
+particular type.
+
+1. Show global trust configuration for Active Directory type of trusts:
+
+ ipa trustconfig-show --type ad
+
+2. Modify global configuration for all trusts of Active Directory type and set
+ a different fallback primary group (fallback primary group GID is used as
+ a primary user GID if user authenticating to IPA domain does not have any other
+ primary GID already set):
+
+ ipa trustconfig-mod --type ad --fallback-primary-group "alternative AD group"
+
+3. Change primary fallback group back to default hidden group (any group with
+ posixGroup object class is allowed):
+
+ ipa trustconfig-mod --type ad --fallback-primary-group "Default SMB Group"
""")
trust_output_params = (
@@ -120,6 +145,14 @@ _trust_status_dict = {True : _('Established and verified'),
False : _('Waiting for confirmation by remote side')}
_trust_type_dict_unknown = _('Unknown')
+_trust_type_option = StrEnum('trust_type',
+ cli_name='type',
+ label=_('Trust type (ad for Active Directory, default)'),
+ values=(u'ad',),
+ default=u'ad',
+ autofill=True,
+ )
+
def trust_type_string(level):
"""
Returns a string representing a type of the trust. The original field is an enum:
@@ -193,13 +226,7 @@ sides.
''')
takes_options = LDAPCreate.takes_options + (
- StrEnum('trust_type',
- cli_name='type',
- label=_('Trust type (ad for Active Directory, default)'),
- values=(u'ad',),
- default=u'ad',
- autofill=True,
- ),
+ _trust_type_option,
Str('realm_admin?',
cli_name='admin',
label=_("Active Directory domain administrator"),
@@ -482,3 +509,152 @@ api.register(trust_mod)
api.register(trust_del)
api.register(trust_find)
api.register(trust_show)
+
+_trustconfig_dn = {
+ u'ad': DN(('cn', api.env.domain), api.env.container_cifsdomains, api.env.basedn),
+}
+
+
+class trustconfig(LDAPObject):
+ """
+ Trusts global configuration object
+ """
+ object_name = _('trust configuration')
+ default_attributes = [
+ 'cn', 'ipantsecurityidentifier', 'ipantflatname', 'ipantdomainguid',
+ 'ipantfallbackprimarygroup',
+ ]
+
+ label = _('Global Trust Configuration')
+ label_singular = _('Global Trust Configuration')
+
+ takes_params = (
+ Str('cn',
+ label=_('Domain'),
+ flags=['no_update'],
+ ),
+ Str('ipantsecurityidentifier',
+ label=_('Security Identifier'),
+ flags=['no_update'],
+ ),
+ Str('ipantflatname',
+ label=_('NetBIOS name'),
+ flags=['no_update'],
+ ),
+ Str('ipantdomainguid',
+ label=_('Domain GUID'),
+ flags=['no_update'],
+ ),
+ Str('ipantfallbackprimarygroup',
+ cli_name='fallback_primary_group',
+ label=_('Fallback primary group'),
+ ),
+ )
+
+ def get_dn(self, *keys, **kwargs):
+ trust_type = kwargs.get('trust_type')
+ if trust_type is None:
+ raise errors.RequirementError(name='trust_type')
+ try:
+ return _trustconfig_dn[kwargs['trust_type']]
+ except KeyError:
+ raise errors.ValidationError(name='trust_type',
+ error=_("unsupported trust type"))
+
+ def _normalize_groupdn(self, entry_attrs):
+ """
+ Checks that group with given name/DN exists and updates the entry_attrs
+ """
+ if 'ipantfallbackprimarygroup' not in entry_attrs:
+ return
+
+ group = entry_attrs['ipantfallbackprimarygroup']
+ if isinstance(group, (list, tuple)):
+ group = group[0]
+
+ if group is None:
+ return
+
+ try:
+ dn = DN(group)
+ # group is in a form of a DN
+ try:
+ self.backend.get_entry(dn)
+ except errors.NotFound:
+ self.api.Object['group'].handle_not_found(group)
+ # DN is valid, we can just return
+ return
+ except ValueError:
+ # The search is performed for groups with "posixgroup" objectclass
+ # and not "ipausergroup" so that it can also match groups like
+ # "Default SMB Group" which does not have this objectclass.
+ try:
+ (dn, group_entry) = self.backend.find_entry_by_attr(
+ self.api.Object['group'].primary_key.name,
+ group,
+ ['posixgroup'],
+ [''],
+ self.api.Object['group'].container_dn)
+ except errors.NotFound:
+ self.api.Object['group'].handle_not_found(group)
+ else:
+ entry_attrs['ipantfallbackprimarygroup'] = [dn]
+
+ def _convert_groupdn(self, entry_attrs, options):
+ """
+ Convert an group dn into a name. As we use CN as user RDN, its value
+ can be extracted from the DN without further LDAP queries.
+ """
+ if options.get('raw', False):
+ return
+
+ try:
+ groupdn = entry_attrs['ipantfallbackprimarygroup'][0]
+ except (IndexError, KeyError):
+ groupdn = None
+
+ if groupdn is None:
+ return
+ assert isinstance(groupdn, DN)
+
+ entry_attrs['ipantfallbackprimarygroup'] = [groupdn[0][0].value]
+
+api.register(trustconfig)
+
+class trustconfig_mod(LDAPUpdate):
+ __doc__ = _('Modify global trust configuration.')
+
+ takes_options = LDAPUpdate.takes_options + (_trust_type_option,)
+ msg_summary = _('Modified "%(value)s" trust configuration')
+
+ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ self.obj._normalize_groupdn(entry_attrs)
+ return dn
+
+ def execute(self, *keys, **options):
+ result = super(trustconfig_mod, self).execute(*keys, **options)
+ result['value'] = options['trust_type']
+ return result
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ self.obj._convert_groupdn(entry_attrs, options)
+ return dn
+
+api.register(trustconfig_mod)
+
+
+class trustconfig_show(LDAPRetrieve):
+ __doc__ = _('Show global trust configuration.')
+
+ takes_options = LDAPRetrieve.takes_options + (_trust_type_option,)
+
+ def execute(self, *keys, **options):
+ result = super(trustconfig_show, self).execute(*keys, **options)
+ result['value'] = options['trust_type']
+ return result
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ self.obj._convert_groupdn(entry_attrs, options)
+ return dn
+
+api.register(trustconfig_show)
diff --git a/tests/test_xmlrpc/test_trust_plugin.py b/tests/test_xmlrpc/test_trust_plugin.py
new file mode 100644
index 00000000..7627be74
--- /dev/null
+++ b/tests/test_xmlrpc/test_trust_plugin.py
@@ -0,0 +1,159 @@
+# Authors:
+# Martin Kosek <mkosek@redhat.com>
+#
+# Copyright (C) 2010 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Test the `ipalib/plugins/trust.py` module.
+"""
+
+import nose
+from ipalib import api, errors
+from ipapython.dn import DN
+from tests.test_xmlrpc import objectclasses
+from xmlrpc_test import (Declarative, fuzzy_guid, fuzzy_domain_sid, fuzzy_string,
+ fuzzy_uuid, fuzzy_digits)
+
+
+trustconfig_ad_config = DN(('cn', api.env.domain),
+ api.env.container_cifsdomains, api.env.basedn)
+testgroup = u'adtestgroup'
+testgroup_dn = DN(('cn', testgroup), api.env.container_group, api.env.basedn)
+
+default_group = u'Default SMB Group'
+default_group_dn = DN(('cn', default_group), api.env.container_group, api.env.basedn)
+
+class test_trustconfig(Declarative):
+
+ @classmethod
+ def setUpClass(cls):
+ super(test_trustconfig, cls).setUpClass()
+ if not api.Backend.xmlclient.isconnected():
+ api.Backend.xmlclient.connect(fallback=False)
+ try:
+ api.Command['trustconfig_show'](trust_type=u'ad')
+ except errors.NotFound:
+ raise nose.SkipTest('Trusts are not configured')
+
+ cleanup_commands = [
+ ('group_del', [testgroup], {}),
+ ('trustconfig_mod', [], {'trust_type': u'ad',
+ 'ipantfallbackprimarygroup': default_group}),
+ ]
+
+ tests = [
+
+ dict(
+ desc='Retrieve trust configuration for AD domains',
+ command=('trustconfig_show', [], {'trust_type': u'ad'}),
+ expected={
+ 'value': u'ad',
+ 'summary': None,
+ 'result': {
+ 'dn': trustconfig_ad_config,
+ 'cn': [api.env.domain],
+ 'ipantdomainguid': [fuzzy_guid],
+ 'ipantfallbackprimarygroup': [default_group],
+ 'ipantflatname': [fuzzy_string],
+ 'ipantsecurityidentifier': [fuzzy_domain_sid]
+ },
+ },
+ ),
+
+ dict(
+ desc='Retrieve trust configuration for AD domains with --raw',
+ command=('trustconfig_show', [], {'trust_type': u'ad', 'raw': True}),
+ expected={
+ 'value': u'ad',
+ 'summary': None,
+ 'result': {
+ 'dn': trustconfig_ad_config,
+ 'cn': [api.env.domain],
+ 'ipantdomainguid': [fuzzy_guid],
+ 'ipantfallbackprimarygroup': [default_group_dn],
+ 'ipantflatname': [fuzzy_string],
+ 'ipantsecurityidentifier': [fuzzy_domain_sid]
+ },
+ },
+ ),
+
+ dict(
+ desc='Create auxiliary group %r' % testgroup,
+ command=(
+ 'group_add', [testgroup], dict(description=u'Test group')
+ ),
+ expected=dict(
+ value=testgroup,
+ summary=u'Added group "%s"' % testgroup,
+ result=dict(
+ cn=[testgroup],
+ description=[u'Test group'],
+ gidnumber=[fuzzy_digits],
+ objectclass=objectclasses.group + [u'posixgroup'],
+ ipauniqueid=[fuzzy_uuid],
+ dn=testgroup_dn,
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Try to change primary fallback group to nonexistent group',
+ command=('trustconfig_mod', [],
+ {'trust_type': u'ad', 'ipantfallbackprimarygroup': u'doesnotexist'}),
+ expected=errors.NotFound(reason=u'%s: group not found' % 'doesnotexist')
+ ),
+
+ dict(
+ desc='Try to change primary fallback group to nonexistent group DN',
+ command=('trustconfig_mod', [], {'trust_type': u'ad',
+ 'ipantfallbackprimarygroup': u'cn=doesnotexist,dc=test'}),
+ expected=errors.NotFound(reason=u'%s: group not found' % 'cn=doesnotexist,dc=test')
+ ),
+
+ dict(
+ desc='Change primary fallback group to "%s"' % testgroup,
+ command=('trustconfig_mod', [], {'trust_type': u'ad',
+ 'ipantfallbackprimarygroup': testgroup}),
+ expected={
+ 'value': u'ad',
+ 'summary': u'Modified "ad" trust configuration',
+ 'result': {
+ 'cn': [api.env.domain],
+ 'ipantdomainguid': [fuzzy_guid],
+ 'ipantfallbackprimarygroup': [testgroup],
+ 'ipantflatname': [fuzzy_string],
+ 'ipantsecurityidentifier': [fuzzy_domain_sid]
+ },
+ },
+ ),
+
+ dict(
+ desc='Change primary fallback group back to "%s" using DN' % default_group,
+ command=('trustconfig_mod', [], {'trust_type': u'ad',
+ 'ipantfallbackprimarygroup': unicode(default_group_dn)}),
+ expected={
+ 'value': u'ad',
+ 'summary': u'Modified "ad" trust configuration',
+ 'result': {
+ 'cn': [api.env.domain],
+ 'ipantdomainguid': [fuzzy_guid],
+ 'ipantfallbackprimarygroup': [default_group],
+ 'ipantflatname': [fuzzy_string],
+ 'ipantsecurityidentifier': [fuzzy_domain_sid]
+ },
+ },
+ ),
+ ]
diff --git a/tests/test_xmlrpc/xmlrpc_test.py b/tests/test_xmlrpc/xmlrpc_test.py
index 7c32be0d..610fa97c 100644
--- a/tests/test_xmlrpc/xmlrpc_test.py
+++ b/tests/test_xmlrpc/xmlrpc_test.py
@@ -40,6 +40,16 @@ fuzzy_uuid = Fuzzy(
'^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
)
+# Matches trusted domain GUID, like u'463bf2be-3456-4a57-979e-120304f2a0eb'
+fuzzy_guid = fuzzy_uuid
+
+# Matches SID of a trusted domain
+# SID syntax: http://msdn.microsoft.com/en-us/library/ff632068.aspx
+_sid_identifier_authority = '(0x[0-9a-f]{1,12}|[0-9]{1,10})'
+fuzzy_domain_sid = Fuzzy(
+ '^S-1-5-21-%(idauth)s-%(idauth)s-%(idauth)s$' % dict(idauth=_sid_identifier_authority)
+)
+
# Matches netgroup dn. Note (?i) at the beginning of the regexp is the ingnore case flag
fuzzy_netgroupdn = Fuzzy(
'(?i)ipauniqueid=[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12},cn=ng,cn=alt,%s' % api.env.basedn