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
This commit is contained in:
Martin Kosek
2013-02-11 15:38:22 +01:00
parent f7e27b5475
commit 67d8b434c5
5 changed files with 377 additions and 8 deletions
+24
View File
@@ -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)
+1 -1
View File
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
IPA_API_VERSION_MINOR=47
IPA_API_VERSION_MINOR=48
+183 -7
View File
@@ -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)
+159
View File
@@ -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]
},
},
),
]
+10
View File
@@ -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