diff options
-rw-r--r-- | API.txt | 25 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | install/share/60ipadns.ldif | 1 | ||||
-rw-r--r-- | install/share/dns.ldif | 5 | ||||
-rw-r--r-- | install/updates/10-bind-schema.update | 7 | ||||
-rw-r--r-- | install/updates/40-dns.update | 12 | ||||
-rw-r--r-- | ipalib/plugins/dns.py | 79 | ||||
-rw-r--r-- | ipalib/plugins/permission.py | 43 | ||||
-rw-r--r-- | ipaserver/install/plugins/dns.py | 2 | ||||
-rw-r--r-- | tests/test_xmlrpc/objectclasses.py | 11 | ||||
-rw-r--r-- | tests/test_xmlrpc/test_dns_plugin.py | 115 |
11 files changed, 278 insertions, 24 deletions
@@ -1031,6 +1031,12 @@ option: Str('version?', exclude='webui') output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', <type 'unicode'>, None) +command: dnszone_add_permission +args: 1,0,3 +arg: Str('idnsname', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('result', <type 'bool'>, None) +output: Output('value', <type 'unicode'>, None) command: dnszone_del args: 1,1,3 arg: Str('idnsname', attribute=True, cli_name='name', multivalue=True, primary_key=True, query=True, required=True) @@ -1113,6 +1119,12 @@ option: Str('version?', exclude='webui') output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('value', <type 'unicode'>, None) +command: dnszone_remove_permission +args: 1,0,3 +arg: Str('idnsname', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('result', <type 'bool'>, None) +output: Output('value', <type 'unicode'>, None) command: dnszone_show args: 1,4,3 arg: Str('idnsname', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True) @@ -2075,10 +2087,21 @@ option: Str('privilege*', alwaysask=True, cli_name='privileges', csv=True) output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('failed', <type 'dict'>, None) output: Output('completed', <type 'int'>, None) +command: permission_add_noaci +args: 1,4,3 +arg: Str('cn', cli_name='name', multivalue=False, pattern=None, primary_key=True, required=True) +option: StrEnum('permissiontype?', values=(u'SYSTEM',)) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('version?', exclude='webui') +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('value', <type 'unicode'>, None) command: permission_del -args: 1,1,3 +args: 1,2,3 arg: Str('cn', attribute=True, cli_name='name', multivalue=True, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, query=True, required=True) option: Flag('continue', autofill=True, cli_name='continue', default=False) +option: Flag('force', autofill=True, default=False) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('result', <type 'dict'>, None) output: Output('value', <type 'unicode'>, None) @@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000 # # ######################################################## IPA_API_VERSION_MAJOR=2 -IPA_API_VERSION_MINOR=38 +IPA_API_VERSION_MINOR=39 diff --git a/install/share/60ipadns.ldif b/install/share/60ipadns.ldif index 6f88d05e2..9697227fb 100644 --- a/install/share/60ipadns.ldif +++ b/install/share/60ipadns.ldif @@ -52,3 +52,4 @@ attributeTypes: ( 2.16.840.1.113730.3.8.5.17 NAME 'idnsPersistentSearch' DESC 'a objectClasses: ( 2.16.840.1.113730.3.8.6.0 NAME 'idnsRecord' DESC 'dns Record, usually a host' SUP top STRUCTURAL MUST idnsName MAY ( cn $ idnsAllowDynUpdate $ DNSTTL $ DNSClass $ ARecord $ AAAARecord $ A6Record $ NSRecord $ CNAMERecord $ PTRRecord $ SRVRecord $ TXTRecord $ MXRecord $ MDRecord $ HINFORecord $ MINFORecord $ AFSDBRecord $ SIGRecord $ KEYRecord $ LOCRecord $ NXTRecord $ NAPTRRecord $ KXRecord $ CERTRecord $ DNAMERecord $ DSRecord $ SSHFPRecord $ RRSIGRecord $ NSECRecord ) objectClasses: ( 2.16.840.1.113730.3.8.6.1 NAME 'idnsZone' DESC 'Zone class' SUP idnsRecord STRUCTURAL MUST ( idnsName $ idnsZoneActive $ idnsSOAmName $ idnsSOArName $ idnsSOAserial $ idnsSOArefresh $ idnsSOAretry $ idnsSOAexpire $ idnsSOAminimum ) MAY ( idnsUpdatePolicy $ idnsAllowQuery $ idnsAllowTransfer $ idnsAllowSyncPTR $ idnsForwardPolicy $ idnsForwarders ) ) objectClasses: ( 2.16.840.1.113730.3.8.6.2 NAME 'idnsConfigObject' DESC 'DNS global config options' STRUCTURAL MAY ( idnsForwardPolicy $ idnsForwarders $ idnsAllowSyncPTR $ idnsZoneRefresh $ idnsPersistentSearch ) ) +objectClasses: ( 2.16.840.1.113730.3.8.12.18 NAME 'ipaDNSZone' SUP top AUXILIARY MUST idnsName MAY managedBy X-ORIGIN 'IPA v3' ) diff --git a/install/share/dns.ldif b/install/share/dns.ldif index 81ba21009..d27f105b7 100644 --- a/install/share/dns.ldif +++ b/install/share/dns.ldif @@ -4,7 +4,10 @@ objectClass: idnsConfigObject objectClass: nsContainer objectClass: top cn: dns -aci: (targetattr = "*")(version 3.0; acl "No access to DNS tree without a permission"; deny (read,search,compare) (groupdn != "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX") and (groupdn != "ldap:///cn=Read DNS Entries,cn=permissions,cn=pbac,$SUFFIX");) +aci: (targetattr = "*")(version 3.0; acl "Allow read access"; allow (read,search,compare) groupdn = "ldap:///cn=Read DNS Entries,cn=permissions,cn=pbac,$SUFFIX" or userattr = "parent[0,1].managedby#GROUPDN";) +aci: (target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Add DNS entries in a zone";allow (add) userattr = "parent[1].managedby#GROUPDN";) +aci: (target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Remove DNS entries from a zone";allow (delete) userattr = "parent[1].managedby#GROUPDN";) +aci: (targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy || idnsallowquery || idnsallowtransfer || idnsallowsyncptr || idnsforwardpolicy || idnsforwarders")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Update DNS entries in a zone";allow (write) userattr = "parent[0,1].managedby#GROUPDN";) dn: $SUFFIX changetype: modify diff --git a/install/updates/10-bind-schema.update b/install/updates/10-bind-schema.update index c3398c1f2..0edbad204 100644 --- a/install/updates/10-bind-schema.update +++ b/install/updates/10-bind-schema.update @@ -68,4 +68,11 @@ add:objectClasses: MAY ( idnsForwardPolicy $$ idnsForwarders $$ idnsAllowSyncPTR $$ idnsZoneRefresh $$ idnsPersistentSearch ) ) +add:objectClasses: + ( 2.16.840.1.113730.3.8.12.18 + NAME 'ipaDNSZone' + SUP top AUXILIARY + MUST idnsName + MAY managedBy + X-ORIGIN 'IPA v3' ) replace:objectClasses:( 2.16.840.1.113730.3.8.6.1 NAME 'idnsZone' DESC 'Zone class' SUP idnsRecord STRUCTURAL MUST ( idnsZoneActive $$ idnsSOAmName $$ idnsSOArName $$ idnsSOAserial $$ idnsSOArefresh $$ idnsSOAretry $$ idnsSOAexpire $$ idnsSOAminimum ) MAY idnsUpdatePolicy )::( 2.16.840.1.113730.3.8.6.1 NAME 'idnsZone' DESC 'Zone class' SUP idnsRecord STRUCTURAL MUST ( idnsName $$ idnsZoneActive $$ idnsSOAmName $$ idnsSOArName $$ idnsSOAserial $$ idnsSOArefresh $$ idnsSOAretry $$ idnsSOAexpire $$ idnsSOAminimum ) MAY ( idnsUpdatePolicy $$ idnsAllowQuery $$ idnsAllowTransfer $$ idnsAllowSyncPTR $$ idnsForwardPolicy $$ idnsForwarders ) ) diff --git a/install/updates/40-dns.update b/install/updates/40-dns.update index 3dacb248f..3478a03ca 100644 --- a/install/updates/40-dns.update +++ b/install/updates/40-dns.update @@ -26,10 +26,18 @@ add: basedn: 'cn=privileges,cn=pbac,$SUFFIX' add: filter: (objectclass=*) add: ttl: 10 -# add idnsConfigObject if it is not there already +# update DNS container dn: cn=dns, $SUFFIX addifexist: objectClass: idnsConfigObject +addifexist: aci:'(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Add DNS entries in a zone";allow (add) userattr = "parent[1].managedby#GROUPDN";)' +addifexist: aci:'(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Remove DNS entries from a zone";allow (delete) userattr = "parent[1].managedby#GROUPDN";)' +addifexist: aci:'(targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy || idnsallowquery || idnsallowtransfer || idnsallowsyncptr || idnsforwardpolicy || idnsforwarders")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "Update DNS entries in a zone";allow (write) userattr = "parent[0,1].managedby#GROUPDN";)' # update DNS acis with new idnsRecord attributes dn: $SUFFIX -replace:aci:'(targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "permission:update dns entries";allow (write) groupdn = "ldap:///cn=update dns entries,cn=permissions,cn=pbac,$SUFFIX";)::(targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy || idnsallowquery || idnsallowtransfer || idnsallowsyncptr || idnsforwardpolicy || idnsforwarders")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "permission:update dns entries";allow (write) groupdn = "ldap:///cn=update dns entries,cn=permissions,cn=pbac,$SUFFIX";)' +replace:aci:'(targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "permission:update dns entries";allow (write) groupdn = "ldap:///cn=update dns entries,cn=permissions,cn=pbac,$SUFFIX";)::(targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy || idnsallowquery || idnsallowtransfer || idnsallowsyncptr || idnsforwardpolicy || idnsforwarders || managedby")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "permission:update dns entries";allow (write) groupdn = "ldap:///cn=update dns entries,cn=permissions,cn=pbac,$SUFFIX";)' +replace:aci:'(targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy || idnsallowquery || idnsallowtransfer || idnsallowsyncptr || idnsforwardpolicy || idnsforwarders")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "permission:update dns entries";allow (write) groupdn = "ldap:///cn=update dns entries,cn=permissions,cn=pbac,$SUFFIX";)::(targetattr = "idnsname || cn || idnsallowdynupdate || dnsttl || dnsclass || arecord || aaaarecord || a6record || nsrecord || cnamerecord || ptrrecord || srvrecord || txtrecord || mxrecord || mdrecord || hinforecord || minforecord || afsdbrecord || sigrecord || keyrecord || locrecord || nxtrecord || naptrrecord || kxrecord || certrecord || dnamerecord || dsrecord || sshfprecord || rrsigrecord || nsecrecord || idnsname || idnszoneactive || idnssoamname || idnssoarname || idnssoaserial || idnssoarefresh || idnssoaretry || idnssoaexpire || idnssoaminimum || idnsupdatepolicy || idnsallowquery || idnsallowtransfer || idnsallowsyncptr || idnsforwardpolicy || idnsforwarders || managedby")(target = "ldap:///idnsname=*,cn=dns,$SUFFIX")(version 3.0;acl "permission:update dns entries";allow (write) groupdn = "ldap:///cn=update dns entries,cn=permissions,cn=pbac,$SUFFIX";)' + +# replace DNS tree deny rule with managedBy enhanced allow rule +dn: cn=dns, $SUFFIX +replace:aci:'(targetattr = "*")(version 3.0; acl "No access to DNS tree without a permission"; deny (read,search,compare) (groupdn != "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX") and (groupdn != "ldap:///cn=Read DNS Entries,cn=permissions,cn=pbac,$SUFFIX");)::(targetattr = "*")(version 3.0; acl "Allow read access"; allow (read,search,compare) groupdn = "ldap:///cn=Read DNS Entries,cn=permissions,cn=pbac,$SUFFIX" or userattr = "parent[0,1].managedby#GROUPDN";)' diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py index 0f1014cae..c2bf13a2f 100644 --- a/ipalib/plugins/dns.py +++ b/ipalib/plugins/dns.py @@ -75,6 +75,9 @@ EXAMPLES: ipa dnszone-add example.com --name-server=nameserver.example.com \\ --admin-email=admin@example.com + Add system permission that can be used for per-zone privilege delegation: + ipa dnszone-add-permission example.com + Modify the zone to allow dynamic updates for hosts own records in realm EXAMPLE.COM: ipa dnszone-mod example.com --dynamic-update=TRUE @@ -1528,6 +1531,7 @@ class dnszone(LDAPObject): object_name = _('DNS zone') object_name_plural = _('DNS zones') object_class = ['top', 'idnsrecord', 'idnszone'] + possible_objectclasses = ['ipadnszone'] default_attributes = [ 'idnsname', 'idnszoneactive', 'idnssoamname', 'idnssoarname', 'idnssoaserial', 'idnssoarefresh', 'idnssoaretry', 'idnssoaexpire', @@ -1696,6 +1700,9 @@ class dnszone(LDAPObject): return dn + def permission_name(self, zone): + return u"Manage DNS zone %s" % zone + api.register(dnszone) @@ -1752,6 +1759,14 @@ api.register(dnszone_add) class dnszone_del(LDAPDelete): __doc__ = _('Delete DNS zone (SOA record).') + def post_callback(self, ldap, dn, *keys, **options): + try: + api.Command['permission_del'](self.obj.permission_name(keys[-1]), + force=True) + except errors.NotFound: + pass + return True + api.register(dnszone_del) @@ -1851,6 +1866,70 @@ class dnszone_enable(LDAPQuery): api.register(dnszone_enable) +class dnszone_add_permission(LDAPQuery): + __doc__ = _('Add a permission for per-zone access delegation.') + + has_output = output.standard_value + msg_summary = _('Added system permission "%(value)s"') + + def execute(self, *keys, **options): + ldap = self.obj.backend + dn = self.obj.get_dn(*keys, **options) + + try: + (dn_, entry_attrs) = ldap.get_entry(dn, ['objectclass']) + except errors.NotFound: + self.obj.handle_not_found(*keys) + + permission_name = self.obj.permission_name(keys[-1]) + permission = api.Command['permission_add_noaci'](permission_name, + permissiontype=u'SYSTEM' + )['result'] + + update = {} + dnszone_ocs = entry_attrs.get('objectclass') + if dnszone_ocs: + dnszone_ocs.append('ipadnszone') + update['objectclass'] = list(set(dnszone_ocs)) + + update['managedby'] = [permission['dn']] + ldap.update_entry(dn, update) + + return dict( + result=True, + value=permission_name, + ) + +api.register(dnszone_add_permission) + +class dnszone_remove_permission(LDAPQuery): + __doc__ = _('Remove a permission for per-zone access delegation.') + + has_output = output.standard_value + msg_summary = _('Removed system permission "%(value)s"') + + def execute(self, *keys, **options): + ldap = self.obj.backend + dn = self.obj.get_dn(*keys, **options) + + try: + ldap.update_entry(dn, {'managedby': None}) + except errors.NotFound: + self.obj.handle_not_found(*keys) + except errors.EmptyModlist: + # managedBy attribute is clean, lets make sure there is also no + # dangling DNS zone permission + pass + + permission_name = self.obj.permission_name(keys[-1]) + api.Command['permission_del'](permission_name, force=True) + + return dict( + result=True, + value=permission_name, + ) + +api.register(dnszone_remove_permission) class dnsrecord(LDAPObject): """ diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py index 05d19ad8d..ec3d78d1b 100644 --- a/ipalib/plugins/permission.py +++ b/ipalib/plugins/permission.py @@ -246,14 +246,55 @@ class permission_add(LDAPCreate): api.register(permission_add) +class permission_add_noaci(LDAPCreate): + __doc__ = _('Add a system permission without an ACI') + + msg_summary = _('Added permission "%(value)s"') + has_output_params = LDAPCreate.has_output_params + output_params + NO_CLI = True + + takes_options = ( + StrEnum('permissiontype?', + label=_('Permission type'), + values=(u'SYSTEM',), + ), + ) + + def get_args(self): + # do not validate system permission names + yield self.obj.primary_key.clone(pattern=None, pattern_errmsg=None) + + def get_options(self): + for option in super(permission_add_noaci, self).get_options(): + # filter out ACI options + if option.name in self.obj.aci_attributes: + continue + yield option + + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + permission_type = options.get('permissiontype') + if permission_type: + entry_attrs['ipapermissiontype'] = [ permission_type ] + return dn + +api.register(permission_add_noaci) + class permission_del(LDAPDelete): __doc__ = _('Delete a permission.') msg_summary = _('Deleted permission "%(value)s"') + takes_options = LDAPDelete.takes_options + ( + Flag('force', + label=_('Force'), + flags=['no_option', 'no_output'], + doc=_('force delete of SYSTEM permissions'), + ), + ) + def pre_callback(self, ldap, dn, *keys, **options): - if not self.obj.check_system(ldap, dn, *keys): + if not options.get('force') and not self.obj.check_system(ldap, dn, *keys): raise errors.ACIError(info='A SYSTEM permission may not be removed') # remove permission even when the underlying ACI is missing try: diff --git a/ipaserver/install/plugins/dns.py b/ipaserver/install/plugins/dns.py index 29b71dd9d..e11c331a4 100644 --- a/ipaserver/install/plugins/dns.py +++ b/ipaserver/install/plugins/dns.py @@ -119,7 +119,7 @@ class update_dns_permissions(PostUpdate): _write_dns_aci_entry = ['add:aci:\'(targetattr = "idnsforwardpolicy || idnsforwarders || idnsallowsyncptr || idnszonerefresh || idnspersistentsearch")(target = "ldap:///cn=dns,%(realm)s")(version 3.0;acl "permission:Write DNS Configuration";allow (write) groupdn = "ldap:///cn=Write DNS Configuration,cn=permissions,cn=pbac,%(realm)s";)\'' % dict(realm=api.env.basedn)] _read_dns_aci_dn = DN(api.env.container_dns, api.env.basedn) - _read_dns_aci_entry = ['add:aci:\'(targetattr = "*")(version 3.0; acl "No access to DNS tree without a permission"; deny (read,search,compare) (groupdn != "ldap:///cn=admins,cn=groups,cn=accounts,%(realm)s") and (groupdn != "ldap:///cn=Read DNS Entries,cn=permissions,cn=pbac,%(realm)s");)\'' % dict(realm=api.env.basedn) ] + _read_dns_aci_entry = ['add:aci:\'(targetattr = "*")(version 3.0; acl "Allow read access"; allow (read,search,compare) groupdn = "ldap:///cn=Read DNS Entries,cn=permissions,cn=pbac,%(realm)s" or userattr = "parent[0,1].managedby#GROUPDN";)\'' % dict(realm=api.env.basedn) ] def execute(self, **options): ldap = self.obj.backend diff --git a/tests/test_xmlrpc/objectclasses.py b/tests/test_xmlrpc/objectclasses.py index a036b34de..4bb2b3510 100644 --- a/tests/test_xmlrpc/objectclasses.py +++ b/tests/test_xmlrpc/objectclasses.py @@ -141,3 +141,14 @@ hbacrule = [ u'ipaassociation', u'ipahbacrule', ] + +dnszone = [ + u'top', + u'idnsrecord', + u'idnszone', +] + +dnsrecord = [ + u'top', + u'idnsrecord', +] diff --git a/tests/test_xmlrpc/test_dns_plugin.py b/tests/test_xmlrpc/test_dns_plugin.py index ab1d4f0be..d121b2f0f 100644 --- a/tests/test_xmlrpc/test_dns_plugin.py +++ b/tests/test_xmlrpc/test_dns_plugin.py @@ -31,6 +31,9 @@ dnszone1_dn = DN(('idnsname',dnszone1),('cn','dns'),api.env.basedn) dnszone1_mname = u'ns1.%s.' % dnszone1 dnszone1_mname_dn = DN(('idnsname','ns1'), dnszone1_dn) dnszone1_rname = u'root.%s.' % dnszone1 +dnszone1_permission = u'Manage DNS zone %s' % dnszone1 +dnszone1_permission_dn = DN(('cn',dnszone1_permission), + api.env.container_permission,api.env.basedn) dnszone2 = u'dnszone2.test' dnszone2_dn = DN(('idnsname',dnszone2),('cn','dns'),api.env.basedn) dnszone2_mname = u'ns1.%s.' % dnszone2 @@ -76,7 +79,8 @@ class test_dns(Declarative): 'idnsforwardpolicy' : None, 'idnsallowsyncptr' : None, 'idnszonerefresh' : None, - }) + }), + ('permission_del', [dnszone1_permission], {'force': True}), ] tests = [ @@ -151,7 +155,7 @@ class test_dns(Declarative): % dict(realm=api.env.realm)], 'idnsallowtransfer': [u'none;'], 'idnsallowquery': [u'any;'], - 'objectclass': [u'top', u'idnsrecord', u'idnszone'], + 'objectclass': objectclasses.dnszone, }, }, ), @@ -212,7 +216,7 @@ class test_dns(Declarative): % dict(realm=api.env.realm)], 'idnsallowtransfer': [u'none;'], 'idnsallowquery': [u'any;'], - 'objectclass': [u'top', u'idnsrecord', u'idnszone'], + 'objectclass': objectclasses.dnszone, }, }, ), @@ -305,7 +309,7 @@ class test_dns(Declarative): % dict(realm=api.env.realm, zone=revdnszone1)], 'idnsallowtransfer': [u'none;'], 'idnsallowquery': [u'any;'], - 'objectclass': [u'top', u'idnsrecord', u'idnszone'], + 'objectclass': objectclasses.dnszone, }, }, ), @@ -503,7 +507,7 @@ class test_dns(Declarative): 'result': { 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'arecord': [u'127.0.0.1'], }, }, @@ -548,7 +552,7 @@ class test_dns(Declarative): 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], 'arecord': [u'127.0.0.1', u'10.10.0.1'], - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, }, }, ), @@ -626,7 +630,7 @@ class test_dns(Declarative): 'value': u'@', 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord', u'idnszone'], + 'objectclass': objectclasses.dnszone, 'dn': unicode(dnszone1_dn), 'idnsname': [u'@'], 'mxrecord': [u"0 %s" % dnszone1_mname], @@ -674,7 +678,7 @@ class test_dns(Declarative): 'value': u'_foo._tcp', 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'dn': unicode(DN(('idnsname', u'_foo._tcp'), dnszone1_dn)), 'idnsname': [u'_foo._tcp'], 'srvrecord': [u"0 100 1234 %s" % dnszone1_mname], @@ -731,7 +735,7 @@ class test_dns(Declarative): 'value': u'@', 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord', u'idnszone'], + 'objectclass': objectclasses.dnszone, 'dn': unicode(dnszone1_dn), 'idnsname': [u'@'], 'mxrecord': [u"0 %s" % dnszone1_mname], @@ -756,7 +760,7 @@ class test_dns(Declarative): 'value': dnsres1, 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], 'arecord': [u'10.10.0.1'], @@ -780,7 +784,7 @@ class test_dns(Declarative): 'value': dnsres1, 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], 'arecord': [u'10.10.0.1'], @@ -797,7 +801,7 @@ class test_dns(Declarative): 'value': dnsres1, 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], 'arecord': [u'10.10.0.1'], @@ -817,7 +821,7 @@ class test_dns(Declarative): 'value': dnsres1, 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], 'arecord': [u'10.10.0.1'], @@ -849,7 +853,7 @@ class test_dns(Declarative): 'value': dnsres1, 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], 'arecord': [u'10.10.0.1'], @@ -943,7 +947,7 @@ class test_dns(Declarative): % dict(realm=api.env.realm, zone=revdnszone1)], 'idnsallowtransfer': [u'none;'], 'idnsallowquery': [u'any;'], - 'objectclass': [u'top', u'idnsrecord', u'idnszone'], + 'objectclass': objectclasses.dnszone, }, }, ), @@ -964,7 +968,7 @@ class test_dns(Declarative): 'value': dnsrev1, 'summary': None, 'result': { - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'dn': unicode(dnsrev1_dn), 'idnsname': [dnsrev1], 'ptrrecord': [u'foo-1.example.com.'], @@ -1072,7 +1076,7 @@ class test_dns(Declarative): 'result': { 'dn': unicode(dnsres1_dn), 'idnsname': [dnsres1], - 'objectclass': [u'top', u'idnsrecord'], + 'objectclass': objectclasses.dnsrecord, 'arecord': [u'80.142.15.81'], }, }, @@ -1095,6 +1099,83 @@ class test_dns(Declarative): dict( + desc='Try to add per-zone permission for unknown zone', + command=('dnszone_add_permission', [u'does.not.exist'], {}), + expected=errors.NotFound(reason=u'does.not.exist: DNS zone not found') + ), + + + dict( + desc='Add per-zone permission for zone %r' % dnszone1, + command=( + 'dnszone_add_permission', [dnszone1], {} + ), + expected=dict( + result=True, + value=dnszone1_permission, + summary=u'Added system permission "%s"' % dnszone1_permission, + ), + ), + + + dict( + desc='Try to add duplicate per-zone permission for zone %r' % dnszone1, + command=( + 'dnszone_add_permission', [dnszone1], {} + ), + expected=errors.DuplicateEntry(message=u'permission with name ' + '"%s" already exists' % dnszone1_permission) + ), + + + dict( + desc='Make sure the permission was created %r' % dnszone1, + command=( + 'permission_show', [dnszone1_permission], {} + ), + expected=dict( + value=dnszone1_permission, + summary=None, + result={ + 'dn': lambda x: DN(x) == dnszone1_permission_dn, + 'cn': [dnszone1_permission], + 'ipapermissiontype': [u'SYSTEM'], + }, + ), + ), + + + dict( + desc='Try to remove per-zone permission for unknown zone', + command=('dnszone_remove_permission', [u'does.not.exist'], {}), + expected=errors.NotFound(reason=u'does.not.exist: DNS zone not found') + ), + + + dict( + desc='Remove per-zone permission for zone %r' % dnszone1, + command=( + 'dnszone_remove_permission', [dnszone1], {} + ), + expected=dict( + result=True, + value=dnszone1_permission, + summary=u'Removed system permission "%s"' % dnszone1_permission, + ), + ), + + + dict( + desc='Make sure the permission for zone %r was deleted' % dnszone1, + command=( + 'permission_show', [dnszone1_permission], {} + ), + expected=errors.NotFound(reason=u'%s: permission not found' + % dnszone1_permission) + ), + + + dict( desc='Delete zone %r' % dnszone1, command=('dnszone_del', [dnszone1], {}), expected={ |