On 8.10.2014 18:51, Petr Vobornik wrote:
On 1.10.2014 18:15, Petr Vobornik wrote:
Hello list,

Patch for: https://fedorahosted.org/freeipa/ticket/4419


New revisions of 761 and 763 with updated API and ACIs:

ipa host-allow-operation HOSTNAME retrieve-keytab --users=STR --groups STR
   ipa host-disallow-operation HOSTNAME retrieve-keytab --users=STR
--groups STR
   ipa host-allow-operation HOSTNAME create-keytab --users=STR --groups STR
   ipa host-disallow-operation HOSTNAME create-keytab --users=STR
--groups STR

   ipa service-allow-operation PRINCIPAL retrieve-keytab --users=STR
--groups STR
   ipa service-disallow-operation PRINCIPAL retrieve-keytab --users=STR
--groups STR
   ipa service-allow-operation PRINCIPAL create-keytab --users=STR
--groups STR
   ipa service-disallow-operation PRINCIPAL create-keytab --users=STR
--groups STR

ACIs are targeted to specific operations by including subtypes.


patch #761 rebased because of VERSION bump
--
Petr Vobornik
From 224f09d92b64d1658a13b760a85bc562729af8ed Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvobo...@redhat.com>
Date: Thu, 2 Oct 2014 16:57:08 +0200
Subject: [PATCH] keytab manipulation permission management

Adds new API:
  ipa host-allow-operation HOSTNAME retrieve-keytab --users=STR --groups STR
  ipa host-disallow-operation HOSTNAME retrieve-keytab --users=STR --groups STR
  ipa host-allow-operation HOSTNAME create-keytab --users=STR --groups STR
  ipa host-disallow-operation HOSTNAME create-keytab --users=STR --groups STR

  ipa service-allow-operation PRINCIPAL retrieve-keytab --users=STR --groups STR
  ipa service-disallow-operation PRINCIPAL retrieve-keytab --users=STR --groups STR
  ipa service-allow-operation PRINCIPAL create-keytab --users=STR --groups STR
  ipa service-disallow-operation PRINCIPAL create-keytab --users=STR --groups STR

these methods add or remove user or group DNs in `ipaallowedtoperform` attr with
`read_keys` and `write_keys` subtypes.

service|host-mod|show outputs these attrs as:

  Users allowed to retrieve keytab: user1
  Groups allowed to retrieve keytab: group1
  Users allowed to create keytab: user1
  Groups allowed to create keytab: group1

Adding of object class is implemented as a reusable method since this code is
used on many places and most likely will be also used in new features. Older
code may be refactored later.

https://fedorahosted.org/freeipa/ticket/4419
---
 ACI.txt                    |   4 ++
 API.txt                    |  52 +++++++++++++++++++
 VERSION                    |   4 +-
 ipalib/plugins/baseldap.py |  17 ++++++
 ipalib/plugins/host.py     |  51 ++++++++++++++++--
 ipalib/plugins/service.py  | 127 +++++++++++++++++++++++++++++++++++++++++++--
 6 files changed, 244 insertions(+), 11 deletions(-)

diff --git a/ACI.txt b/ACI.txt
index bc031ff1f24b9e9f08fe9ba78e5a262162f32cef..6fc88233e340d6eabe4ad819176e3cfa98d8c9ce 100644
--- a/ACI.txt
+++ b/ACI.txt
@@ -95,6 +95,8 @@ aci: (targetattr = "userpassword")(targetfilter = "(objectclass=ipahost)")(versi
 dn: cn=computers,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "krblastpwdchange || krbprincipalkey")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=computers,cn=accounts,dc=ipa,dc=example
