On 04/13/2016 09:55 AM, Tomas Babej wrote: > On 04/07/2016 01:53 PM, Sumit Bose wrote: >> On Mon, Apr 04, 2016 at 04:27:02PM +0200, Jan Cholasta wrote: >>> Hi, >>> >>> On 1.4.2016 16:53, Tomas Babej wrote: >>>> Hi, >>>> >>>> this extends the user ID overrides with capability to store the user >>>> certificate. >>>> >>>> https://fedorahosted.org/freeipa/ticket/4955 >>> >>> The preferred way of managing certificates nowadays is using $OBJ-add-cert >>> and $OBJ-remove-cert commands, you should add them here as well. >>> >>> I would even go as far as not allowing to modify certificates using >>> idoverrideuser-mod - in user-mod and host-mod, it's there just for backward >>> compatibility, which is not the case here. But I don't have a strong opinion >>> on that. >>> >>> For consistency with user-find and host-find, the full certificate blob >>> should not be shown in idoverrideuser-find. You can do that by setting >>> search_display_attributes attribute on the idoverrideuser class >>> appropriately. >> >> I tested the current patch with my related patches for SSSD and all is >> working as expected. >> >> bye, >> Sumit >> >>> >>> Honza >>> >>> -- >>> Jan Cholasta >>> >>> -- >>> Manage your subscription for the Freeipa-devel mailing list: >>> https://www.redhat.com/mailman/listinfo/freeipa-devel >>> Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code >> > > Thanks for the reviews, > > attaching a updated patch that addresses Honza's comments. > > Tomas >
Sending an improved version addressing a couple of additional issues. Tomas
From f56129024fecfe1522cd6bd85f7daddfd3bf5129 Mon Sep 17 00:00:00 2001 From: Tomas Babej <tba...@redhat.com> Date: Thu, 3 Mar 2016 15:14:10 +0100 Subject: [PATCH] idviews: Add user certificate attribute to user ID overrides --- ACI.txt | 2 +- API.txt | 30 ++++++++++++++-- install/share/71idviews.ldif | 2 +- ipalib/plugins/idviews.py | 82 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/ACI.txt b/ACI.txt index 24cb332ce6e10c82a5bfab76d084fb6c0277800d..ae00cf7a1b8e2ea0e33798993bb24dc5f06127e3 100644 --- a/ACI.txt +++ b/ACI.txt @@ -149,7 +149,7 @@ aci: (targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:S dn: cn=views,cn=accounts,dc=ipa,dc=example aci: (targetattr = "cn || createtimestamp || description || entryusn || gidnumber || ipaanchoruuid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaGroupOverride)")(version 3.0;acl "permission:System: Read Group ID Overrides";allow (compare,read,search) userdn = "ldap:///all";) dn: cn=views,cn=accounts,dc=ipa,dc=example -aci: (targetattr = "createtimestamp || description || entryusn || gecos || gidnumber || homedirectory || ipaanchoruuid || ipaoriginaluid || ipasshpubkey || loginshell || modifytimestamp || objectclass || uid || uidnumber")(targetfilter = "(objectclass=ipaUserOverride)")(version 3.0;acl "permission:System: Read User ID Overrides";allow (compare,read,search) userdn = "ldap:///all";) +aci: (targetattr = "createtimestamp || description || entryusn || gecos || gidnumber || homedirectory || ipaanchoruuid || ipaoriginaluid || ipasshpubkey || loginshell || modifytimestamp || objectclass || uid || uidnumber || usercertificate")(targetfilter = "(objectclass=ipaUserOverride)")(version 3.0;acl "permission:System: Read User ID Overrides";allow (compare,read,search) userdn = "ldap:///all";) dn: cn=ranges,cn=etc,dc=ipa,dc=example aci: (targetattr = "cn || createtimestamp || entryusn || ipabaseid || ipabaserid || ipaidrangesize || ipanttrusteddomainsid || iparangetype || ipasecondarybaserid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaidrange)")(version 3.0;acl "permission:System: Read ID Ranges";allow (compare,read,search) userdn = "ldap:///all";) dn: cn=views,cn=accounts,dc=ipa,dc=example diff --git a/API.txt b/API.txt index 5b75413f930d0e9caaffc68023bed8106d786653..76b260da72533ee88027f72d56a591c7566c72b7 100644 --- a/API.txt +++ b/API.txt @@ -2429,7 +2429,7 @@ output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDA output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: PrimaryKey('value', None, None) command: idoverrideuser_add -args: 2,15,3 +args: 2,16,3 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True) arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') @@ -2446,6 +2446,19 @@ option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui option: Str('setattr*', cli_name='setattr', exclude='webui') option: 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_.$-]?$', required=False) option: Int('uidnumber', attribute=True, cli_name='uid', minvalue=1, multivalue=False, required=False) +option: Bytes('usercertificate', attribute=True, cli_name='certificate', multivalue=True, required=False) +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: PrimaryKey('value', None, None) +command: idoverrideuser_add_cert +args: 2,5,3 +arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True) +arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('fallback_to_ldap?', autofill=True, default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Bytes('usercertificate', alwaysask=True, attribute=True, cli_name='certificate', multivalue=True, required=False) 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) @@ -2485,7 +2498,7 @@ output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('truncated', <type 'bool'>, None) command: idoverrideuser_mod -args: 2,18,3 +args: 2,19,3 arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True) arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=True) option: Str('addattr*', cli_name='addattr', exclude='webui') @@ -2505,6 +2518,19 @@ option: Flag('rights', autofill=True, default=False) option: Str('setattr*', cli_name='setattr', exclude='webui') option: Str('uid', attribute=True, autofill=False, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', required=False) option: Int('uidnumber', attribute=True, autofill=False, cli_name='uid', minvalue=1, multivalue=False, required=False) +option: Bytes('usercertificate', attribute=True, autofill=False, cli_name='certificate', multivalue=True, required=False) +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: PrimaryKey('value', None, None) +command: idoverrideuser_remove_cert +args: 2,5,3 +arg: Str('idviewcn', cli_name='idview', multivalue=False, primary_key=True, query=True, required=True) +arg: Str('ipaanchoruuid', attribute=True, cli_name='anchor', multivalue=False, primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('fallback_to_ldap?', autofill=True, default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Bytes('usercertificate', alwaysask=True, attribute=True, cli_name='certificate', multivalue=True, required=False) 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) diff --git a/install/share/71idviews.ldif b/install/share/71idviews.ldif index 33db9840bf39ab6f0f2e0eb04de8568d47460f21..b2fa1f414874b432f874dcb10c8d7f4463aec617 100644 --- a/install/share/71idviews.ldif +++ b/install/share/71idviews.ldif @@ -3,6 +3,6 @@ attributeTypes: (2.16.840.1.113730.3.8.11.62 NAME 'ipaAnchorUUID' DESC 'Unique A attributeTypes: (2.16.840.1.113730.3.8.11.63 NAME 'ipaOriginalUid' DESC 'Original UID of overriden user' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v4') objectClasses: (2.16.840.1.113730.3.8.12.29 NAME 'ipaIDView' SUP nsContainer STRUCTURAL MAY ( description ) X-ORIGIN 'IPA v4' ) objectClasses: (2.16.840.1.113730.3.8.12.30 NAME 'ipaOverrideAnchor' SUP top STRUCTURAL MUST ( ipaAnchorUUID ) MAY ( description ) X-ORIGIN 'IPA v4' ) -objectClasses: (2.16.840.1.113730.3.8.12.31 NAME 'ipaUserOverride' DESC 'Override for User Attributes' SUP ipaOverrideAnchor STRUCTURAL MAY ( uid $ uidNumber $ gidNumber $ homeDirectory $ loginShell $ gecos $ ipaOriginalUid ) X-ORIGIN 'IPA v4' ) +objectClasses: (2.16.840.1.113730.3.8.12.31 NAME 'ipaUserOverride' DESC 'Override for User Attributes' SUP ipaOverrideAnchor STRUCTURAL MAY ( uid $ uidNumber $ gidNumber $ homeDirectory $ loginShell $ gecos $ ipaOriginalUid $ userCertificate ) X-ORIGIN 'IPA v4' ) objectClasses: (2.16.840.1.113730.3.8.12.32 NAME 'ipaGroupOverride' DESC 'Override for Group Attributes' SUP ipaOverrideAnchor STRUCTURAL MAY ( gidNumber $ cn ) X-ORIGIN 'IPA v4' ) objectClasses: (2.16.840.1.113730.3.8.12.35 NAME 'ipaOverrideTarget' SUP top STRUCTURAL MUST ( ipaAnchorUUID ) X-ORIGIN 'IPA v4' ) diff --git a/ipalib/plugins/idviews.py b/ipalib/plugins/idviews.py index 6f8bdc7a8f5f50e82d77aa6696092ce6b43aeb9d..d5bb790da38f1e6653ebc5ed4cfffd5b9c60ca11 100644 --- a/ipalib/plugins/idviews.py +++ b/ipalib/plugins/idviews.py @@ -23,9 +23,12 @@ import six from ipalib.plugins.baseldap import (LDAPQuery, LDAPObject, LDAPCreate, LDAPDelete, LDAPUpdate, LDAPSearch, + LDAPAddAttribute, LDAPRemoveAttribute, LDAPRetrieve, global_output_params) from ipalib.plugins.hostgroup import get_complete_hostgroup_member_list -from ipalib import api, Str, Int, Flag, _, ngettext, errors, output +from ipalib.plugins.service import validate_certificate +from ipalib import api, Str, Int, Bytes, Flag, _, ngettext, errors, output +from ipalib import x509 from ipalib.constants import IPA_ANCHOR_PREFIX, SID_ANCHOR_PREFIX from ipalib.plugable import Registry from ipalib.util import (normalize_sshpubkey, validate_sshpubkey, @@ -817,7 +820,7 @@ class idoverrideuser(baseidoverride): 'ipapermdefaultattr': { 'objectClass', 'ipaAnchorUUID', 'uidNumber', 'description', 'homeDirectory', 'uid', 'ipaOriginalUid', 'loginShell', 'gecos', - 'gidNumber', 'ipaSshPubkey', + 'gidNumber', 'ipaSshPubkey', 'usercertificate' }, }, } @@ -826,6 +829,11 @@ class idoverrideuser(baseidoverride): possible_objectclasses = ['ipasshuser', 'ipaSshGroupOfPubKeys'] default_attributes = baseidoverride.default_attributes + [ 'homeDirectory', 'uidNumber', 'uid', 'ipaOriginalUid', 'loginShell', + 'ipaSshPubkey', 'gidNumber', 'gecos', 'usercertificate;binary', + ] + + search_display_attributes = baseidoverride.default_attributes + [ + 'homeDirectory', 'uidNumber', 'uid', 'ipaOriginalUid', 'loginShell', 'ipaSshPubkey', 'gidNumber', 'gecos', ] @@ -870,6 +878,12 @@ class idoverrideuser(baseidoverride): csv=True, flags=['no_search'], ), + Bytes('usercertificate*', validate_certificate, + cli_name='certificate', + label=_('Certificate'), + doc=_('Base-64 encoded user certificate'), + flags=['no_search',], + ), ) override_object = 'user' @@ -888,6 +902,17 @@ class idoverrideuser(baseidoverride): # we have no way to update the original_uid pass + def convert_usercertificate_pre(self, entry_attrs): + if 'usercertificate' in entry_attrs: + entry_attrs['usercertificate;binary'] = entry_attrs.pop( + 'usercertificate') + + def convert_usercertificate_post(self, entry_attrs, **options): + if 'usercertificate;binary' in entry_attrs: + entry_attrs['usercertificate'] = entry_attrs.pop( + 'usercertificate;binary') + + @register() class idoverridegroup(baseidoverride): @@ -935,6 +960,50 @@ class idoverridegroup(baseidoverride): override_object = 'group' +@register() +class idoverrideuser_add_cert(LDAPAddAttribute): + __doc__ = _('Add one or more certificates to the idoverrideuser entry') + msg_summary = _('Added certificates to idoverrideuser "%(value)s"') + attribute = 'usercertificate' + + takes_options = LDAPAddAttribute.takes_options + (fallback_to_ldap_option,) + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, + **options): + dn = self.obj.get_dn(*keys, **options) + self.obj.convert_usercertificate_pre(entry_attrs) + + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + self.obj.convert_usercertificate_post(entry_attrs, **options) + self.obj.convert_anchor_to_human_readable_form(entry_attrs, **options) + return dn + + +@register() +class idoverrideuser_remove_cert(LDAPRemoveAttribute): + __doc__ = _('Remove one or more certificates to the idoverrideuser entry') + msg_summary = _('Removed certificates from idoverrideuser "%(value)s"') + attribute = 'usercertificate' + + takes_options = LDAPRemoveAttribute.takes_options + (fallback_to_ldap_option,) + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, + **options): + dn = self.obj.get_dn(*keys, **options) + self.obj.convert_usercertificate_pre(entry_attrs) + + return dn + + def post_callback(self, ldap, dn, entry_attrs, *keys, **options): + assert isinstance(dn, DN) + self.obj.convert_usercertificate_post(entry_attrs, **options) + self.obj.convert_anchor_to_human_readable_form(entry_attrs, **options) + + return dn + @register() class idoverrideuser_add(baseidoverride_add): @@ -947,6 +1016,13 @@ class idoverrideuser_add(baseidoverride_add): entry_attrs['objectclass'].append('ipasshuser') + # Normalize the certificate to DER format + certs = options.get('usercertificate', []) + certs_der = [x509.normalize_certificate(c) for c in certs] + entry_attrs['usercertificate'] = certs_der + + self.obj.convert_usercertificate_pre(entry_attrs) + # Update the ipaOriginalUid self.obj.update_original_uid_reference(entry_attrs) return dn @@ -954,6 +1030,8 @@ 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) + + self.obj.convert_usercertificate_post(entry_attrs, **options) convert_sshpubkey_post(ldap, dn, entry_attrs) return dn -- 2.5.5
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code