From 14ee02dcbd6cbb6c221ac7526e471a9fc58fcc82 Mon Sep 17 00:00:00 2001 From: Martin Basti Date: Apr 26 2016 11:37:23 +0000 Subject: Do not do extra search for ipasshpubkey to generate fingerprints Host, user and idview commands do unnnecessary extra search for ipasshpubkey attribute to generate fingerprints. Note: Host and user plugins shows ipasshpubkey only when the attribute is changed, idviews show ipasshpubkey always. This behavior has been kept by this commit. common_pre/post_callbacks were fixed in [base|stage]user modules. common_callbacks requires the same arguments as pre/post_callbacks now (except baseuser_find.post_common_callback) Note2: in *-add commands there is no need for managing ipasshpubkey as this attribute should be shown always there. https://fedorahosted.org/freeipa/ticket/3376 Reviewed-By: Stanislav Laznicka --- diff --git a/ipalib/plugins/baseuser.py b/ipalib/plugins/baseuser.py index af81b93..cb6bf26 100644 --- a/ipalib/plugins/baseuser.py +++ b/ipalib/plugins/baseuser.py @@ -32,8 +32,14 @@ from ipalib.request import context from ipalib import _ from ipapython.ipautil import ipa_generate_password from ipapython.ipavalidate import Email -from ipalib.util import (normalize_sshpubkey, validate_sshpubkey, - convert_sshpubkey_post) +from ipalib.util import ( + normalize_sshpubkey, + validate_sshpubkey, + convert_sshpubkey_post, + remove_sshpubkey_from_output_post, + remove_sshpubkey_from_output_list_post, + add_sshpubkey_to_attrs_pre, +) if six.PY3: unicode = str @@ -490,15 +496,16 @@ class baseuser_add(LDAPCreate): """ Prototype command plugin to be implemented by real plugin """ - def pre_common_callback(self, ldap, dn, entry_attrs, **options): + def pre_common_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, + **options): assert isinstance(dn, DN) self.obj.convert_usercertificate_pre(entry_attrs) - def post_common_callback(self, ldap, dn, entry_attrs, **options): + def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) self.obj.convert_usercertificate_post(entry_attrs, **options) self.obj.get_password_attributes(ldap, dn, entry_attrs) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) radius_dn2pk(self.api, entry_attrs) class baseuser_del(LDAPDelete): @@ -565,8 +572,11 @@ class baseuser_mod(LDAPUpdate): answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl) entry_attrs['ipatokenradiusconfiglink'] = answer - def pre_common_callback(self, ldap, dn, entry_attrs, **options): + def pre_common_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, + **options): assert isinstance(dn, DN) + add_sshpubkey_to_attrs_pre(self.context, attrs_list) + self.check_namelength(ldap, **options) self.check_mail(entry_attrs) @@ -578,7 +588,7 @@ class baseuser_mod(LDAPUpdate): self.check_objectclass(ldap, dn, entry_attrs) self.obj.convert_usercertificate_pre(entry_attrs) - def post_common_callback(self, ldap, dn, entry_attrs, **options): + def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) if options.get('random', False): try: @@ -589,7 +599,8 @@ class baseuser_mod(LDAPUpdate): convert_nsaccountlock(entry_attrs) self.obj.get_password_attributes(ldap, dn, entry_attrs) self.obj.convert_usercertificate_post(entry_attrs, **options) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) + remove_sshpubkey_from_output_post(self.context, entry_attrs) radius_dn2pk(self.api, entry_attrs) class baseuser_find(LDAPSearch): @@ -615,6 +626,10 @@ class baseuser_find(LDAPSearch): if cl in options: newoptions[cl] = self.api.Object['radiusproxy'].get_dn(options[cl]) + def pre_common_callback(self, ldap, filters, attrs_list, base_dn, scope, + *args, **options): + add_sshpubkey_to_attrs_pre(self.context, attrs_list) + def post_common_callback(self, ldap, entries, lockout=False, **options): for attrs in entries: self.obj.convert_usercertificate_post(attrs, **options) @@ -622,17 +637,23 @@ class baseuser_find(LDAPSearch): attrs['nsaccountlock'] = True else: convert_nsaccountlock(attrs) - convert_sshpubkey_post(ldap, attrs.dn, attrs) + convert_sshpubkey_post(attrs) + remove_sshpubkey_from_output_list_post(self.context, entries) class baseuser_show(LDAPRetrieve): """ Prototype command plugin to be implemented by real plugin """ - def post_common_callback(self, ldap, dn, entry_attrs, **options): + def pre_common_callback(self, ldap, dn, attrs_list, *keys, **options): + assert isinstance(dn, DN) + add_sshpubkey_to_attrs_pre(self.context, attrs_list) + + def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) self.obj.get_password_attributes(ldap, dn, entry_attrs) self.obj.convert_usercertificate_post(entry_attrs, **options) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) + remove_sshpubkey_from_output_post(self.context, entry_attrs) radius_dn2pk(self.api, entry_attrs) diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py index 1e8b2b6..611f9a9 100644 --- a/ipalib/plugins/host.py +++ b/ipalib/plugins/host.py @@ -44,7 +44,10 @@ from ipalib import x509 from ipalib import output from ipalib.request import context from ipalib.util import (normalize_sshpubkey, validate_sshpubkey_no_options, - convert_sshpubkey_post, validate_hostname) + convert_sshpubkey_post, validate_hostname, + add_sshpubkey_to_attrs_pre, + remove_sshpubkey_from_output_post, + remove_sshpubkey_from_output_list_post) from ipapython.ipautil import ipa_generate_password, CheckedIPAddress from ipapython.dnsutil import DNSName from ipapython.ssh import SSHPublicKey @@ -712,7 +715,7 @@ class host_add(LDAPCreate): # fetched anywhere. entry_attrs['has_keytab'] = False - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) return dn @@ -927,6 +930,8 @@ class host_mod(LDAPUpdate): if 'krbticketpolicyaux' not in entry_attrs['objectclass']: entry_attrs['objectclass'].append('krbticketpolicyaux') + add_sshpubkey_to_attrs_pre(self.context, attrs_list) + return dn def post_callback(self, ldap, dn, entry_attrs, *keys, **options): @@ -947,7 +952,8 @@ class host_mod(LDAPUpdate): self.obj.suppress_netgroup_memberof(ldap, entry_attrs) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) + remove_sshpubkey_from_output_post(self.context, entry_attrs) convert_ipaassignedidview_post(entry_attrs, options) return dn @@ -1015,6 +1021,8 @@ class host_find(LDAPSearch): (filter, hosts_filter), ldap.MATCH_ALL ) + add_sshpubkey_to_attrs_pre(self.context, attrs_list) + return (filter.replace('locality', 'l'), base_dn, scope) def post_callback(self, ldap, entries, truncated, *args, **options): @@ -1029,9 +1037,12 @@ class host_find(LDAPSearch): if options.get('all', False): entry_attrs['managing'] = self.obj.get_managed_hosts(entry_attrs.dn) - convert_sshpubkey_post(ldap, entry_attrs.dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) + remove_sshpubkey_from_output_post(self.context, entry_attrs) convert_ipaassignedidview_post(entry_attrs, options) + remove_sshpubkey_from_output_list_post(self.context, entries) + return truncated @@ -1048,6 +1059,11 @@ class host_show(LDAPRetrieve): member_attributes = ['managedby'] + def pre_callback(self, ldap, dn, attrs_list, *keys, **options): + assert isinstance(dn, DN) + add_sshpubkey_to_attrs_pre(self.context, attrs_list) + return dn + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): assert isinstance(dn, DN) self.obj.get_password_attributes(ldap, dn, entry_attrs) @@ -1065,7 +1081,8 @@ class host_show(LDAPRetrieve): self.obj.suppress_netgroup_memberof(ldap, entry_attrs) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) + remove_sshpubkey_from_output_post(self.context, entry_attrs) convert_ipaassignedidview_post(entry_attrs, options) return dn diff --git a/ipalib/plugins/idviews.py b/ipalib/plugins/idviews.py index 6f8bdc7..bfbec56 100644 --- a/ipalib/plugins/idviews.py +++ b/ipalib/plugins/idviews.py @@ -954,7 +954,7 @@ class idoverrideuser_add(baseidoverride_add): def post_callback(self, ldap, dn, entry_attrs, *keys, **options): dn = super(idoverrideuser_add, self).post_callback(ldap, dn, entry_attrs, *keys, **options) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) return dn @@ -990,7 +990,7 @@ class idoverrideuser_mod(baseidoverride_mod): def post_callback(self, ldap, dn, entry_attrs, *keys, **options): dn = super(idoverrideuser_mod, self).post_callback(ldap, dn, entry_attrs, *keys, **options) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) return dn @@ -1004,7 +1004,7 @@ class idoverrideuser_find(baseidoverride_find): truncated = super(idoverrideuser_find, self).post_callback( ldap, entries, truncated, *args, **options) for entry in entries: - convert_sshpubkey_post(ldap, entry.dn, entry) + convert_sshpubkey_post(entry) return truncated @@ -1015,7 +1015,7 @@ class idoverrideuser_show(baseidoverride_show): def post_callback(self, ldap, dn, entry_attrs, *keys, **options): dn = super(idoverrideuser_show, self).post_callback(ldap, dn, entry_attrs, *keys, **options) - convert_sshpubkey_post(ldap, dn, entry_attrs) + convert_sshpubkey_post(entry_attrs) return dn diff --git a/ipalib/plugins/stageuser.py b/ipalib/plugins/stageuser.py index d34d6c0..c147f63 100644 --- a/ipalib/plugins/stageuser.py +++ b/ipalib/plugins/stageuser.py @@ -374,7 +374,8 @@ class stageuser_add(baseuser_add): answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl) entry_attrs['ipatokenradiusconfiglink'] = answer - self.pre_common_callback(ldap, dn, entry_attrs, **options) + self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, + **options) return dn @@ -394,7 +395,7 @@ class stageuser_add(baseuser_add): # if both randompassword and userpassword options were used pass - self.post_common_callback(ldap, dn, entry_attrs, **options) + self.post_common_callback(ldap, dn, entry_attrs, *keys, **options) return dn @register() @@ -412,7 +413,8 @@ class stageuser_mod(baseuser_mod): has_output_params = baseuser_mod.has_output_params + stageuser_output_params def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): - self.pre_common_callback(ldap, dn, entry_attrs, **options) + self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, + **options) # Make sure it is not possible to authenticate with a Stage user account if 'nsaccountlock' in entry_attrs: del entry_attrs['nsaccountlock'] @@ -433,6 +435,8 @@ class stageuser_find(baseuser_find): def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): assert isinstance(base_dn, DN) + self.pre_common_callback(ldap, filter, attrs_list, base_dn, scope, + *keys, **options) container_filter = "(objectclass=posixaccount)" # provisioning system can create non posixaccount stage user @@ -458,9 +462,14 @@ class stageuser_show(baseuser_show): has_output_params = baseuser_show.has_output_params + stageuser_output_params + def pre_callback(self, ldap, dn, attrs_list, *keys, **options): + assert isinstance(dn, DN) + self.pre_common_callback(ldap, dn, attrs_list, *keys, **options) + return dn + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): entry_attrs['nsaccountlock'] = True - self.post_common_callback(ldap, dn, entry_attrs, **options) + self.post_common_callback(ldap, dn, entry_attrs, *keys, **options) return dn diff --git a/ipalib/plugins/user.py b/ipalib/plugins/user.py index 102f821..adcee2c 100644 --- a/ipalib/plugins/user.py +++ b/ipalib/plugins/user.py @@ -542,7 +542,8 @@ class user_add(baseuser_add): answer = self.api.Object['radiusproxy'].get_dn_if_exists(rcl) entry_attrs['ipatokenradiusconfiglink'] = answer - self.pre_common_callback(ldap, dn, entry_attrs, **options) + self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, + **options) return dn @@ -588,7 +589,7 @@ class user_add(baseuser_add): self.obj.get_preserved_attribute(entry_attrs, options) - self.post_common_callback(ldap, dn, entry_attrs, **options) + self.post_common_callback(ldap, dn, entry_attrs, *keys, **options) return dn @@ -752,12 +753,13 @@ class user_mod(baseuser_mod): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): dn = self.obj.get_either_dn(*keys, **options) - self.pre_common_callback(ldap, dn, entry_attrs, **options) + self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, + **options) validate_nsaccountlock(entry_attrs) return dn def post_callback(self, ldap, dn, entry_attrs, *keys, **options): - self.post_common_callback(ldap, dn, entry_attrs, **options) + self.post_common_callback(ldap, dn, entry_attrs, *keys, **options) self.obj.get_preserved_attribute(entry_attrs, options) return dn @@ -782,6 +784,8 @@ class user_find(baseuser_find): def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *keys, **options): assert isinstance(base_dn, DN) + self.pre_common_callback(ldap, filter, attrs_list, base_dn, scope, + *keys, **options) if options.get('whoami'): return ("(&(objectclass=posixaccount)(krbprincipalname=%s))"%\ @@ -830,11 +834,12 @@ class user_show(baseuser_show): def pre_callback(self, ldap, dn, attrs_list, *keys, **options): dn = self.obj.get_either_dn(*keys, **options) + self.pre_common_callback(ldap, dn, attrs_list, *keys, **options) return dn def post_callback(self, ldap, dn, entry_attrs, *keys, **options): convert_nsaccountlock(entry_attrs) - self.post_common_callback(ldap, dn, entry_attrs, **options) + self.post_common_callback(ldap, dn, entry_attrs, *keys, **options) self.obj.get_preserved_attribute(entry_attrs, options) return dn diff --git a/ipalib/util.py b/ipalib/util.py index 262acf9..709bfbb 100644 --- a/ipalib/util.py +++ b/ipalib/util.py @@ -290,12 +290,9 @@ def validate_sshpubkey_no_options(ugettext, value): if pubkey.has_options(): return _('options are not allowed') -def convert_sshpubkey_post(ldap, dn, entry_attrs): - if 'ipasshpubkey' in entry_attrs: - pubkeys = entry_attrs['ipasshpubkey'] - else: - old_entry_attrs = ldap.get_entry(dn, ['ipasshpubkey']) - pubkeys = old_entry_attrs.get('ipasshpubkey') + +def convert_sshpubkey_post(entry_attrs): + pubkeys = entry_attrs.get('ipasshpubkey') if not pubkeys: return @@ -321,6 +318,37 @@ def convert_sshpubkey_post(ldap, dn, entry_attrs): if fingerprints: entry_attrs['sshpubkeyfp'] = fingerprints + +def add_sshpubkey_to_attrs_pre(context, attrs_list): + """ + Attribute ipasshpubkey should be added to attrs_list to be able compute + ssh fingerprint. This attribute must be removed later if was added here + (see remove_sshpubkey_from_output_post). + """ + if not ('ipasshpubkey' in attrs_list or '*' in attrs_list): + setattr(context, 'ipasshpubkey_added', True) + attrs_list.append('ipasshpubkey') + + +def remove_sshpubkey_from_output_post(context, entry_attrs): + """ + Remove ipasshpubkey from output if it was added in pre_callbacks + """ + if getattr(context, 'ipasshpubkey_added', False): + entry_attrs.pop('ipasshpubkey', None) + delattr(context, 'ipasshpubkey_added') + + +def remove_sshpubkey_from_output_list_post(context, entries): + """ + Remove ipasshpubkey from output if it was added in pre_callbacks + """ + if getattr(context, 'ipasshpubkey_added', False): + for entry_attrs in entries: + entry_attrs.pop('ipasshpubkey', None) + delattr(context, 'ipasshpubkey_added') + + class cachedproperty(object): """ A property-like attribute that caches the return value of a method call.