summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt25
-rw-r--r--VERSION2
-rw-r--r--install/share/60ipadns.ldif1
-rw-r--r--install/share/dns.ldif5
-rw-r--r--install/updates/10-bind-schema.update7
-rw-r--r--install/updates/40-dns.update12
-rw-r--r--ipalib/plugins/dns.py79
-rw-r--r--ipalib/plugins/permission.py43
-rw-r--r--ipaserver/install/plugins/dns.py2
-rw-r--r--tests/test_xmlrpc/objectclasses.py11
-rw-r--r--tests/test_xmlrpc/test_dns_plugin.py115
11 files changed, 278 insertions, 24 deletions
diff --git a/API.txt b/API.txt
index a0c22143..5cdbc79b 100644
--- a/API.txt
+++ b/API.txt
@@ -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)
diff --git a/VERSION b/VERSION
index 77340e02..bc76959b 100644
--- a/VERSION
+++ b/VERSION
@@ -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 6f88d05e..9697227f 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 81ba2100..d27f105b 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 c3398c1f..0edbad20 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 3dacb248..3478a03c 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 0f1014ca..c2bf13a2 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 05d19ad8..ec3d78d1 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 29b71dd9..e11c331a 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 a036b34d..4bb2b351 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 ab1d4f0b..d121b2f0 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={