+aci: (targetattr = "createtimestamp || entryusn || ipaallowedtoperform;read_keys || ipaallowedtoperform;write_keys || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Keytab Permissions";allow (compare,read,search,write) groupdn = "ldap:///cn=System: Manage Host Keytab Permissions,cn=permissions,cn=pbac,dc=ipa,dc=example";)
+dn: cn=computers,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "ipasshpubkey")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host SSH Public Keys";allow (write) groupdn = "ldap:///cn=System: Manage Host SSH Public Keys,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=computers,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "description || ipaassignedidview || l || macaddress || nshardwareplatform || nshostlocation || nsosversion || userclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Modify Hosts";allow (write) groupdn = "ldap:///cn=System: Modify Hosts,cn=permissions,cn=pbac,dc=ipa,dc=example";)
@@ -193,6 +195,8 @@ aci: (targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:Sys
 dn: cn=services,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "krblastpwdchange || krbprincipalkey")(targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Manage Service Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Service Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=services,cn=accounts,dc=ipa,dc=example
+aci: (targetattr = "createtimestamp || entryusn || ipaallowedtoperform;read_keys || ipaallowedtoperform;write_keys || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Manage Service Keytab Permissions";allow (compare,read,search,write) groupdn = "ldap:///cn=System: Manage Service Keytab Permissions,cn=permissions,cn=pbac,dc=ipa,dc=example";)
+dn: cn=services,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "usercertificate")(targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Modify Services";allow (write) groupdn = "ldap:///cn=System: Modify Services,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=services,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "createtimestamp || entryusn || ipakrbauthzdata || ipakrbprincipalalias || ipauniqueid || krbcanonicalname || krblastpwdchange || krbobjectreferences || krbpasswordexpiration || krbprincipalaliases || krbprincipalexpiration || krbprincipalname || managedby || memberof || modifytimestamp || objectclass || usercertificate")(targetfilter = "(objectclass=ipaservice)")(version 3.0;acl "permission:System: Read Services";allow (compare,read,search) userdn = "ldap:///all";;)
diff --git a/API.txt b/API.txt
index 1af78509732b13eec07208114cea00e56c1059b4..0dbc7b2c8d4fb5d337d4436395f2213713c4d019 100644
--- a/API.txt
+++ b/API.txt
@@ -1819,6 +1819,19 @@ option: Str('version?', exclude='webui')
 output: Output('completed', <type 'int'>, None)
 output: Output('failed', <type 'dict'>, None)
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+command: host_allow_operation
+args: 2,6,3
+arg: Str('fqdn', attribute=True, cli_name='hostname', multivalue=False, primary_key=True, query=True, required=True)
+arg: StrEnum('operation', values=(u'retrieve-keytab', u'create-keytab'))
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: host_del
 args: 1,3,3
 arg: Str('fqdn', attribute=True, cli_name='hostname', multivalue=True, primary_key=True, query=True, required=True)
@@ -1835,6 +1848,19 @@ option: Str('version?', exclude='webui')
 output: Output('result', <type 'bool'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: host_disallow_operation
+args: 2,6,3
+arg: Str('fqdn', attribute=True, cli_name='hostname', multivalue=False, primary_key=True, query=True, required=True)
+arg: StrEnum('operation', values=(u'retrieve-keytab', u'create-keytab'))
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: host_find
 args: 1,34,4
 arg: Str('criteria?', noextrawhitespace=False)
@@ -3473,6 +3499,19 @@ option: Str('version?', exclude='webui')
 output: Output('completed', <type 'int'>, None)
 output: Output('failed', <type 'dict'>, None)
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+command: service_allow_operation
+args: 2,6,3
+arg: Str('krbprincipalname', attribute=True, cli_name='principal', multivalue=False, primary_key=True, query=True, required=True)
+arg: StrEnum('operation', values=(u'retrieve-keytab', u'create-keytab'))
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: service_del
 args: 1,2,3
 arg: Str('krbprincipalname', attribute=True, cli_name='principal', multivalue=True, primary_key=True, query=True, required=True)
@@ -3488,6 +3527,19 @@ option: Str('version?', exclude='webui')
 output: Output('result', <type 'bool'>, None)
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: PrimaryKey('value', None, None)
+command: service_disallow_operation
+args: 2,6,3
+arg: Str('krbprincipalname', attribute=True, cli_name='principal', multivalue=False, primary_key=True, query=True, required=True)
+arg: StrEnum('operation', values=(u'retrieve-keytab', u'create-keytab'))
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('group*', alwaysask=True, cli_name='groups', csv=True)
+option: Flag('no_members', autofill=True, default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('user*', alwaysask=True, cli_name='users', csv=True)
+option: Str('version?', exclude='webui')
+output: Output('completed', <type 'int'>, None)
+output: Output('failed', <type 'dict'>, None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 command: service_find
 args: 1,11,4
 arg: Str('criteria?', noextrawhitespace=False)
diff --git a/VERSION b/VERSION
index 03f713bd9d7e245eb4fe7fd36a5ae62dc49be266..b1b38544bd48bc5e559ec0a0aef390d4a31af60f 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=105
-# Last change: abbra - ID views attributes
+IPA_API_VERSION_MINOR=106
+# Last change: pvoborni - manage authorization of keytab operations
diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py
index e589a53219766a2aa41bbb4abef7fc3a12fcc267..375441c0fd55efe70d5a6f5bed6700e618874082 100644
--- a/ipalib/plugins/baseldap.py
+++ b/ipalib/plugins/baseldap.py
@@ -494,6 +494,23 @@ def host_is_master(ldap, fqdn):
         return
 
 
+def add_missing_object_class(ldap, objectclass, dn, entry_attrs=None, update=True):
+    """
+    Add object class if missing into entry. Fetches entry if not passed. Updates
+    the entry by default.
+
+    Returns the entry
+    """
+
+    if not entry_attrs:
+        entry_attrs = ldap.get_entry(dn, ['objectclass'])
+    if (objectclass.lower() not in (o.lower() for o in entry_attrs['objectclass'])):
+        entry_attrs['objectclass'].append(objectclass)
+        if update:
+            ldap.update_entry(entry_attrs)
+    return entry_attrs
+
+
 class LDAPObject(Object):
     """
     Object representing a LDAP entry.
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index bbee09395dc55730d400944729a5bed48670f785..901ab0a48f548c287eee353fedaa9d0c1ad38a8d 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -28,10 +28,12 @@ from ipalib.plugins.baseldap import (LDAPQuery, LDAPObject, LDAPCreate,
                                      LDAPDelete, LDAPUpdate, LDAPSearch,
                                      LDAPRetrieve, LDAPAddMember,
                                      LDAPRemoveMember, host_is_master,
-                                     pkey_to_value)
+                                     pkey_to_value, add_missing_object_class)
 from ipalib.plugins.service import (split_principal, validate_certificate,
     set_certificate_attrs, ticket_flags_params, update_krbticketflags,
-    set_kerberos_attrs)
+    set_kerberos_attrs, rename_ipaallowedtoperform_from_ldap,
+    rename_ipaallowedtoperform_to_ldap, service_allow_operation,
+    service_disallow_operation)
 from ipalib.plugins.dns import (dns_container_exists, _record_types,
         add_records_for_host_validation, add_records_for_host,
         get_reverse_zone)
@@ -102,6 +104,9 @@ EXAMPLES:
 """) + _("""
  Add a host that can manage this host's keytab and certificate:
    ipa host-add-managedby --hosts=test2 test
+""") + _("""
+ Allow user to create keytab:
+   ipa host-allow-operation test2 create-keytab --users=tuser1
 """)
 
 register = Registry()
@@ -201,6 +206,18 @@ host_output_params = (
     Str('sshpubkeyfp*',
         label=_('SSH public key fingerprint'),
     ),
+    Str('ipaallowedtoperform_read_keys_user',
+        label=_('Users allowed to retrieve keytab'),
+    ),
+    Str('ipaallowedtoperform_read_keys_group',
+        label=_('Groups allowed to retrieve keytab'),
+    ),
+    Str('ipaallowedtoperform_write_keys_user',
+        label=_('Users allowed to create keytab'),
+    ),
+    Str('ipaallowedtoperform_write_keys_group',
+        label=_('Groups allowed to create keytab'),
+    ),
 )
 
 
@@ -241,17 +258,18 @@ class host(LDAPObject):
     object_name = _('host')
     object_name_plural = _('hosts')
     object_class = ['ipaobject', 'nshost', 'ipahost', 'pkiuser', 'ipaservice']
+    possible_objectclasses = ['ipaallowedoperations']
     permission_filter_objectclasses = ['ipahost']
     # object_class_config = 'ipahostobjectclasses'
     search_attributes = [
         'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
-        'nshardwareplatform', 'nsosversion', 'managedby'
+        'nshardwareplatform', 'nsosversion', 'managedby', 'ipaallowedtoperform'
     ]
     default_attributes = [
         'fqdn', 'description', 'l', 'nshostlocation', 'krbprincipalname',
         'nshardwareplatform', 'nsosversion', 'usercertificate', 'memberof',
         'managedby', 'memberindirect', 'memberofindirect', 'macaddress',
-        'userclass'
+        'userclass', 'ipaallowedtoperform'
     ]
     uuid_attribute = 'ipauniqueid'
     attribute_members = {
@@ -261,6 +279,8 @@ class host(LDAPObject):
         'managing': ['host'],
         'memberofindirect': ['hostgroup', 'netgroup', 'role', 'hbacrule',
         'sudorule'],
+        'ipaallowedtoperform_read_keys': ['user', 'group'],
+        'ipaallowedtoperform_write_keys': ['user', 'group'],
     }
     bindable = True
     relationships = {
@@ -268,6 +288,8 @@ class host(LDAPObject):
         'enrolledby': ('Enrolled by', 'enroll_by_', 'not_enroll_by_'),
         'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
         'managing': ('Managing', 'man_', 'not_man_'),
+        'ipaallowedtoperform_read_keys': ('Allow to retrieve keytab by', 'retrieve_keytab_by_', 'not_retrieve_keytab_by_'),
+        'ipaallowedtoperform_write_keys': ('Allow to create keytab by', 'write_keytab_by_', 'not_write_keytab_by'),
     }
     password_attributes = [('userpassword', 'has_password'),
                            ('krbprincipalkey', 'has_keytab')]
@@ -344,6 +366,14 @@ class host(LDAPObject):
             ],
             'default_privileges': {'Host Administrators', 'Host Enrollment'},
         },
+        'System: Manage Host Keytab Permissions': {
+            'ipapermright': {'read', 'search', 'compare', 'write'},
+            'ipapermdefaultattr': {
+                'ipaallowedtoperform;write_keys',
+                'ipaallowedtoperform;read_keys', 'objectclass'
+            },
+            'default_privileges': {'Host Administrators'},
+        },
         'System: Modify Hosts': {
             'ipapermright': {'write'},
             'ipapermdefaultattr': {
@@ -629,6 +659,7 @@ class host_add(LDAPCreate):
             )
         set_certificate_attrs(entry_attrs)
         set_kerberos_attrs(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
 
         if options.get('all', False):
             entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
@@ -888,6 +919,7 @@ class host_mod(LDAPUpdate):
             entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
         set_certificate_attrs(entry_attrs)
         set_kerberos_attrs(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
         self.obj.get_password_attributes(ldap, dn, entry_attrs)
         if entry_attrs['has_password']:
             # If an OTP is set there is no keytab, at least not one
@@ -974,6 +1006,7 @@ class host_find(LDAPSearch):
         for entry_attrs in entries:
             set_certificate_attrs(entry_attrs)
             set_kerberos_attrs(entry_attrs, options)
+            rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
             self.obj.get_password_attributes(ldap, entry_attrs.dn, entry_attrs)
             self.obj.suppress_netgroup_memberof(ldap, entry_attrs)
             if entry_attrs['has_password']:
@@ -1012,6 +1045,7 @@ class host_show(LDAPRetrieve):
 
         set_certificate_attrs(entry_attrs)
         set_kerberos_attrs(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
 
         if options.get('all', False):
             entry_attrs['managing'] = self.obj.get_managed_hosts(dn)
@@ -1159,3 +1193,12 @@ class host_remove_managedby(LDAPRemoveMember):
         self.obj.suppress_netgroup_memberof(ldap, entry_attrs)
         return (completed, dn)
 
+
+@register()
+class host_allow_operation(service_allow_operation):
+    __doc__ = _('Allow users or groups to perform operation connected with this host.')
+
+
+@register()
+class host_disallow_operation(service_disallow_operation):
+    __doc__ = _('Disallow users or groups to perform operation connected with this host.')
diff --git a/ipalib/plugins/service.py b/ipalib/plugins/service.py
index 3ca5066f300a004c993660e0a5e220c364e38cbf..a152ed72c032f33fede1df69ab87bb17dd4331c2 100644
--- a/ipalib/plugins/service.py
+++ b/ipalib/plugins/service.py
@@ -86,7 +86,10 @@ EXAMPLES:
 
  Request a certificate for an IPA service:
    ipa cert-request --principal=HTTP/web.example.com example.csr
-
+""") + _("""
+ Allow user to create keytab:
+   ipa service-allow-operation HTTP/web.example.com create-keytab --users=tuser1
+""") + _("""
  Generate and retrieve a keytab for an IPA service:
    ipa-getkeytab -s ipa.example.com -p HTTP/web.example.com -k /etc/httpd/httpd.keytab
 
@@ -127,7 +130,19 @@ output_params = (
     ),
     Str('revocation_reason?',
         label=_('Revocation reason'),
-    )
+    ),
+    Str('ipaallowedtoperform_read_keys_user',
+        label=_('Users allowed to retrieve keytab'),
+    ),
+    Str('ipaallowedtoperform_read_keys_group',
+        label=_('Groups allowed to retrieve keytab'),
+    ),
+    Str('ipaallowedtoperform_write_keys_user',
+        label=_('Users allowed to create keytab'),
+    ),
+    Str('ipaallowedtoperform_write_keys_group',
+        label=_('Groups allowed to create keytab'),
+    ),
 )
 
 ticket_flags_params = (
@@ -152,6 +167,11 @@ _ticket_flags_map = {
 
 _ticket_flags_default = _ticket_flags_map['ipakrbrequirespreauth']
 
+ipaallowedtoperform_subtypes_map = {
+    u'retrieve-keytab': 'read_keys',
+    u'create-keytab': 'write_keys',
+}
+
 def split_principal(principal):
     service = hostname = realm = None
 
@@ -290,6 +310,22 @@ def set_kerberos_attrs(entry_attrs, options):
         if name in options or all_opt:
             entry_attrs[name] = bool(ticket_flags & value)
 
+def rename_ipaallowedtoperform_from_ldap(entry_attrs, options):
+    if options.get('raw', False):
+        return
+
+    for subtype in (ipaallowedtoperform_subtypes_map.values()):
+        name = 'ipaallowedtoperform;%s' % subtype
+        if name in entry_attrs:
+            new_name = 'ipaallowedtoperform_%s' % subtype
+            entry_attrs[new_name] = entry_attrs.pop(name)
+
+def rename_ipaallowedtoperform_to_ldap(entry_attrs, subtype):
+    if 'ipaallowedtoperform_read_keys' in entry_attrs:
+        new_name = 'ipaallowedtoperform;%s' % subtype
+        entry_attrs[new_name] = entry_attrs.pop('ipaallowedtoperform_read_keys')
+
+
 @register()
 class service(LDAPObject):
     """
@@ -302,19 +338,24 @@ class service(LDAPObject):
         'krbprincipal', 'krbprincipalaux', 'krbticketpolicyaux', 'ipaobject',
         'ipaservice', 'pkiuser'
     ]
-    possible_objectclasses = ['ipakrbprincipal']
+    possible_objectclasses = ['ipakrbprincipal', 'ipaallowedoperations']
     permission_filter_objectclasses = ['ipaservice']
-    search_attributes = ['krbprincipalname', 'managedby', 'ipakrbauthzdata']
+    search_attributes = ['krbprincipalname', 'managedby', 'ipakrbauthzdata',
+        'ipaallowedtoperform']
     default_attributes = ['krbprincipalname', 'usercertificate', 'managedby',
-        'ipakrbauthzdata', 'memberof']
+        'ipakrbauthzdata', 'memberof', 'ipaallowedtoperform']
     uuid_attribute = 'ipauniqueid'
     attribute_members = {
         'managedby': ['host'],
         'memberof': ['role'],
+        'ipaallowedtoperform_read_keys': ['user', 'group'],
+        'ipaallowedtoperform_write_keys': ['user', 'group'],
     }
     bindable = True
     relationships = {
         'managedby': ('Managed by', 'man_by_', 'not_man_by_'),
+        'ipaallowedtoperform_read_keys': ('Allow to retrieve keytab by', 'retrieve_keytab_by_', 'not_retrieve_keytab_by_'),
+        'ipaallowedtoperform_write_keys': ('Allow to create keytab by', 'write_keytab_by_', 'not_write_keytab_by'),
     }
     password_attributes = [('krbprincipalkey', 'has_keytab')]
     managed_permissions = {
@@ -346,6 +387,14 @@ class service(LDAPObject):
             ],
             'default_privileges': {'Service Administrators', 'Host Administrators'},
         },
+        'System: Manage Service Keytab Permissions': {
+            'ipapermright': {'read', 'search', 'compare', 'write'},
+            'ipapermdefaultattr': {
+                'ipaallowedtoperform;write_keys',
+                'ipaallowedtoperform;read_keys', 'objectclass'
+            },
+            'default_privileges': {'Service Administrators', 'Host Administrators'},
+        },
         'System: Modify Services': {
             'ipapermright': {'write'},
             'ipapermdefaultattr': {'usercertificate'},
@@ -469,6 +518,7 @@ class service_add(LDAPCreate):
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
         set_kerberos_attrs(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
         return dn
 
 
@@ -561,6 +611,7 @@ class service_mod(LDAPUpdate):
         assert isinstance(dn, DN)
         set_certificate_attrs(entry_attrs)
         set_kerberos_attrs(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
         return dn
 
 
@@ -598,6 +649,7 @@ class service_find(LDAPSearch):
             self.obj.get_password_attributes(ldap, entry_attrs.dn, entry_attrs)
             set_certificate_attrs(entry_attrs)
             set_kerberos_attrs(entry_attrs, options)
+            rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
         return truncated
 
 
@@ -620,6 +672,7 @@ class service_show(LDAPRetrieve):
 
         set_certificate_attrs(entry_attrs)
         set_kerberos_attrs(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
 
         return dn
 
@@ -654,6 +707,70 @@ class service_remove_host(LDAPRemoveMember):
     has_output_params = LDAPRemoveMember.has_output_params + output_params
 
 
+@register()
+class service_allow_operation(LDAPAddMember):
+    __doc__ = _('Allow users or groups to perform operation connected with this service.')
+
+    # We need to choose existing attribute_member to properly generate options
+    # for users and groups. The subtype is removed/modified in pre and post
+    # callback according to specified operation.
+    member_attributes = ['ipaallowedtoperform_read_keys']
+    has_output_params = LDAPAddMember.has_output_params + output_params
+
+    def get_args(self):
+        for arg in super(service_allow_operation, self).get_args():
+            yield arg
+        yield StrEnum(
+            'operation',
+            label=_('Operation'),
+            doc=_('Operation to allow'),
+            values=tuple(ipaallowedtoperform_subtypes_map.keys()))
+
+    def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+        rename_ipaallowedtoperform_to_ldap(found, options['_subtype'])
+        rename_ipaallowedtoperform_to_ldap(not_found, options['_subtype'])
+        add_missing_object_class(ldap, u'ipaallowedoperations', dn)
+        return dn
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(failed, options)
+        return (completed, dn)
+
+    def execute(self, *keys, **options):
+        options['_subtype'] = ipaallowedtoperform_subtypes_map[keys[-1]]
+        return super(service_allow_operation, self).execute(*keys[:-1], **options)
+
+
+@register()
+class service_disallow_operation(LDAPRemoveMember):
+    __doc__ = _('Disallow users or groups to perform operation connected with this service.')
+    member_attributes = ['ipaallowedtoperform_read_keys']
+    has_output_params = LDAPRemoveMember.has_output_params + output_params
+
+    def get_args(self):
+        for arg in super(service_disallow_operation, self).get_args():
+            yield arg
+        yield StrEnum(
+            'operation',
+            label=_('Operation'),
+            doc=_('Operation to disallow'),
+            values=tuple(ipaallowedtoperform_subtypes_map.keys()))
+
+    def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+        rename_ipaallowedtoperform_to_ldap(found, options['_subtype'])
+        rename_ipaallowedtoperform_to_ldap(not_found, options['_subtype'])
+        return dn
+
+    def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
+        rename_ipaallowedtoperform_from_ldap(entry_attrs, options)
+        rename_ipaallowedtoperform_from_ldap(failed, options)
+        return (completed, dn)
+
+    def execute(self, *keys, **options):
+        options['_subtype'] = ipaallowedtoperform_subtypes_map[keys[-1]]
+        return super(service_disallow_operation, self).execute(*keys[:-1], **options)
+
 
 @register()
 class service_disable(LDAPQuery):
-- 
1.9.3

From f416a37c68ab73572a3695cb4f3d85d488cda68d Mon Sep 17 00:00:00 2001
From: Petr Vobornik <pvobo...@redhat.com>
Date: Fri, 3 Oct 2014 13:00:57 +0200
Subject: [PATCH] tests: management of keytab permissions

https://fedorahosted.org/freeipa/ticket/4419
---
 ipatests/test_xmlrpc/test_host_plugin.py    | 364 +++++++++++++++++++++++++++
 ipatests/test_xmlrpc/test_service_plugin.py | 370 ++++++++++++++++++++++++++++
 2 files changed, 734 insertions(+)

diff --git a/ipatests/test_xmlrpc/test_host_plugin.py b/ipatests/test_xmlrpc/test_host_plugin.py
index 479afd100e974d1380fc7d940782d4bd5a2f158f..3fa1506d7a69e25d61d42503fd4e836626e7bcf1 100644
--- a/ipatests/test_xmlrpc/test_host_plugin.py
+++ b/ipatests/test_xmlrpc/test_host_plugin.py
@@ -34,6 +34,8 @@ from nose.plugins.skip import SkipTest
 from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, XMLRPC_test,
     fuzzy_uuid, fuzzy_digits, fuzzy_hash, fuzzy_date, fuzzy_issuer,
     fuzzy_hex)
+from ipatests.test_xmlrpc.test_user_plugin import (
+    get_user_result, get_user_dn, get_group_dn)
 from ipatests.test_xmlrpc import objectclasses
 from ipatests.test_xmlrpc.testcert import get_testcert
 import base64
@@ -139,6 +141,15 @@ ipv6_fromip_ptr_dn = DN(('idnsname', ipv6_fromip_ptr), revipv6zone_dn)
 sshpubkey = u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDGAX3xAeLeaJggwTqMjxNwa6XHBUAikXPGMzEpVrlLDCZtv00djsFTBi38PkgxBJVkgRWMrcBsr/35lq7P6w8KGIwA8GI48Z0qBS2NBMJ2u9WQ2hjLN6GdMlo77O0uJY3251p12pCVIS/bHRSq8kHO2No8g7KA9fGGcagPfQH+ee3t7HUkpbQkFTmbPPN++r3V8oVUk5LxbryB3UIIVzNmcSIn3JrXynlvui4MixvrtX6zx+O/bBo68o8/eZD26QrahVbA09fivrn/4h3TM019Eu/c2jOdckfU3cHUV/3Tno5d6JicibyaoDDK7S/yjdn5jhaz8MSEayQvFkZkiF0L public key test'
 sshpubkeyfp = u'13:67:6B:BF:4E:A2:05:8E:AE:25:8B:A1:31:DE:6F:1B public key test (ssh-rsa)'
 
+user1 = u'tuser1'
+user2 = u'tuser2'
+group1 = u'group1'
+group1_dn = get_group_dn(group1)
+group2 = u'group2'
+group2_dn = get_group_dn(group2)
+read_keys_op = u'retrieve-keytab'
+write_keys_op = u'create-keytab'
+
 class test_host(Declarative):
 
     cleanup_commands = [
@@ -1402,3 +1413,356 @@ class test_host_dns(Declarative):
             ),
         ),
     ]
+
+
+class test_host_allowed_to(Declarative):
+    cleanup_commands = [
+        ('user_del', [user1], {}),
+        ('user_del', [user2], {}),
+        ('group_del', [group1], {}),
+        ('group_del', [group2], {}),
+        ('host_del', [fqdn1], {}),
+    ]
+
+    tests = [
+        # prepare entries
+        dict(
+            desc='Create %r' % user1,
+            command=(
+                'user_add', [], dict(givenname=u'Test', sn=u'User1')
+            ),
+            expected=dict(
+                value=user1,
+                summary=u'Added user "%s"' % user1,
+                result=get_user_result(user1, u'Test', u'User1', 'add'),
+            ),
+        ),
+        dict(
+            desc='Create %r' % user2,
+            command=(
+                'user_add', [], dict(givenname=u'Test', sn=u'User2')
+            ),
+            expected=dict(
+                value=user2,
+                summary=u'Added user "%s"' % user2,
+                result=get_user_result(user2, u'Test', u'User2', 'add'),
+            ),
+        ),
+        dict(
+            desc='Create group: %r' % group1,
+            command=(
+                'group_add', [group1], dict()
+            ),
+            expected=dict(
+                value=group1,
+                summary=u'Added group "%s"' % group1,
+                result=dict(
+                    cn=[group1],
+                    objectclass=objectclasses.group + [u'posixgroup'],
+                    ipauniqueid=[fuzzy_uuid],
+                    gidnumber=[fuzzy_digits],
+                    dn=group1_dn
+                ),
+            ),
+        ),
+        dict(
+            desc='Create group: %r' % group2,
+            command=(
+                'group_add', [group2], dict()
+            ),
+            expected=dict(
+                value=group2,
+                summary=u'Added group "%s"' % group2,
+                result=dict(
+                    cn=[group2],
+                    objectclass=objectclasses.group + [u'posixgroup'],
+                    ipauniqueid=[fuzzy_uuid],
+                    gidnumber=[fuzzy_digits],
+                    dn=group2_dn
+                ),
+            ),
+        ),
+        dict(
+            desc='Create %r' % fqdn1,
+            command=(
+                'host_add', [fqdn1],
+                dict(
+                    force=True,
+                ),
+            ),
+            expected=dict(
+                value=fqdn1,
+                summary=u'Added host "%s"' % fqdn1,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    objectclass=objectclasses.host,
+                    ipauniqueid=[fuzzy_uuid],
+                    managedby_host=[fqdn1],
+                    has_keytab=False,
+                    has_password=False,
+                ),
+            ),
+        ),
+
+        # verify
+        dict(
+            desc='Allow %r to retrieve a keytab of %r' % (user1, fqdn1),
+            command=('host_allow_operation', [fqdn1, read_keys_op],
+                     dict(user=user1)),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=1,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Duplicate add: user %r' % (user1),
+            command=('host_allow_operation', [fqdn1, read_keys_op],
+                     dict(user=user1)),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[[user1, u'This entry is already a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Allow %r, %r to retrieve a keytab of %r' % (
+                group1, group2, fqdn1),
+            command=('host_allow_operation', [fqdn1, read_keys_op],
+                     dict(group=[group1, group2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=2,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1, group2],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Invalid disallowance of retrieve keytab %r' % (user2),
+            command=('host_disallow_operation', [fqdn1, read_keys_op],
+                     dict(user=[user2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[[user2, u'This entry is not a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1, group2],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Disallow %r to retrieve a keytab' % (group2),
+            command=('host_disallow_operation', [fqdn1, read_keys_op],
+                     dict(group=[group2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=1,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Allow %r, %r to create a keytab of %r' % (
+                group1, user1, fqdn1),
+            command=('host_allow_operation', [fqdn1, write_keys_op],
+                     dict(group=[group1, group2], user=[user1])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=3,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1, group2],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Duplicate add: %r, %r' % (user1, group1),
+            command=('host_allow_operation', [fqdn1, write_keys_op],
+                     dict(group=[group1], user=[user1])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[[group1, u'This entry is already a member']],
+                        user=[[user1, u'This entry is already a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1, group2],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Invalid disallowance of create keytab %r' % (user2),
+            command=('host_disallow_operation', [fqdn1, write_keys_op],
+                     dict(user=[user2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[],
+                        user=[[user2, u'This entry is not a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1, group2],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Disallow %r to create a keytab ' % (group2),
+            command=('host_disallow_operation', [fqdn1, write_keys_op],
+                     dict(group=[group2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=1,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Presence of ipaallowedtoperform in show output',
+            command=('host_show', [fqdn1], {}),
+            expected=dict(
+                value=fqdn1,
+                summary=None,
+                result=dict(
+                    dn=dn1,
+                    fqdn=[fqdn1],
+                    has_keytab=False,
+                    has_password=False,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Presence of ipaallowedtoperform in mod output',
+            command=(
+                'host_mod', [fqdn1],
+                dict(description=u"desc")),
+            expected=dict(
+                value=fqdn1,
+                summary=u'Modified host "%s"' % fqdn1,
+                result=dict(
+                    description=[u"desc"],
+                    fqdn=[fqdn1],
+                    has_keytab=False,
+                    has_password=False,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+    ]
diff --git a/ipatests/test_xmlrpc/test_service_plugin.py b/ipatests/test_xmlrpc/test_service_plugin.py
index c29c94d8619261197e0d445909ec8be4f78cd691..899c8894512f5edf595aa9a11152b11a364e2261 100644
--- a/ipatests/test_xmlrpc/test_service_plugin.py
+++ b/ipatests/test_xmlrpc/test_service_plugin.py
@@ -27,6 +27,8 @@ from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_digits, fuzzy_date, fuzzy_iss
 from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_hex
 from ipatests.test_xmlrpc import objectclasses
 from ipatests.test_xmlrpc.testcert import get_testcert
+from ipatests.test_xmlrpc.test_user_plugin import (
+    get_user_result, get_user_dn, get_group_dn)
 import base64
 from ipapython.dn import DN
 
@@ -46,6 +48,14 @@ role1_dn = DN(('cn', role1), api.env.container_rolegroup, api.env.basedn)
 
 badservercert = 'MIICbzCCAdigAwIBAgICA/4wDQYJKoZIhvcNAQEFBQAwKTEnMCUGA1UEAxMeSVBBIFRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEwMDgwOTE1MDIyN1oXDTIwMDgwOTE1MDIyN1owKTEMMAoGA1UEChMDSVBBMRkwFwYDVQQDExBwdW1hLmdyZXlvYWsuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwYbfEOQPgGenPn9vt1JFKvWm/Je3y2tawGWA3LXDuqfFJyYtZ8ib3TcBUOnLk9WK5g2qCwHaNlei7bj8ggIfr5hegAVe10cun+wYErjnYo7hsHYd+57VZezeipWrXu+7NoNd4+c4A5lk4A/xJay9j3bYx2oOM8BEox4xWYoWge1ljPrc5JK46f0X7AGW4F2VhnKPnf8rwSuzI1U8VGjutyM9TWNy3m9KMWeScjyG/ggIpOjUDMV7HkJL0Di61lznR9jXubpiEC7gWGbTp84eGl/Nn9bgK1AwHfJ2lHwfoY4uiL7ge1gyP6EvuUlHoBzdb7pekiX28iePjW3iEG9IawIDAQABoyIwIDARBglghkgBhvhCAQEEBAMCBkAwCwYDVR0PBAQDAgUgMA0GCSqGSIb3DQEBBQUAA4GBACRESLemRV9BPxfEgbALuxH5oE8jQm8WZ3pm2pALbpDlAd9wQc3yVf6RtkfVthyDnM18bg7IhxKpd77/p3H8eCnS8w5MLVRda6ktUC6tGhFTS4QKAf0WyDGTcIgkXbeDw0OPAoNHivoXbIXIIRxlw/XgaSaMzJQDBG8iROsN4kCv'
 
+user1 = u'tuser1'
+user2 = u'tuser2'
+group1 = u'group1'
+group1_dn = get_group_dn(group1)
+group2 = u'group2'
+group2_dn = get_group_dn(group2)
+read_keys_op = u'retrieve-keytab'
+write_keys_op = u'create-keytab'
 
 class test_service(Declarative):
 
@@ -752,3 +762,363 @@ class test_service_in_role(Declarative):
             ),
         ),
     ]
+
+
+class test_service_allowed_to(Declarative):
+    cleanup_commands = [
+        ('user_del', [user1], {}),
+        ('user_del', [user2], {}),
+        ('group_del', [group1], {}),
+        ('group_del', [group2], {}),
+        ('host_del', [fqdn1], {}),
+        ('service_del', [service1], {}),
+    ]
+
+    tests = [
+        # prepare entries
+        dict(
+            desc='Create %r' % user1,
+            command=(
+                'user_add', [], dict(givenname=u'Test', sn=u'User1')
+            ),
+            expected=dict(
+                value=user1,
+                summary=u'Added user "%s"' % user1,
+                result=get_user_result(user1, u'Test', u'User1', 'add'),
+            ),
+        ),
+        dict(
+            desc='Create %r' % user2,
+            command=(
+                'user_add', [], dict(givenname=u'Test', sn=u'User2')
+            ),
+            expected=dict(
+                value=user2,
+                summary=u'Added user "%s"' % user2,
+                result=get_user_result(user2, u'Test', u'User2', 'add'),
+            ),
+        ),
+        dict(
+            desc='Create group: %r' % group1,
+            command=(
+                'group_add', [group1], dict()
+            ),
+            expected=dict(
+                value=group1,
+                summary=u'Added group "%s"' % group1,
+                result=dict(
+                    cn=[group1],
+                    objectclass=objectclasses.group + [u'posixgroup'],
+                    ipauniqueid=[fuzzy_uuid],
+                    gidnumber=[fuzzy_digits],
+                    dn=group1_dn
+                ),
+            ),
+        ),
+        dict(
+            desc='Create group: %r' % group2,
+            command=(
+                'group_add', [group2], dict()
+            ),
+            expected=dict(
+                value=group2,
+                summary=u'Added group "%s"' % group2,
+                result=dict(
+                    cn=[group2],
+                    objectclass=objectclasses.group + [u'posixgroup'],
+                    ipauniqueid=[fuzzy_uuid],
+                    gidnumber=[fuzzy_digits],
+                    dn=group2_dn
+                ),
+            ),
+        ),
+        dict(
+            desc='Create %r' % fqdn1,
+            command=(
+                'host_add', [fqdn1],
+                dict(
+                    description=u'Test host 1',
+                    l=u'Undisclosed location 1',
+                    force=True,
+                ),
+            ),
+            expected=dict(
+                value=fqdn1,
+                summary=u'Added host "%s"' % fqdn1,
+                result=dict(
+                    dn=host1dn,
+                    fqdn=[fqdn1],
+                    description=[u'Test host 1'],
+                    l=[u'Undisclosed location 1'],
+                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
+                    objectclass=objectclasses.host,
+                    ipauniqueid=[fuzzy_uuid],
+                    managedby_host=[u'%s' % fqdn1],
+                    has_keytab=False,
+                    has_password=False,
+                ),
+            ),
+        ),
+        dict(
+            desc='Create %r' % service1,
+            command=('service_add', [service1_no_realm], dict(force=True)),
+            expected=dict(
+                value=service1,
+                summary=u'Added service "%s"' % service1,
+                result=dict(
+                    dn=service1dn,
+                    krbprincipalname=[service1],
+                    objectclass=objectclasses.service,
+                    ipauniqueid=[fuzzy_uuid],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        # verify
+        dict(
+            desc='Allow %r to retrieve a keytab of %r' % (user1, service1),
+            command=('service_allow_operation', [service1, read_keys_op],
+                     dict(user=user1)),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=1,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Duplicate add: user %r' % (user1),
+            command=('service_allow_operation', [service1, read_keys_op],
+                     dict(user=user1)),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[[user1, u'This entry is already a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Allow %r, %r to retrieve a keytab of %r' % (
+                group1, group2, service1),
+            command=('service_allow_operation', [service1, read_keys_op],
+                     dict(group=[group1, group2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=2,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1, group2],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Invalid disallowance of retrieve keytab: %r' % (user2),
+            command=('service_disallow_operation', [service1, read_keys_op],
+                     dict(user=[user2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[[user2, u'This entry is not a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1, group2],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Disallow %r to retrieve a keytab' % (group2),
+            command=('service_disallow_operation', [service1, read_keys_op],
+                     dict(group=[group2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_read_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=1,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Allow %r, %r to create a keytab of %r' % (
+                group1, user1, service1),
+            command=('service_allow_operation', [service1, write_keys_op],
+                     dict(group=[group1, group2], user=[user1])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=3,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1, group2],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Duplicate add: %r, %r' % (user1, group1),
+            command=('service_allow_operation', [service1, write_keys_op],
+                     dict(group=[group1], user=[user1])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[[group1, u'This entry is already a member']],
+                        user=[[user1, u'This entry is already a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1, group2],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Invalid disallowance of create keytab: %r' % (user2),
+            command=('service_disallow_operation', [service1, write_keys_op],
+                     dict(user=[user2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[],
+                        user=[[user2, u'This entry is not a member']],
+                    ),
+                ),
+                completed=0,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1, group2],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Disallow %r to create a keytab' % (group2),
+            command=('service_disallow_operation', [service1, write_keys_op],
+                     dict(group=[group2])),
+            expected=dict(
+                failed=dict(
+                    ipaallowedtoperform_write_keys=dict(
+                        group=[],
+                        user=[],
+                    ),
+                ),
+                completed=1,
+                result=dict(
+                    dn=service1dn,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Presence of ipaallowedtoperform in show output',
+            command=('service_show', [service1_no_realm], {}),
+            expected=dict(
+                value=service1,
+                summary=None,
+                result=dict(
+                    dn=service1dn,
+                    has_keytab=False,
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1],
+                    krbprincipalname=[service1],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+
+        dict(
+            desc='Presence of ipaallowedtoperform in mod output',
+            command=(
+                'service_mod', [service1_no_realm],
+                dict(ipakrbokasdelegate=True)),
+            expected=dict(
+                value=service1,
+                summary=u'Modified service "%s"' % service1,
+                result=dict(
+                    ipaallowedtoperform_read_keys_user=[user1],
+                    ipaallowedtoperform_read_keys_group=[group1],
+                    ipaallowedtoperform_write_keys_user=[user1],
+                    ipaallowedtoperform_write_keys_group=[group1],
+                    ipakrbokasdelegate=True,
+                    krbprincipalname=[service1],
+                    krbticketflags=[u'1048704'],
+                    managedby_host=[fqdn1],
+                ),
+            ),
+        ),
+    ]
-- 
1.9.3

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to