diff options
Diffstat (limited to 'ipalib')
-rw-r--r-- | ipalib/plugins/trust.py | 329 |
1 files changed, 278 insertions, 51 deletions
diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py index 3d050299..53dee918 100644 --- a/ipalib/plugins/trust.py +++ b/ipalib/plugins/trust.py @@ -181,6 +181,13 @@ 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) + if trust_type: + container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn) + return DN(dn, container_dn) + return dn + class trust(LDAPObject): """ Trust object. @@ -195,7 +202,8 @@ class trust(LDAPObject): 'ipantauthtrustoutgoing', 'ipanttrustauthincoming', 'ipanttrustforesttrustinfo', 'ipanttrustposixoffset', 'ipantsupportedencryptiontypes' ] search_display_attributes = ['cn', 'ipantflatname', - 'ipanttrusteddomainsid', 'ipanttrusttype' ] + 'ipanttrusteddomainsid', 'ipanttrusttype', + 'ipantsidblacklistincoming', 'ipantsidblacklistoutgoing' ] label = _('Trusts') label_singular = _('Trust') @@ -241,12 +249,26 @@ class trust(LDAPObject): raise errors.ValidationError(name=attr, error=_("invalid SID: %(value)s") % dict(value=value)) -def make_trust_dn(env, trust_type, dn): - assert isinstance(dn, DN) - if trust_type in trust.trust_types: - container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn) - return DN(dn[0], container_dn) - return dn + def get_dn(self, *keys, **kwargs): + sdn = map(lambda x: ('cn', x), keys) + sdn.reverse() + trust_type = kwargs.get('trust_type') + if trust_type is None: + ldap = self.backend + filter = ldap.make_filter({'objectclass': ['ipaNTTrustedDomain'], 'cn': [keys[-1]]}) + filter = ldap.combine_filters((filter, "ipaNTSIDBlacklistIncoming=*"), rules=ldap.MATCH_ALL) + try: + result = ldap.get_entries(DN(self.container_dn, self.env.basedn), + ldap.SCOPE_SUBTREE, filter, ['']) + except errors.NotFound: + trust_type = u'ad' + else: + if len(result) > 1: + raise errors.OnlyOneValueAllowed(attr='trust domain') + return result[0].dn + + dn=make_trust_dn(self.env, trust_type, DN(*sdn)) + return dn class trust_add(LDAPCreate): __doc__ = _(''' @@ -589,10 +611,13 @@ sides. def execute_ad(self, full_join, *keys, **options): # Join domain using full credentials and with random trustdom # secret (will be generated by the join method) - try: - api.Command['trust_show'](keys[-1]) + + # First see if the trust is already in place + # Force retrieval of the trust object by not passing trust_type + dn = self.obj.get_dn(keys[-1]) + if dn: summary = _('Re-established trust to domain "%(value)s"') - except errors.NotFound: + else: summary = self.msg_summary # 1. Full access to the remote domain. Use admin credentials and @@ -665,14 +690,6 @@ class trust_del(LDAPDelete): msg_summary = _('Deleted trust "%(value)s"') - def pre_callback(self, ldap, dn, *keys, **options): - assert isinstance(dn, DN) - try: - result = self.api.Command.trust_show(keys[-1]) - except errors.NotFound, e: - self.obj.handle_not_found(*keys) - return result['result']['dn'] - class trust_mod(LDAPUpdate): __doc__ = _(""" Modify a trust (for future use). @@ -686,16 +703,10 @@ class trust_mod(LDAPUpdate): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) - result = None - try: - result = self.api.Command.trust_show(keys[-1]) - except errors.NotFound, e: - self.obj.handle_not_found(*keys) self.obj.validate_sid_blacklists(entry_attrs) - # TODO: we found the trust object, now modify it - return result['result']['dn'] + return dn class trust_find(LDAPSearch): __doc__ = _('Search for trusts.') @@ -709,8 +720,10 @@ class trust_find(LDAPSearch): # 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): - assert isinstance(base_dn, DN) - return (filters, base_dn, ldap.SCOPE_SUBTREE) + # list only trust, not trust domains + trust_filter = '(ipaNTSIDBlacklistIncoming=*)' + filter = ldap.combine_filters((filters, trust_filter), rules=ldap.MATCH_ALL) + return (filter, base_dn, ldap.SCOPE_SUBTREE) def post_callback(self, ldap, entries, truncated, *args, **options): if options.get('pkey_only', False): @@ -731,30 +744,6 @@ class trust_show(LDAPRetrieve): has_output_params = LDAPRetrieve.has_output_params + trust_output_params +\ (Str('ipanttrusttype'), Str('ipanttrustdirection')) - def execute(self, *keys, **options): - error = None - result = None - for trust_type in trust.trust_types: - options['trust_show_type'] = trust_type - try: - result = super(trust_show, self).execute(*keys, **options) - except errors.NotFound, e: - result = None - error = e - if result: - break - if error or not result: - self.obj.handle_not_found(*keys) - - return result - - def pre_callback(self, ldap, dn, entry_attrs, *keys, **options): - assert isinstance(dn, DN) - if 'trust_show_type' in options: - return make_trust_dn(self.env, options['trust_show_type'], dn) - - return dn - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): # Translate ipanttrusttype to trusttype @@ -1091,3 +1080,241 @@ class sidgen_was_run(Command): return dict(result=True) api.register(sidgen_was_run) + +class trustdomain(LDAPObject): + """ + Object representing a domain of the AD trust. + """ + parent_object = 'trust' + trust_type_idx = {'2':u'ad'} + object_name = _('trust domain') + object_name_plural = _('trust domains') + object_class = ['ipaNTTrustedDomain'] + default_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', 'ipanttrustpartner'] + search_display_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', ] + + label = _('Trusted domains') + label_singular = _('Trusted domain') + + takes_params = ( + Str('cn', + label=_('Domain name'), + cli_name='domain', + primary_key=True + ), + Str('ipantflatname?', + cli_name='flat_name', + label=_('Domain NetBIOS name'), + ), + Str('ipanttrusteddomainsid?', + cli_name='sid', + label=_('Domain Security Identifier'), + ), + Str('ipanttrustpartner?', + label=_('Trusted domain partner'), + flags=['no_display', 'no_option'], + ), + ) + + # 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 = map(lambda x: ('cn', x), keys) + sdn.reverse() + trust_type = kwargs.get('trust_type') + if not trust_type: + trust_type=u'ad' + + dn=make_trust_dn(self.env, trust_type, DN(*sdn)) + return dn +api.register(trustdomain) + +class trustdomain_find(LDAPSearch): + __doc__ = _('Search domains of the trust') + + def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): + return (filters, base_dn, ldap.SCOPE_SUBTREE) +api.register(trustdomain_find) + +class trustdomain_mod(LDAPUpdate): + __doc__ = _('Modify trustdomain of the trust') + + NO_CLI = True + takes_options = LDAPUpdate.takes_options + (_trust_type_option,) +api.register(trustdomain_mod) + +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): + if 'ipanttrustpartner' in options: + entry_attrs['ipanttrustpartner'] = [options['ipanttrustpartner']] + return dn +api.register(trustdomain_add) + +class trustdomain_del(LDAPDelete): + __doc__ = _('Remove infromation about the domain associated with the trust.') + + msg_summary = _('Removed information about the trusted domain "%(value)s"') + + def execute(self, *keys, **options): + # 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]: + if keys[0].lower() == domain: + raise errors.ValidationError(name='domain', + error=_("cannot delete root domain of the trust, use trust-del to delete the trust itself")) + try: + res = api.Command.trustdomain_enable(keys[0], domain) + except errors.AlreadyActive: + pass + result = super(trustdomain_del, self).execute(*keys, **options) + result['value'] = u','.join(keys[1]) + return result + + +api.register(trustdomain_del) + + +def fetch_domains_from_trust(self, trustinstance, trust_entry): + trust_name = trust_entry['cn'][0] + domains = ipaserver.dcerpc.fetch_domains(self.api, trustinstance.local_flatname, trust_name) + result = [] + if not domains: + return None + + for dom in domains: + dom['trust_type'] = u'ad' + try: + name = dom['cn'] + del dom['cn'] + if 'all' in options: + dom['all'] = options['all'] + if 'raw' in options: + dom['raw'] = options['raw'] + res = self.api.Command.trustdomain_add(trust_name, name, **dom) + result.append(res['result']) + except errors.DuplicateEntry: + # Ignore updating duplicate entries + pass + return result + +class trust_fetch_domains(LDAPRetrieve): + __doc__ = _('Refresh list of the domains associated with the trust') + + has_output = output.standard_list_of_entries + + def execute(self, *keys, **options): + if not _bindings_installed: + raise errors.NotFound( + name=_('AD Trust setup'), + reason=_( + 'Cannot perform join operation without Samba 4 support ' + 'installed. Make sure you have installed server-trust-ad ' + 'sub-package of IPA' + ) + ) + trust = self.api.Command.trust_show(keys[0], raw=True)['result'] + + 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' + ) + ) + domains = fetch_domains_from_trust(self, trustinstance, trust) + result = dict() + + if len(domains) > 0: + result['summary'] = unicode(_('List of trust domains successfully refreshed')) + else: + result['summary'] = unicode(_('No new trust domains were found')) + + result['result'] = domains + result['count'] = len(domains) + result['truncated'] = False + return result + +api.register(trust_fetch_domains) + +class trustdomain_enable(LDAPQuery): + __doc__ = _('Allow use of IPA resources by the domain of the trust') + + has_output = output.standard_value + msg_summary = _('Enabled trust domain "%(value)s"') + + def execute(self, *keys, **options): + ldap = self.api.Backend.ldap2 + + 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")) + try: + trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') + trust_entry = ldap.get_entry(trust_dn) + except errors.NotFound: + self.api.Object[self.obj.parent_object].handle_not_found(keys[0]) + + dn = self.obj.get_dn(keys[0], keys[1], trust_type=u'ad') + try: + entry = ldap.get_entry(dn) + sid = entry['ipanttrusteddomainsid'][0] + if sid in trust_entry['ipantsidblacklistincoming']: + trust_entry['ipantsidblacklistincoming'].remove(sid) + ldap.update_entry(trust_entry) + else: + raise errors.AlreadyActive() + except errors.NotFound: + self.obj.handle_not_found(*keys) + + return dict( + result=True, + value=keys[1], + ) + +api.register(trustdomain_enable) + +class trustdomain_disable(LDAPQuery): + __doc__ = _('Disable use of IPA resources by the domain of the trust') + + has_output = output.standard_value + msg_summary = _('Disabled trust domain "%(value)s"') + + def execute(self, *keys, **options): + ldap = self.api.Backend.ldap2 + + 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")) + try: + trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') + trust_entry = ldap.get_entry(trust_dn) + except errors.NotFound: + self.api.Object[self.obj.parent_object].handle_not_found(keys[0]) + + dn = self.obj.get_dn(keys[0], keys[1], trust_type=u'ad') + try: + entry = ldap.get_entry(dn) + sid = entry['ipanttrusteddomainsid'][0] + if not (sid in trust_entry['ipantsidblacklistincoming']): + trust_entry['ipantsidblacklistincoming'].append(sid) + ldap.update_entry(trust_entry) + else: + raise errors.AlreadyInactive() + except errors.NotFound: + self.obj.handle_not_found(*keys) + + return dict( + result=True, + value=keys[1], + ) + +api.register(trustdomain_disable) |