URL: https://github.com/freeipa/freeipa/pull/1367 Author: abbra Title: #1367: [backport ipa-4-5] Trust avoid mitkrb trust type backport Action: opened
PR body: """ Manual backport of https://github.com/freeipa/freeipa/pull/1294 because the code did diverge between 4.5 and 4.6. """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/1367/head:pr1367 git checkout pr1367
From fbb41aa8f4c4bac279c96dbd8fcecb5edba5ea6e Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy <aboko...@redhat.com> Date: Thu, 9 Nov 2017 09:57:47 +0200 Subject: [PATCH 1/3] ipaserver/plugins/trust.py; fix some indenting issues --- ipaserver/plugins/trust.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py index d0bbfbc47c..f06aa25486 100644 --- a/ipaserver/plugins/trust.py +++ b/ipaserver/plugins/trust.py @@ -315,15 +315,17 @@ def generate_creds(trustinstance, style, **options): elif style == CRED_STYLE_KERBEROS: sp = admin_name.split('\\') if len(sp) > 1: - sp = [sp[1]] + sp = [sp[1]] else: - sp = admin_name.split(sep) + sp = admin_name.split(sep) if len(sp) == 1: - sp.append(trustinstance.remote_domain.info['dns_domain'].upper()) + sp.append(trustinstance.remote_domain + .info['dns_domain'].upper()) creds = u"{name}%{password}".format(name=sep.join(sp), password=password) return creds + def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): """ First, we try to derive the parameters of the ID range based on the @@ -354,7 +356,7 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): # CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System info_filter = '(objectClass=msSFU30DomainInfo)' info_dn = DN('CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System')\ - + basedn + + basedn # Get the domain validator domain_validator = ipaserver.dcerpc.DomainValidator(myapi) @@ -402,7 +404,7 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): base_id = int(info.get('msSFU30OrderNumber')[0]) range_size = (1 + (max_id - base_id) // DEFAULT_RANGE_SIZE)\ - * DEFAULT_RANGE_SIZE + * DEFAULT_RANGE_SIZE # Second, options given via the CLI options take precedence to discovery if options.get('range_type', None): @@ -630,11 +632,10 @@ def warning_if_ad_trust_dom_have_missing_SID(self, result, **options): pass else: for entry in entries: - add_message( + add_message( options['version'], result, - BrokenTrust(domain=entry.single_value['cn']) - ) + BrokenTrust(domain=entry.single_value['cn'])) @register() From 81abf2bff8762dfb2725f34df51cae9b84b68200 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy <aboko...@redhat.com> Date: Fri, 17 Nov 2017 17:19:25 +0200 Subject: [PATCH 2/3] trust: detect and error out when non-AD trust with IPA domain name exists Quite often users choose wrong type of trust on Active Directory side when setting up a trust to freeIPA. The trust type supported by freeIPA is just a normal forest trust to another Active Directory. However, some people follow old internet recipes that force using a trust to MIT Kerberos realm. This is a wrong type of trust. Unfortunately, when someone used MIT Kerberos realm trust, there is no way to programmatically remote the trust from freeIPA side. As result, we have to detect such situation and report an error. To do proper reporting, we need reuse some constants and trust type names we use in IPA CLI/Web UI. These common components were moved to a separate ipaserver/dcerpc_common.py module that is imported by both ipaserver/plugins/trust.py and ipaserver/dcerpc.py. Fixes https://pagure.io/freeipa/issue/7264 --- ipaserver/dcerpc.py | 37 +++++++++++++++-------- ipaserver/dcerpc_common.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++ ipaserver/plugins/trust.py | 65 ++++++++++------------------------------- 3 files changed, 113 insertions(+), 62 deletions(-) create mode 100644 ipaserver/dcerpc_common.py diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py index aa63cd9db0..ac1b2a3478 100644 --- a/ipaserver/dcerpc.py +++ b/ipaserver/dcerpc.py @@ -31,6 +31,10 @@ from ipapython.ipa_log_manager import root_logger from ipapython.dn import DN from ipaserver.install import installutils +from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL, + TRUST_JOIN_EXTERNAL, + trust_type_string) + from ipalib.util import normalize_name import os @@ -77,15 +81,6 @@ and Samba4 python bindings. """) -# Both constants can be used as masks against trust direction -# because bi-directional has two lower bits set. -TRUST_ONEWAY = 1 -TRUST_BIDIRECTIONAL = 3 - -# Trust join behavior -# External trust -- allow creating trust to a non-root domain in the forest -TRUST_JOIN_EXTERNAL = 1 - def is_sid_valid(sid): try: @@ -151,6 +146,7 @@ def is_sid_valid(sid): pysss_nss_idmap.ID_BOTH: 'both', } + class TrustTopologyConflictSolved(Exception): """ Internal trust error: raised when previously detected @@ -1254,9 +1250,26 @@ def establish_trust(self, another_domain, trustdom_secret, dname = lsa.String() dname.string = another_domain.info['dns_domain'] res = self._pipe.QueryTrustedDomainInfoByName( - self._policy_handle, - dname, - lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO) + self._policy_handle, + dname, + lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO + ) + if res.info_ex.trust_type != lsa.LSA_TRUST_TYPE_UPLEVEL: + msg = _('There is already a trust to {ipa_domain} with ' + 'unsupported type {trust_type}. Please remove ' + 'it manually on AD DC side.') + ttype = trust_type_string( + res.info_ex.trust_type, res.info_ex.trust_attributes + ) + err = unicode(msg).format( + ipa_domain=another_domain.info['dns_domain'], + trust_type=ttype) + + raise errors.ValidationError( + name=_('AD domain controller'), + error=err + ) + self._pipe.DeleteTrustedDomain(self._policy_handle, res.info_ex.sid) except RuntimeError as e: diff --git a/ipaserver/dcerpc_common.py b/ipaserver/dcerpc_common.py new file mode 100644 index 0000000000..526b025e32 --- /dev/null +++ b/ipaserver/dcerpc_common.py @@ -0,0 +1,73 @@ +import six +from ipalib import _ +if six.PY3: + unicode = six.text_type + +# Both constants can be used as masks against trust direction +# because bi-directional has two lower bits set. +TRUST_ONEWAY = 1 +TRUST_BIDIRECTIONAL = 3 + +# Trust join behavior +# External trust -- allow creating trust to a non-root domain in the forest +TRUST_JOIN_EXTERNAL = 1 + +# We don't want to import any of Samba Python code here just for constants +# Since these constants set in MS-ADTS, we can rely on their stability +LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001 + +_trust_direction_dict = { + 1: _('Trusting forest'), + 2: _('Trusted forest'), + 3: _('Two-way trust') +} + +_trust_status_dict = { + True: _('Established and verified'), + False: _('Waiting for confirmation by remote side') +} + +_trust_type_dict_unknown = _('Unknown') + +# Trust type is a combination of ipanttrusttype and ipanttrustattributes +# We shift trust attributes by 3 bits to left so bit 0 becomes bit 3 and +# 2+(1 << 3) becomes 10. +_trust_type_dict = { + 1: _('Non-Active Directory domain'), + 2: _('Active Directory domain'), + 3: _('RFC4120-compliant Kerberos realm'), + 10: _('Non-transitive external trust to a domain in ' + 'another Active Directory forest'), + 11: _('Non-transitive external trust to an RFC4120-' + 'compliant Kerberos realm') +} + + +def trust_type_string(level, attrs): + """ + Returns a string representing a type of the trust. + The original field is an enum: + LSA_TRUST_TYPE_DOWNLEVEL = 0x00000001, + LSA_TRUST_TYPE_UPLEVEL = 0x00000002, + LSA_TRUST_TYPE_MIT = 0x00000003 + """ + transitive = int(attrs) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE + string = _trust_type_dict.get(int(level) | (transitive << 3), + _trust_type_dict_unknown) + return unicode(string) + + +def trust_direction_string(level): + """ + Returns a string representing a direction of the trust. + The original field is a bitmask taking two bits in use + LSA_TRUST_DIRECTION_INBOUND = 0x00000001, + LSA_TRUST_DIRECTION_OUTBOUND = 0x00000002 + """ + string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown) + return unicode(string) + + +def trust_status_string(level): + string = _trust_status_dict.get(level, _trust_type_dict_unknown) + return unicode(string) diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py index f06aa25486..d01529ee02 100644 --- a/ipaserver/plugins/trust.py +++ b/ipaserver/plugins/trust.py @@ -44,6 +44,13 @@ from ipalib import output from ldap import SCOPE_SUBTREE from time import sleep +from ipaserver.dcerpc_common import (TRUST_ONEWAY, + TRUST_BIDIRECTIONAL, + TRUST_JOIN_EXTERNAL, + LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE, + trust_type_string, + trust_direction_string, + trust_status_string) if six.PY3: unicode = str @@ -63,9 +70,6 @@ if api.env.in_server and api.env.context in ['lite', 'server']: try: import ipaserver.dcerpc - from ipaserver.dcerpc import (TRUST_ONEWAY, - TRUST_BIDIRECTIONAL, - TRUST_JOIN_EXTERNAL) import dbus import dbus.mainloop.glib _bindings_installed = True @@ -157,28 +161,14 @@ register = Registry() -# Trust type is a combination of ipanttrusttype and ipanttrustattributes -# We shift trust attributes by 3 bits to left so bit 0 becomes bit 3 and -# 2+(1 << 3) becomes 10. -_trust_type_dict = {1 : _('Non-Active Directory domain'), - 2 : _('Active Directory domain'), - 3 : _('RFC4120-compliant Kerberos realm'), - 10: _('Non-transitive external trust to a domain in another Active Directory forest')} - -_trust_direction_dict = {1 : _('Trusting forest'), - 2 : _('Trusted forest'), - 3 : _('Two-way trust')} -_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, - ) +_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, + ) DEFAULT_RANGE_SIZE = 200000 @@ -187,31 +177,6 @@ CRED_STYLE_SAMBA = 1 CRED_STYLE_KERBEROS = 2 -LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001 - -def trust_type_string(level, attrs): - """ - Returns a string representing a type of the trust. The original field is an enum: - LSA_TRUST_TYPE_DOWNLEVEL = 0x00000001, - LSA_TRUST_TYPE_UPLEVEL = 0x00000002, - LSA_TRUST_TYPE_MIT = 0x00000003 - """ - transitive = int(attrs) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE - string = _trust_type_dict.get(int(level) | (transitive << 3), _trust_type_dict_unknown) - return unicode(string) - -def trust_direction_string(level): - """ - Returns a string representing a direction of the trust. The original field is a bitmask taking two bits in use - LSA_TRUST_DIRECTION_INBOUND = 0x00000001, - LSA_TRUST_DIRECTION_OUTBOUND = 0x00000002 - """ - string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown) - return unicode(string) - -def trust_status_string(level): - string = _trust_status_dict.get(level, _trust_type_dict_unknown) - return unicode(string) def make_trust_dn(env, trust_type, dn): assert isinstance(dn, DN) From 06321c5140ed43cc7b5c850a3be18f823b4d7b23 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy <aboko...@redhat.com> Date: Fri, 17 Nov 2017 17:25:57 +0200 Subject: [PATCH 3/3] ipaserver/plugins/trust.py: pep8 compliance --- ipaserver/plugins/trust.py | 356 +++++++++++++++++++++++++++------------------ 1 file changed, 214 insertions(+), 142 deletions(-) diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py index d01529ee02..73e137abce 100644 --- a/ipaserver/plugins/trust.py +++ b/ipaserver/plugins/trust.py @@ -81,10 +81,10 @@ Manage trust relationship between IPA and Active Directory domains. -In order to allow users from a remote domain to access resources in IPA -domain, trust relationship needs to be established. Currently IPA supports -only trusts between IPA and Active Directory domains under control of Windows -Server 2008 or later, with functional level 2008 or later. +In order to allow users from a remote domain to access resources in IPA domain, +trust relationship needs to be established. Currently IPA supports only trusts +between IPA and Active Directory domains under control of Windows Server 2008 +or later, with functional level 2008 or later. Please note that DNS on both IPA and Active Directory domain sides should be configured properly to discover each other. Trust relationship relies on @@ -95,7 +95,8 @@ 1. Establish cross-realm trust with Active Directory using AD administrator credentials: - ipa trust-add --type=ad <ad.domain> --admin <AD domain administrator> --password + ipa trust-add --type=ad <ad.domain> --admin <AD domain administrator> \ + --password 2. List all existing trust relationships: @@ -110,35 +111,39 @@ ipa trust-del <ad.domain> Once trust relationship is established, remote users will need to be mapped -to local POSIX groups in order to actually use IPA resources. The mapping should -be done via use of external membership of non-POSIX group and then this group -should be included into one of local POSIX groups. +to local POSIX groups in order to actually use IPA resources. The mapping +should be done via use of external membership of non-POSIX group and then +this group should be included into one of local POSIX groups. Example: -1. Create group for the trusted domain admins' mapping and their local POSIX group: +1. Create group for the trusted domain admins' mapping and their local POSIX +group: - ipa group-add --desc='<ad.domain> admins external map' ad_admins_external --external + ipa group-add --desc='<ad.domain> admins external map' \ + ad_admins_external --external ipa group-add --desc='<ad.domain> admins' ad_admins -2. Add security identifier of Domain Admins of the <ad.domain> to the ad_admins_external - group: +2. Add security identifier of Domain Admins of the <ad.domain> to the + ad_admins_external group: ipa group-add-member ad_admins_external --external 'AD\\Domain Admins' -3. Allow members of ad_admins_external group to be associated with ad_admins POSIX group: +3. Allow members of ad_admins_external group to be associated with + ad_admins POSIX group: ipa group-add-member ad_admins --groups ad_admins_external -4. List members of external members of ad_admins_external group to see their SIDs: +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 +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. @@ -147,11 +152,11 @@ 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): + 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" + ipa trustconfig-mod --type ad --fallback-primary-group "another AD group" 3. Change primary fallback group back to default hidden group (any group with posixGroup object class is allowed): @@ -185,6 +190,7 @@ def make_trust_dn(env, trust_type, dn): return DN(dn, container_dn) return dn + def find_adtrust_masters(ldap, api): """ Returns a list of names of IPA servers with ADTRUST component configured. @@ -200,6 +206,7 @@ def find_adtrust_masters(ldap, api): return [entry.dn[1].value for entry in entries] + def verify_samba_component_presence(ldap, api): """ Verifies that Samba is installed and configured on this particular master. @@ -233,7 +240,7 @@ def raise_missing_component_error(message): # First check for packages missing elif not _bindings_installed: - error_message=_( + error_message = _( 'Cannot perform the selected command without Samba 4 support ' 'installed. Make sure you have installed server-trust-ad ' 'sub-package of IPA.' @@ -243,7 +250,7 @@ def raise_missing_component_error(message): # Packages present, but ADTRUST instance is not configured elif not adtrust_present: - error_message=_( + error_message = _( 'Cannot perform the selected command without Samba 4 instance ' 'configured on this machine. Make sure you have run ' 'ipa-adtrust-install on this server.' @@ -263,7 +270,8 @@ def generate_creds(trustinstance, style, **options): **options -- options with realm_admin and realm_passwd keys Result: - a string representing credentials with first % separating username and password + a string representing credentials with first % separating + username and password None is returned if realm_passwd key returns nothing from options """ creds = None @@ -284,8 +292,9 @@ def generate_creds(trustinstance, style, **options): else: sp = admin_name.split(sep) if len(sp) == 1: - sp.append(trustinstance.remote_domain - .info['dns_domain'].upper()) + sp.append( + trustinstance.remote_domain.info['dns_domain'].upper() + ) creds = u"{name}%{password}".format(name=sep.join(sp), password=password) return creds @@ -334,7 +343,8 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options): creds = None if trustinstance: # Re-use AD administrator credentials if they were provided - creds = generate_creds(trustinstance, style=CRED_STYLE_KERBEROS, **options) + creds = generate_creds(trustinstance, + style=CRED_STYLE_KERBEROS, **options) if creds: domain_validator._admin_creds = creds # KDC might not get refreshed data at the first time, @@ -417,21 +427,32 @@ def fetch_trusted_domains_over_dbus(myapi, log, forest_name): _stdout = '' _stderr = '' bus = dbus.SystemBus() - intf = bus.get_object(DBUS_IFACE_TRUST,"/", follow_name_owner_changes=True) - fetch_domains_method = intf.get_dbus_method('fetch_domains', dbus_interface=DBUS_IFACE_TRUST) + intf = bus.get_object(DBUS_IFACE_TRUST, "/", + follow_name_owner_changes=True) + fetch_domains_method = intf.get_dbus_method( + 'fetch_domains', + dbus_interface=DBUS_IFACE_TRUST) (_ret, _stdout, _stderr) = fetch_domains_method(forest_name) except dbus.DBusException as e: - log.error('Failed to call %(iface)s.fetch_domains helper.' - 'DBus exception is %(exc)s.' % dict(iface=DBUS_IFACE_TRUST, exc=str(e))) + log.error( + 'Failed to call %(iface)s.fetch_domains helper. ' + 'DBus exception is %(exc)s.' % dict(iface=DBUS_IFACE_TRUST, exc=str(e)) + ) if _ret != 0: - log.error('Helper was called for forest %(forest)s, return code is %(ret)d' % dict(forest=forest_name, ret=_ret)) - log.error('Standard output from the helper:\n%s---\n' % (_stdout)) - log.error('Error output from the helper:\n%s--\n' % (_stderr)) - raise errors.ServerCommandError(server=myapi.env.host, - error=_('Fetching domains from trusted forest failed. ' - 'See details in the error_log')) + log.error( + 'Helper was called for forest %s, return code is %d', + forest_name, _ret + ) + log.error('Standard output from the helper:\n%s---\n', _stdout) + log.error('Error output from the helper:\n%s--\n', _stderr) + raise errors.ServerCommandError( + server=myapi.env.host, + error=_('Fetching domains from trusted forest failed. ' + 'See details in the error_log') + ) return + @register() class trust(LDAPObject): """ @@ -538,8 +559,8 @@ def validate_sid_blacklists(self, entry_attrs): continue for value in values: if not ipaserver.dcerpc.is_sid_valid(value): - raise errors.ValidationError(name=attr, - error=_("invalid SID: %(value)s") % dict(value=value)) + err = unicode(_("invalid SID: {SID}")).format(SID=value) + raise errors.ValidationError(name=attr, error=err) def get_dn(self, *keys, **kwargs): trust_type = kwargs.get('trust_type') @@ -600,7 +621,8 @@ def warning_if_ad_trust_dom_have_missing_SID(self, result, **options): add_message( options['version'], result, - BrokenTrust(domain=entry.single_value['cn'])) + BrokenTrust(domain=entry.single_value['cn']) + ) @register() @@ -622,7 +644,7 @@ class trust_add(LDAPCreate): range_types = { u'ipa-ad-trust': unicode(_('Active Directory domain range')), u'ipa-ad-trust-posix': unicode(_('Active Directory trust range with ' - 'POSIX attributes')), + 'POSIX attributes')), } takes_options = LDAPCreate.takes_options + ( @@ -720,9 +742,10 @@ def execute(self, *keys, **options): trust_filter = "cn=%s" % result['value'] trusts, _truncated = ldap.find_entries( - base_dn=DN(self.api.env.container_trusts, self.api.env.basedn), - filter=trust_filter, - attrs_list=attrs_list) + base_dn=DN(self.api.env.container_trusts, self.api.env.basedn), + filter=trust_filter, + attrs_list=attrs_list + ) result['result'] = entry_to_dict(trusts[0], **options) @@ -731,10 +754,11 @@ def execute(self, *keys, **options): # Note that add_new_domains_from_trust will add needed ranges for # the algorithmic ID mapping case. if (options.get('trust_type') == u'ad' and - options.get('trust_secret') is None): + options.get('trust_secret') is None): + if options.get('bidirectional') == True: - # Bidirectional trust allows us to use cross-realm TGT, so we can - # run the call under original user's credentials + # Bidirectional trust allows us to use cross-realm TGT, + # so we can run the call under original user's credentials res = fetch_domains_from_trust(self.api, self.trustinstance, **options) add_new_domains_from_trust( @@ -790,7 +814,9 @@ def validate_options(self, *keys, **options): # If domain name and realm does not match, IPA server is not be able # to establish trust with Active Directory. - realm_not_matching_domain = (self.api.env.domain.upper() != self.api.env.realm) + realm_not_matching_domain = ( + self.api.env.domain.upper() != self.api.env.realm + ) if options['trust_type'] == u'ad' and realm_not_matching_domain: raise errors.ValidationError( @@ -917,11 +943,12 @@ def validate_range(self, *keys, **options): ) if range_type and range_type != old_range_type: - raise errors.ValidationError(name=_('range type change'), - error=_('ID range for the trusted domain already exists, ' - 'but it has a different type. Please remove the ' - 'old range manually, or do not enforce type ' - 'via --range-type option.')) + raise errors.ValidationError( + name=_('range type change'), + error=_('ID range for the trusted domain already ' + 'exists, but it has a different type. Please ' + 'remove the old range manually, or do not ' + 'enforce type via --range-type option.')) return old_range, range_name, dom_sid @@ -956,33 +983,55 @@ def execute_ad(self, full_join, *keys, **options): trust_type ) except errors.NotFound: - error_message=_("Unable to resolve domain controller for '%s' domain. ") % (keys[-1]) - instructions=[] + _message = _("Unable to resolve domain controller for " + "{domain} domain. ") + error_message = unicode(_message).format(domain=keys[-1]) + instructions = [] + if dns_container_exists(self.obj.backend): try: - dns_zone = self.api.Command.dnszone_show(keys[-1])['result'] - if ('idnsforwardpolicy' in dns_zone) and dns_zone['idnsforwardpolicy'][0] == u'only': - instructions.append(_("Forward policy is defined for it in IPA DNS, " - "perhaps forwarder points to incorrect host?")) + dns_zone = self.api.Command.dnszone_show( + keys[-1])['result'] + + if (('idnsforwardpolicy' in dns_zone) and + dns_zone['idnsforwardpolicy'][0] == u'only'): + + instructions.append( + _("Forward policy is defined for it in " + "IPA DNS, perhaps forwarder points to " + "incorrect host?") + ) except (errors.NotFound, KeyError): - instructions.append(_("IPA manages DNS, please verify " - "your DNS configuration and " - "make sure that service records " - "of the '%(domain)s' domain can " - "be resolved. Examples how to " - "configure DNS with CLI commands " - "or the Web UI can be found in " - "the documentation. " ) % - dict(domain=keys[-1])) + _instruction = _( + "IPA manages DNS, please verify your DNS " + "configuration and make sure that service " + "records of the '{domain}' domain can be " + "resolved. Examples how to configure DNS " + "with CLI commands or the Web UI can be " + "found in the documentation. " + ) + instructions.append( + unicode(_instruction).format(domain=keys[-1]) + ) else: - instructions.append(_("Since IPA does not manage DNS records, ensure DNS " - "is configured to resolve '%(domain)s' domain from " - "IPA hosts and back.") % dict(domain=keys[-1])) - raise errors.NotFound(reason=error_message, instructions=instructions) + _instruction = _( + "Since IPA does not manage DNS records, ensure " + "DNS is configured to resolve '{domain}' " + "domain from IPA hosts and back." + ) + instructions.append( + unicode(_instruction).format(domain=keys[-1]) + ) + raise errors.NotFound( + reason=error_message, + instructions=instructions + ) if result is None: - raise errors.ValidationError(name=_('AD Trust setup'), - error=_('Unable to verify write permissions to the AD')) + raise errors.ValidationError( + name=_('AD Trust setup'), + error=_('Unable to verify write permissions to the AD') + ) ret = dict( value=pkey_to_value( @@ -1019,12 +1068,14 @@ def execute_ad(self, full_join, *keys, **options): error=_('Not enough arguments specified to perform trust ' 'setup')) + @register() class trust_del(LDAPDelete): __doc__ = _('Delete a trust.') msg_summary = _('Deleted trust "%(value)s"') + @register() class trust_mod(LDAPUpdate): __doc__ = _(""" @@ -1037,13 +1088,14 @@ class trust_mod(LDAPUpdate): msg_summary = _('Modified trust "%(value)s" ' '(change will be effective in 60 seconds)') - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + def pre_callback(self, ldap, dn, e_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) - self.obj.validate_sid_blacklists(entry_attrs) + self.obj.validate_sid_blacklists(e_attrs) return dn + @register() class trust_find(LDAPSearch): __doc__ = _('Search for trusts.') @@ -1054,9 +1106,10 @@ class trust_find(LDAPSearch): '%(count)d trust matched', '%(count)d trusts matched', 0 ) - # Since all trusts types are stored within separate containers under 'cn=trusts', - # search needs to be done on a sub-tree scope - def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): + # Since all trusts types are stored within separate containers + # under 'cn=trusts', search needs to be done on a sub-tree scope + def pre_callback(self, ldap, filters, attrs_list, + base_dn, scope, *args, **options): # list only trust, not trust domains return (filters, base_dn, ldap.SCOPE_SUBTREE) @@ -1076,13 +1129,16 @@ def post_callback(self, ldap, entries, truncated, *args, **options): trust_type = attrs.single_value.get('ipanttrusttype', None) attributes = attrs.single_value.get('ipanttrustattributes', 0) if not options.get('raw', False) and trust_type is not None: - attrs['trusttype'] = [trust_type_string(trust_type, attributes)] + attrs['trusttype'] = [ + trust_type_string(trust_type, attributes) + ] del attrs['ipanttrusttype'] if attributes: del attrs['ipanttrustattributes'] return truncated + @register() class trust_show(LDAPRetrieve): __doc__ = _('Display information about a trust.') @@ -1098,7 +1154,7 @@ def execute(self, *keys, **options): return result - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + def post_callback(self, ldap, dn, e_attrs, *keys, **options): assert isinstance(dn, DN) # Translate ipanttrusttype to trusttype @@ -1106,25 +1162,28 @@ def post_callback(self, ldap, dn, entry_attrs, *keys, **options): # if --raw not used if not options.get('raw', False): - trust_type = entry_attrs.single_value.get('ipanttrusttype', None) - attributes = entry_attrs.single_value.get('ipanttrustattributes', 0) + trust_type = e_attrs.single_value.get('ipanttrusttype', None) + attributes = e_attrs.single_value.get('ipanttrustattributes', 0) if trust_type is not None: - entry_attrs['trusttype'] = [trust_type_string(trust_type, attributes)] - del entry_attrs['ipanttrusttype'] + e_attrs['trusttype'] = [ + trust_type_string(trust_type, attributes) + ] + del e_attrs['ipanttrusttype'] - dir_str = entry_attrs.single_value.get('ipanttrustdirection', None) + dir_str = e_attrs.single_value.get('ipanttrustdirection', None) if dir_str is not None: - entry_attrs['trustdirection'] = [trust_direction_string(dir_str)] - del entry_attrs['ipanttrustdirection'] + e_attrs['trustdirection'] = [trust_direction_string(dir_str)] + del e_attrs['ipanttrustdirection'] if attributes: - del entry_attrs['ipanttrustattributes'] + del e_attrs['ipanttrustattributes'] return dn _trustconfig_dn = { - u'ad': DN(('cn', api.env.domain), api.env.container_cifsdomains, api.env.basedn), + u'ad': DN(('cn', api.env.domain), + api.env.container_cifsdomains, api.env.basedn), } @@ -1184,8 +1243,10 @@ def get_dn(self, *keys, **kwargs): try: return _trustconfig_dn[kwargs['trust_type']] except KeyError: - raise errors.ValidationError(name='trust_type', - error=_("unsupported trust type")) + raise errors.ValidationError( + name='trust_type', + error=_("unsupported trust type") + ) def _normalize_groupdn(self, entry_attrs): """ @@ -1254,8 +1315,8 @@ class trustconfig_mod(LDAPUpdate): msg_summary = _('Modified "%(value)s" trust configuration') has_output = output.simple_entry - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - self.obj._normalize_groupdn(entry_attrs) + def pre_callback(self, ldap, dn, e_attrs, attrs_list, *keys, **options): + self.obj._normalize_groupdn(e_attrs) return dn def execute(self, *keys, **options): @@ -1263,14 +1324,13 @@ def execute(self, *keys, **options): result['value'] = pkey_to_value(options['trust_type'], options) return result - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): - self.obj._convert_groupdn(entry_attrs, options) + def post_callback(self, ldap, dn, e_attrs, *keys, **options): + self.obj._convert_groupdn(e_attrs, options) self.api.Object.config.show_servroles_attributes( - entry_attrs, "AD trust agent", "AD trust controller", **options) + e_attrs, "AD trust agent", "AD trust controller", **options) return dn - @register() class trustconfig_show(LDAPRetrieve): __doc__ = _('Show global trust configuration.') @@ -1293,18 +1353,21 @@ def post_callback(self, ldap, dn, entry_attrs, *keys, **options): if _nss_idmap_installed: _idmap_type_dict = { - pysss_nss_idmap.ID_USER : 'user', - pysss_nss_idmap.ID_GROUP : 'group', - pysss_nss_idmap.ID_BOTH : 'both', + pysss_nss_idmap.ID_USER: 'user', + pysss_nss_idmap.ID_GROUP: 'group', + pysss_nss_idmap.ID_BOTH: 'both', } + def idmap_type_string(level): string = _idmap_type_dict.get(int(level), 'unknown') return unicode(string) + @register() class trust_resolve(Command): NO_CLI = True - __doc__ = _('Resolve security identifiers of users and groups in trusted domains') + __doc__ = _('Resolve security identifiers of users and groups ' + 'in trusted domains') takes_options = ( Str('sids+', @@ -1313,8 +1376,8 @@ class trust_resolve(Command): ) has_output_params = ( - Str('name', label= _('Name')), - Str('sid', label= _('SID')), + Str('name', label=_('Name')), + Str('sid', label=_('SID')), ) has_output = ( @@ -1326,13 +1389,15 @@ def execute(self, *keys, **options): if not _nss_idmap_installed: return dict(result=result) try: + NAME_KEY = pysss_nss_idmap.NAME_KEY + TYPE_KEY = pysss_nss_idmap.TYPE_KEY sids = [str(x) for x in options['sids']] xlate = pysss_nss_idmap.getnamebysid(sids) for sid in xlate: entry = dict() entry['sid'] = [unicode(sid)] - entry['name'] = [unicode(xlate[sid][pysss_nss_idmap.NAME_KEY])] - entry['type'] = [idmap_type_string(xlate[sid][pysss_nss_idmap.TYPE_KEY])] + entry['name'] = [unicode(xlate[sid][NAME_KEY])] + entry['type'] = [idmap_type_string(xlate[sid][TYPE_KEY])] result.append(entry) except ValueError: pass @@ -1340,7 +1405,6 @@ def execute(self, *keys, **options): return dict(result=result) - @register() class adtrust_is_enabled(Command): NO_CLI = True @@ -1367,7 +1431,6 @@ def execute(self, *keys, **options): return dict(result=True) - @register() class compat_is_enabled(Command): NO_CLI = True @@ -1411,7 +1474,6 @@ def execute(self, *keys, **options): return dict(result=True) - @register() class sidgen_was_run(Command): """ @@ -1461,7 +1523,7 @@ class trustdomain(LDAPObject): Object representing a domain of the AD trust. """ parent_object = 'trust' - trust_type_idx = {'2':u'ad'} + trust_type_idx = {'2': u'ad'} object_name = _('trust domain') object_name_plural = _('trust domains') object_class = ['ipaNTTrustedDomain'] @@ -1478,40 +1540,39 @@ class trustdomain(LDAPObject): Str('cn', label=_('Domain name'), cli_name='domain', - primary_key=True - ), + primary_key=True), Str('ipantflatname?', cli_name='flat_name', - label=_('Domain NetBIOS name'), - ), + label=_('Domain NetBIOS name')), Str('ipanttrusteddomainsid?', cli_name='sid', - label=_('Domain Security Identifier'), - ), + label=_('Domain Security Identifier')), Flag('domain_enabled', - label=_('Domain enabled'), - flags={'virtual_attribute', 'no_create', 'no_update', 'no_search'}, - ), + label=_('Domain enabled'), + flags={'virtual_attribute', + 'no_create', 'no_update', 'no_search'}), ) - # LDAPObject.get_dn() only passes all but last element of keys and no kwargs - # to the parent object's get_dn() no matter what you pass to it. Make own get_dn() - # as we really need all elements to construct proper dn. + # LDAPObject.get_dn() only passes all but last element of keys and no + # kwargs to the parent object's get_dn() no matter what you pass to it. + # Make own get_dn() as we really need all elements to construct proper dn. def get_dn(self, *keys, **kwargs): sdn = [('cn', x) for x in keys] sdn.reverse() trust_type = kwargs.get('trust_type') if not trust_type: - trust_type=u'ad' + trust_type = u'ad' - dn=make_trust_dn(self.env, trust_type, DN(*sdn)) + dn = make_trust_dn(self.env, trust_type, DN(*sdn)) return dn + @register() class trustdomain_find(LDAPSearch): __doc__ = _('Search domains of the trust') - def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): + def pre_callback(self, ldap, filters, attrs_list, base_dn, + scope, *args, **options): return (filters, base_dn, ldap.SCOPE_SUBTREE) def post_callback(self, ldap, entries, truncated, *args, **options): @@ -1532,7 +1593,6 @@ def post_callback(self, ldap, entries, truncated, *args, **options): return truncated - @register() class trustdomain_mod(LDAPUpdate): __doc__ = _('Modify trustdomain of the trust') @@ -1540,31 +1600,36 @@ class trustdomain_mod(LDAPUpdate): NO_CLI = True takes_options = LDAPUpdate.takes_options + (_trust_type_option,) + @register() class trustdomain_add(LDAPCreate): __doc__ = _('Allow access from the trusted domain') NO_CLI = True takes_options = LDAPCreate.takes_options + (_trust_type_option,) - def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - # ipaNTTrustPartner must always be set to the name of the trusted domain - # See MS-ADTS 6.1.6.7.13 - entry_attrs['ipanttrustpartner'] = [dn[0]['cn']] + + def pre_callback(self, ldap, dn, e_attrs, attrs_list, *keys, **options): + # ipaNTTrustPartner must always be set to the name of the trusted + # domain. See MS-ADTS 6.1.6.7.13 + e_attrs['ipanttrustpartner'] = [dn[0]['cn']] return dn @register() class trustdomain_del(LDAPDelete): - __doc__ = _('Remove information about the domain associated with the trust.') + __doc__ = _('Remove information about the domain associated ' + 'with the trust.') - msg_summary = _('Removed information about the trusted domain "%(value)s"') + msg_summary = _('Removed information about the trusted domain ' + '"%(value)s"') def execute(self, *keys, **options): ldap = self.api.Backend.ldap2 verify_samba_component_presence(ldap, self.api) - # Note that pre-/post- callback handling for LDAPDelete is causing pre_callback - # to always receive empty keys. We need to catch the case when root domain is being deleted + # Note that pre-/post- callback handling for LDAPDelete is causing + # pre_callback to always receive empty keys. We need to catch the case + # when root domain is being deleted for domain in keys[1]: try: @@ -1603,10 +1668,10 @@ def fetch_domains_from_trust(myapi, trustinstance, **options): forest_root_name = trustinstance.remote_domain.info['dns_forest'] # We want to use Kerberos if we have admin credentials even with SMB calls - # as eventually use of NTLMSSP will be deprecated for trusted domain operations - # If admin credentials are missing, 'creds' will be None and fetch_domains - # will use HTTP/ipa.master@IPA.REALM principal, e.g. Kerberos authentication - # as well. + # as eventually use of NTLMSSP will be deprecated for trusted domain + # operations If admin credentials are missing, 'creds' will be None and + # fetch_domains will use HTTP/ipa.master@IPA.REALM principal, e.g. Kerberos + # authentication as well. creds = generate_creds(trustinstance, style=CRED_STYLE_KERBEROS, **options) server = options.get('realm_server', None) domains = ipaserver.dcerpc.fetch_domains( @@ -1616,7 +1681,8 @@ def fetch_domains_from_trust(myapi, trustinstance, **options): return domains -def add_new_domains_from_trust(myapi, trustinstance, trust_entry, domains, **options): +def add_new_domains_from_trust(myapi, trustinstance, trust_entry, + domains, **options): result = [] if not domains: return result @@ -1728,8 +1794,11 @@ def execute(self, *keys, **options): verify_samba_component_presence(ldap, self.api) if keys[0].lower() == keys[1].lower(): - raise errors.ValidationError(name='domain', - error=_("Root domain of the trust is always enabled for the existing trust")) + raise errors.ValidationError( + name='domain', + error=_("Root domain of the trust is always enabled " + "for the existing trust") + ) try: trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') trust_entry = ldap.get_entry(trust_dn) @@ -1766,8 +1835,11 @@ def execute(self, *keys, **options): verify_samba_component_presence(ldap, self.api) if keys[0].lower() == keys[1].lower(): - raise errors.ValidationError(name='domain', - error=_("cannot disable root domain of the trust, use trust-del to delete the trust itself")) + raise errors.ValidationError( + name='domain', + error=_("cannot disable root domain of the trust, " + "use trust-del to delete the trust itself") + ) try: trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') trust_entry = ldap.get_entry(trust_dn)
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org