summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.zuul.yaml20
-rw-r--r--designate/api/middleware.py6
-rw-r--r--designate/api/v2/controllers/zones/tasks/exports.py9
-rw-r--r--designate/central/service.py691
-rw-r--r--designate/common/constants.py4
-rw-r--r--designate/common/policies/base.py70
-rw-r--r--designate/common/policies/context.py50
-rw-r--r--designate/common/policies/diagnostics.py51
-rw-r--r--designate/common/policies/quota.py2
-rw-r--r--designate/common/policies/recordset.py64
-rw-r--r--designate/common/policies/tsigkey.py3
-rw-r--r--designate/common/policies/zone.py25
-rw-r--r--designate/common/policies/zone_export.py24
-rw-r--r--designate/common/policies/zone_import.py2
-rw-r--r--designate/common/policies/zone_transfer_accept.py6
-rw-r--r--designate/common/policies/zone_transfer_request.py25
-rw-r--r--designate/context.py5
-rw-r--r--designate/exceptions.py13
-rw-r--r--designate/objects/adapters/api_v2/zone_transfer_request.py12
-rw-r--r--designate/objects/blacklist.py4
-rw-r--r--designate/objects/fields.py22
-rw-r--r--designate/policy.py15
-rw-r--r--designate/storage/impl_sqlalchemy/__init__.py9
-rw-r--r--designate/tests/__init__.py9
-rw-r--r--designate/tests/test_api/test_middleware.py3
-rw-r--r--designate/tests/test_api/test_v2/test_blacklists.py45
-rw-r--r--designate/tests/test_central/test_service.py21
-rw-r--r--designate/tests/unit/test_central/test_basic.py30
-rw-r--r--releasenotes/notes/Fix-to-address-denylist-invalid-patterns-not-being-checked-ec1f1316ccc6cb1d.yaml16
-rw-r--r--releasenotes/notes/Support-scoped-tokens-6b7d6052a258cd11.yaml4
30 files changed, 937 insertions, 323 deletions
diff --git a/.zuul.yaml b/.zuul.yaml
index 5fb90d98..09f694b6 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -54,6 +54,24 @@
pre-run: playbooks/enable-fips.yaml
- job:
+ name: designate-bind9-scoped-tokens
+ post-run: playbooks/designate-bind9/post.yaml
+ parent: designate-base
+ vars:
+ devstack_local_conf:
+ post-config:
+ $DESIGNATE_CONF:
+ oslo_policy:
+ enforce_scope: True
+ enforce_new_defaults: True
+ test-config:
+ "$TEMPEST_CONFIG":
+ enforce_scope:
+ designate: True
+ dns_feature_enabled:
+ enforce_new_defaults: True
+
+- job:
name: designate-pdns4
post-run: playbooks/designate-pdns4/post.yaml
parent: designate-base
@@ -135,6 +153,7 @@
- designate-bind9
- designate-bind9-centos8stream-fips:
voting: false
+ - designate-bind9-scoped-tokens
- designate-pdns4
- designate-grenade-pdns4
- designate-ipv6-only-pdns4
@@ -143,6 +162,7 @@
gate:
jobs:
- designate-bind9
+ - designate-bind9-scoped-tokens
- designate-pdns4
- designate-grenade-pdns4
- designate-ipv6-only-pdns4
diff --git a/designate/api/middleware.py b/designate/api/middleware.py
index 07fd46b1..2a451606 100644
--- a/designate/api/middleware.py
+++ b/designate/api/middleware.py
@@ -128,14 +128,13 @@ class KeystoneContextMiddleware(ContextMiddleware):
pass
tenant_id = headers.get('X-Tenant-ID')
- if tenant_id is None:
- return flask.Response(status=401)
catalog = None
if headers.get('X-Service-Catalog'):
catalog = jsonutils.loads(headers.get('X-Service-Catalog'))
roles = headers.get('X-Roles').split(',')
+ system_scope = headers.get('Openstack-System-Scope')
try:
self.make_context(
@@ -144,7 +143,8 @@ class KeystoneContextMiddleware(ContextMiddleware):
user_id=headers.get('X-User-ID'),
project_id=tenant_id,
roles=roles,
- service_catalog=catalog
+ service_catalog=catalog,
+ system_scope=system_scope
)
except exceptions.Forbidden:
return flask.Response(status=403)
diff --git a/designate/api/v2/controllers/zones/tasks/exports.py b/designate/api/v2/controllers/zones/tasks/exports.py
index 60a852b9..29d97c78 100644
--- a/designate/api/v2/controllers/zones/tasks/exports.py
+++ b/designate/api/v2/controllers/zones/tasks/exports.py
@@ -16,10 +16,11 @@
import pecan
from oslo_log import log as logging
+from designate.api.v2.controllers import rest
+from designate.common import constants
from designate import exceptions
from designate import policy
from designate import utils
-from designate.api.v2.controllers import rest
from designate.objects.adapters import DesignateAdapter
LOG = logging.getLogger(__name__)
@@ -31,7 +32,11 @@ class ZoneExportController(rest.RestController):
@utils.validate_uuid('export_id')
def get_all(self, export_id):
context = pecan.request.environ['context']
- target = {'tenant_id': context.project_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('zone_export', context, target)
export = self.central_api.get_zone_export(context, export_id)
diff --git a/designate/central/service.py b/designate/central/service.py
index 35cd0e3c..fe3fb04c 100644
--- a/designate/central/service.py
+++ b/designate/central/service.py
@@ -33,6 +33,7 @@ from oslo_config import cfg
import oslo_messaging as messaging
from oslo_log import log as logging
+from designate.common import constants
from designate import context as dcontext
from designate import coordination
from designate import exceptions
@@ -483,6 +484,12 @@ class Service(service.RPCService):
raise exceptions.InvalidTTL('TTL is below the minimum: %s'
% min_ttl)
+ def _is_valid_project_id(self, project_id):
+ if project_id is None:
+ raise exceptions.MissingProjectID(
+ "A project ID must be specified when not using a project "
+ "scoped token.")
+
def _increment_zone_serial(self, context, zone, set_delayed_notify=False):
"""Update the zone serial and the SOA record
Optionally set delayed_notify to have PM issue delayed notify
@@ -660,17 +667,30 @@ class Service(service.RPCService):
# Quota Methods
@rpc.expected_exceptions()
def get_quotas(self, context, tenant_id):
- target = {'tenant_id': tenant_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: tenant_id,
+ 'all_tenants': context.all_tenants}
+ else:
+ target = {'tenant_id': tenant_id}
policy.check('get_quotas', context, target)
- if tenant_id != context.project_id and not context.all_tenants:
+ # TODO(johnsom) Deprecated since Wallaby, remove with legacy default
+ # policies. System scoped admin doesn't have a project_id
+ if (tenant_id != context.project_id and not context.all_tenants and not
+ policy.enforce_new_defaults()):
raise exceptions.Forbidden()
return self.quota.get_quotas(context, tenant_id)
@rpc.expected_exceptions()
def get_quota(self, context, tenant_id, resource):
- target = {'tenant_id': tenant_id, 'resource': resource}
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: tenant_id,
+ 'resource': resource
+ }
+ else:
+ target = {'tenant_id': tenant_id, 'resource': resource}
policy.check('get_quota', context, target)
return self.quota.get_quota(context, tenant_id, resource)
@@ -678,21 +698,34 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
@transaction
def set_quota(self, context, tenant_id, resource, hard_limit):
- target = {
- 'tenant_id': tenant_id,
- 'resource': resource,
- 'hard_limit': hard_limit,
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: tenant_id,
+ 'resource': resource,
+ 'hard_limit': hard_limit,
+ }
+ else:
+ target = {
+ 'tenant_id': tenant_id,
+ 'resource': resource,
+ 'hard_limit': hard_limit,
+ }
policy.check('set_quota', context, target)
- if tenant_id != context.project_id and not context.all_tenants:
+ # TODO(johnsom) Deprecated since Wallaby, remove with legacy default
+ # policies. System scoped admin doesn't have a project_id
+ if (tenant_id != context.project_id and not context.all_tenants and not
+ policy.enforce_new_defaults()):
raise exceptions.Forbidden()
return self.quota.set_quota(context, tenant_id, resource, hard_limit)
@transaction
def reset_quotas(self, context, tenant_id):
- target = {'tenant_id': tenant_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: tenant_id}
+ else:
+ target = {'tenant_id': tenant_id}
policy.check('reset_quotas', context, target)
self.quota.reset_quotas(context, tenant_id)
@@ -808,9 +841,10 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def get_tenant(self, context, tenant_id):
- target = {
- 'tenant_id': tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: tenant_id}
+ else:
+ target = {'tenant_id': tenant_id}
policy.check('get_tenant', context, target)
@@ -857,13 +891,21 @@ class Service(service.RPCService):
# Default to creating in the current users tenant
zone.tenant_id = zone.tenant_id or context.project_id
- target = {
- 'tenant_id': zone.tenant_id,
- 'zone_name': zone.name
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: zone.tenant_id,
+ 'zone_name': zone.name
+ }
+ else:
+ target = {
+ 'tenant_id': zone.tenant_id,
+ 'zone_name': zone.name
+ }
policy.check('create_zone', context, target)
+ self._is_valid_project_id(zone.tenant_id)
+
# Ensure the tenant has enough quota to continue
self._enforce_zone_quota(context, zone.tenant_id)
@@ -971,11 +1013,19 @@ class Service(service.RPCService):
"""
zone = self.storage.get_zone(context, zone_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'tenant_id': zone.tenant_id
+ }
+
policy.check('get_zone', context, target)
return zone
@@ -988,11 +1038,19 @@ class Service(service.RPCService):
pool_id = cfg.CONF['service:central'].default_pool_id
else:
zone = self.storage.get_zone(context, zone_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'tenant_id': zone.tenant_id
- }
+
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'tenant_id': zone.tenant_id
+ }
pool_id = zone.pool_id
policy.check('get_zone_ns_records', context, target)
@@ -1010,7 +1068,11 @@ class Service(service.RPCService):
sort_key=None, sort_dir=None):
"""List existing zones including the ones flagged for deletion.
"""
- target = {'tenant_id': context.project_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('find_zones', context, target)
return self.storage.find_zones(context, criterion, marker, limit,
@@ -1018,7 +1080,11 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_zone(self, context, criterion=None):
- target = {'tenant_id': context.project_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('find_zone', context, target)
return self.storage.find_zone(context, criterion)
@@ -1032,11 +1098,19 @@ class Service(service.RPCService):
:returns: updated zone
"""
- target = {
- 'zone_id': zone.obj_get_original_value('id'),
- 'zone_name': zone.obj_get_original_value('name'),
- 'tenant_id': zone.obj_get_original_value('tenant_id'),
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone.obj_get_original_value('id'),
+ 'zone_name': zone.obj_get_original_value('name'),
+ constants.RBAC_PROJECT_ID: (
+ zone.obj_get_original_value('tenant_id')),
+ }
+ else:
+ target = {
+ 'zone_id': zone.obj_get_original_value('id'),
+ 'zone_name': zone.obj_get_original_value('name'),
+ 'tenant_id': zone.obj_get_original_value('tenant_id'),
+ }
policy.check('update_zone', context, target)
@@ -1102,11 +1176,18 @@ class Service(service.RPCService):
"""
zone = self.storage.get_zone(context, zone_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'tenant_id': zone.tenant_id
+ }
if hasattr(context, 'abandon') and context.abandon:
policy.check('abandon_zone', context, target)
@@ -1161,11 +1242,18 @@ class Service(service.RPCService):
def xfr_zone(self, context, zone_id):
zone = self.storage.get_zone(context, zone_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('xfr_zone', context, target)
@@ -1191,9 +1279,14 @@ class Service(service.RPCService):
if criterion is None:
criterion = {}
- target = {
- 'tenant_id': criterion.get('tenant_id', None)
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: criterion.get('tenant_id', None)
+ }
+ else:
+ target = {
+ 'tenant_id': criterion.get('tenant_id', None)
+ }
policy.check('count_zones', context, target)
@@ -1235,11 +1328,18 @@ class Service(service.RPCService):
def touch_zone(self, context, zone_id):
zone = self.storage.get_zone(context, zone_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('touch_zone', context, target)
@@ -1268,13 +1368,22 @@ class Service(service.RPCService):
if zone.action == 'DELETE':
raise exceptions.BadRequest('Can not update a deleting zone')
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'zone_type': zone.type,
- 'recordset_name': recordset.name,
- 'tenant_id': zone.tenant_id,
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_name': recordset.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id,
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_name': recordset.name,
+ 'tenant_id': zone.tenant_id,
+ }
policy.check('create_recordset', context, target)
@@ -1359,12 +1468,20 @@ class Service(service.RPCService):
else:
zone = self.storage.get_zone(context, recordset.zone_id)
- target = {
- 'zone_id': zone.id,
- 'zone_name': zone.name,
- 'recordset_id': recordset.id,
- 'tenant_id': zone.tenant_id,
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone.id,
+ 'zone_name': zone.name,
+ 'recordset_id': recordset.id,
+ constants.RBAC_PROJECT_ID: zone.tenant_id,
+ }
+ else:
+ target = {
+ 'zone_id': zone.id,
+ 'zone_name': zone.name,
+ 'recordset_id': recordset.id,
+ 'tenant_id': zone.tenant_id,
+ }
policy.check('get_recordset', context, target)
@@ -1377,7 +1494,11 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_recordsets(self, context, criterion=None, marker=None, limit=None,
sort_key=None, sort_dir=None, force_index=False):
- target = {'tenant_id': context.project_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('find_recordsets', context, target)
recordsets = self.storage.find_recordsets(context, criterion, marker,
@@ -1388,7 +1509,10 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_recordset(self, context, criterion=None):
- target = {'tenant_id': context.project_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
policy.check('find_recordset', context, target)
recordset = self.storage.find_recordset(context, criterion)
@@ -1432,13 +1556,22 @@ class Service(service.RPCService):
if zone.action == 'DELETE':
raise exceptions.BadRequest('Can not update a deleting zone')
- target = {
- 'zone_id': recordset.obj_get_original_value('zone_id'),
- 'zone_type': zone.type,
- 'recordset_id': recordset.obj_get_original_value('id'),
- 'zone_name': zone.name,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': recordset.obj_get_original_value('zone_id'),
+ 'zone_type': zone.type,
+ 'recordset_id': recordset.obj_get_original_value('id'),
+ 'zone_name': zone.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': recordset.obj_get_original_value('zone_id'),
+ 'zone_type': zone.type,
+ 'recordset_id': recordset.obj_get_original_value('id'),
+ 'zone_name': zone.name,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('update_recordset', context, target)
@@ -1496,13 +1629,22 @@ class Service(service.RPCService):
if zone.action == 'DELETE':
raise exceptions.BadRequest('Can not update a deleting zone')
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'zone_type': zone.type,
- 'recordset_id': recordset.id,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': recordset.id,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': recordset.id,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('delete_recordset', context, target)
@@ -1545,9 +1687,12 @@ class Service(service.RPCService):
if criterion is None:
criterion = {}
- target = {
- 'tenant_id': criterion.get('tenant_id', None)
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: criterion.get('tenant_id', None)
+ }
+ else:
+ target = {'tenant_id': criterion.get('tenant_id', None)}
policy.check('count_recordsets', context, target)
@@ -1567,14 +1712,24 @@ class Service(service.RPCService):
recordset = self.storage.get_recordset(context, recordset_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'zone_type': zone.type,
- 'recordset_id': recordset_id,
- 'recordset_name': recordset.name,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('create_record', context, target)
@@ -1621,14 +1776,24 @@ class Service(service.RPCService):
if recordset.id != record.recordset_id:
raise exceptions.RecordNotFound()
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'recordset_id': recordset_id,
- 'recordset_name': recordset.name,
- 'record_id': record.id,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ 'record_id': record.id,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ 'record_id': record.id,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('get_record', context, target)
@@ -1637,7 +1802,11 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_records(self, context, criterion=None, marker=None, limit=None,
sort_key=None, sort_dir=None):
- target = {'tenant_id': context.project_id}
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
policy.check('find_records', context, target)
return self.storage.find_records(context, criterion, marker, limit,
@@ -1645,7 +1814,11 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_record(self, context, criterion=None):
- target = {'tenant_id': context.project_id}
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
policy.check('find_record', context, target)
return self.storage.find_record(context, criterion)
@@ -1679,15 +1852,26 @@ class Service(service.RPCService):
raise exceptions.BadRequest('Moving a recordset between '
'recordsets is not allowed')
- target = {
- 'zone_id': record.obj_get_original_value('zone_id'),
- 'zone_name': zone.name,
- 'zone_type': zone.type,
- 'recordset_id': record.obj_get_original_value('recordset_id'),
- 'recordset_name': recordset.name,
- 'record_id': record.obj_get_original_value('id'),
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': record.obj_get_original_value('zone_id'),
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': record.obj_get_original_value('recordset_id'),
+ 'recordset_name': recordset.name,
+ 'record_id': record.obj_get_original_value('id'),
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': record.obj_get_original_value('zone_id'),
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': record.obj_get_original_value('recordset_id'),
+ 'recordset_name': recordset.name,
+ 'record_id': record.obj_get_original_value('id'),
+ 'tenant_id': zone.tenant_id
+ }
policy.check('update_record', context, target)
@@ -1741,15 +1925,26 @@ class Service(service.RPCService):
if recordset.id != record.recordset_id:
raise exceptions.RecordNotFound()
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'zone_type': zone.type,
- 'recordset_id': recordset_id,
- 'recordset_name': recordset.name,
- 'record_id': record.id,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ 'record_id': record.id,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'zone_type': zone.type,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ 'record_id': record.id,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('delete_record', context, target)
@@ -1785,9 +1980,12 @@ class Service(service.RPCService):
if criterion is None:
criterion = {}
- target = {
- 'tenant_id': criterion.get('tenant_id', None)
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: criterion.get('tenant_id', None)
+ }
+ else:
+ target = {'tenant_id': criterion.get('tenant_id', None)}
policy.check('count_records', context, target)
return self.storage.count_records(context, criterion)
@@ -1814,11 +2012,18 @@ class Service(service.RPCService):
def sync_zone(self, context, zone_id):
zone = self.storage.get_zone(context, zone_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('diagnostics_sync_zone', context, target)
@@ -1830,14 +2035,24 @@ class Service(service.RPCService):
zone = self.storage.get_zone(context, zone_id)
recordset = self.storage.get_recordset(context, recordset_id)
- target = {
- 'zone_id': zone_id,
- 'zone_name': zone.name,
- 'recordset_id': recordset_id,
- 'recordset_name': recordset.name,
- 'record_id': record_id,
- 'tenant_id': zone.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ 'record_id': record_id,
+ constants.RBAC_PROJECT_ID: zone.tenant_id
+ }
+ else:
+ target = {
+ 'zone_id': zone_id,
+ 'zone_name': zone.name,
+ 'recordset_id': recordset_id,
+ 'recordset_name': recordset.name,
+ 'record_id': record_id,
+ 'tenant_id': zone.tenant_id
+ }
policy.check('diagnostics_sync_record', context, target)
@@ -2257,6 +2472,8 @@ class Service(service.RPCService):
policy.check('create_pool', context)
+ self._is_valid_project_id(pool.tenant_id)
+
created_pool = self.storage.create_pool(context, pool)
return created_pool
@@ -2508,9 +2725,11 @@ class Service(service.RPCService):
if zone.action == 'DELETE':
raise exceptions.BadRequest('Can not transfer a deleting zone')
- target = {
- 'tenant_id': zone.tenant_id,
- }
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: zone.tenant_id}
+ else:
+ target = {'tenant_id': zone.tenant_id}
+
policy.check('create_zone_transfer_request', context, target)
zone_transfer_request.key = self._transfer_key_generator()
@@ -2518,6 +2737,8 @@ class Service(service.RPCService):
if zone_transfer_request.tenant_id is None:
zone_transfer_request.tenant_id = context.project_id
+ self._is_valid_project_id(zone_transfer_request.tenant_id)
+
created_zone_transfer_request = \
self.storage.create_zone_transfer_request(
context, zone_transfer_request)
@@ -2534,10 +2755,18 @@ class Service(service.RPCService):
elevated_context, zone_transfer_request_id)
LOG.info('Target Tenant ID found - using scoped policy')
- target = {
- 'target_tenant_id': zone_transfer_request.target_tenant_id,
- 'tenant_id': zone_transfer_request.tenant_id,
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_TARGET_PROJECT_ID: (zone_transfer_request.
+ target_tenant_id),
+ constants.RBAC_PROJECT_ID: zone_transfer_request.tenant_id,
+ }
+ else:
+ target = {
+ 'target_tenant_id': zone_transfer_request.target_tenant_id,
+ 'tenant_id': zone_transfer_request.tenant_id,
+ }
+
policy.check('get_zone_transfer_request', context, target)
return zone_transfer_request
@@ -2557,9 +2786,15 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_zone_transfer_request(self, context, criterion):
- target = {
- 'tenant_id': context.project_id,
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: context.project_id,
+ }
+ else:
+ target = {
+ 'tenant_id': context.project_id,
+ }
+
policy.check('find_zone_transfer_request', context, target)
return self.storage.find_zone_transfer_requests(context, criterion)
@@ -2571,9 +2806,14 @@ class Service(service.RPCService):
if 'zone_id' in zone_transfer_request.obj_what_changed():
raise exceptions.InvalidOperation('Zone cannot be changed')
- target = {
- 'tenant_id': zone_transfer_request.tenant_id,
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: zone_transfer_request.tenant_id,
+ }
+ else:
+ target = {
+ 'tenant_id': zone_transfer_request.tenant_id,
+ }
policy.check('update_zone_transfer_request', context, target)
request = self.storage.update_zone_transfer_request(
context, zone_transfer_request)
@@ -2587,9 +2827,14 @@ class Service(service.RPCService):
# Get zone transfer request
zone_transfer_request = self.storage.get_zone_transfer_request(
context, zone_transfer_request_id)
- target = {
- 'tenant_id': zone_transfer_request.tenant_id,
- }
+
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: zone_transfer_request.tenant_id
+ }
+ else:
+ target = {'tenant_id': zone_transfer_request.tenant_id}
+
policy.check('delete_zone_transfer_request', context, target)
return self.storage.delete_zone_transfer_request(
context,
@@ -2616,14 +2861,23 @@ class Service(service.RPCService):
raise exceptions.IncorrectZoneTransferKey(
'Key does not match stored key for request')
- target = {
- 'target_tenant_id': zone_transfer_request.target_tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_TARGET_PROJECT_ID: (zone_transfer_request.
+ target_tenant_id)
+ }
+ else:
+ target = {
+ 'target_tenant_id': zone_transfer_request.target_tenant_id
+ }
+
policy.check('create_zone_transfer_accept', context, target)
if zone_transfer_accept.tenant_id is None:
zone_transfer_accept.tenant_id = context.project_id
+ self._is_valid_project_id(zone_transfer_accept.tenant_id)
+
created_zone_transfer_accept = \
self.storage.create_zone_transfer_accept(
context, zone_transfer_accept)
@@ -2666,9 +2920,15 @@ class Service(service.RPCService):
zone_transfer_accept = self.storage.get_zone_transfer_accept(
context, zone_transfer_accept_id)
- target = {
- 'tenant_id': zone_transfer_accept.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: zone_transfer_accept.tenant_id
+ }
+ else:
+ target = {
+ 'tenant_id': zone_transfer_accept.tenant_id
+ }
+
policy.check('get_zone_transfer_accept', context, target)
return zone_transfer_accept
@@ -2690,9 +2950,14 @@ class Service(service.RPCService):
@notification('dns.zone_transfer_accept.update')
@transaction
def update_zone_transfer_accept(self, context, zone_transfer_accept):
- target = {
- 'tenant_id': zone_transfer_accept.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: zone_transfer_accept.tenant_id
+ }
+ else:
+ target = {
+ 'tenant_id': zone_transfer_accept.tenant_id
+ }
policy.check('update_zone_transfer_accept', context, target)
accept = self.storage.update_zone_transfer_accept(
context, zone_transfer_accept)
@@ -2707,9 +2972,15 @@ class Service(service.RPCService):
zt_accept = self.storage.get_zone_transfer_accept(
context, zone_transfer_accept_id)
- target = {
- 'tenant_id': zt_accept.tenant_id
- }
+ if policy.enforce_new_defaults():
+ target = {
+ constants.RBAC_PROJECT_ID: zt_accept.tenant_id
+ }
+ else:
+ target = {
+ 'tenant_id': zt_accept.tenant_id
+ }
+
policy.check('delete_zone_transfer_accept', context, target)
return self.storage.delete_zone_transfer_accept(
context,
@@ -2719,9 +2990,16 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
@notification('dns.zone_import.create')
def create_zone_import(self, context, request_body):
- target = {'tenant_id': context.project_id}
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('create_zone_import', context, target)
+ self._is_valid_project_id(context.project_id)
+
values = {
'status': 'PENDING',
'message': None,
@@ -2813,7 +3091,12 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_zone_imports(self, context, criterion=None, marker=None,
limit=None, sort_key=None, sort_dir=None):
- target = {'tenant_id': context.project_id}
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('find_zone_imports', context, target)
if not criterion:
@@ -2828,16 +3111,22 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def get_zone_import(self, context, zone_import_id):
- target = {'tenant_id': context.project_id}
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('get_zone_import', context, target)
return self.storage.get_zone_import(context, zone_import_id)
@rpc.expected_exceptions()
@notification('dns.zone_import.update')
def update_zone_import(self, context, zone_import):
- target = {
- 'tenant_id': zone_import.tenant_id,
- }
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: zone_import.tenant_id}
+ else:
+ target = {'tenant_id': zone_import.tenant_id}
policy.check('update_zone_import', context, target)
return self.storage.update_zone_import(context, zone_import)
@@ -2846,10 +3135,18 @@ class Service(service.RPCService):
@notification('dns.zone_import.delete')
@transaction
def delete_zone_import(self, context, zone_import_id):
- target = {
- 'zone_import_id': zone_import_id,
- 'tenant_id': context.project_id
- }
+
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_import_id': zone_import_id,
+ constants.RBAC_PROJECT_ID: context.project_id
+ }
+ else:
+ target = {
+ 'zone_import_id': zone_import_id,
+ 'tenant_id': context.project_id
+ }
+
policy.check('delete_zone_import', context, target)
zone_import = self.storage.delete_zone_import(context, zone_import_id)
@@ -2863,9 +3160,15 @@ class Service(service.RPCService):
# Try getting the zone to ensure it exists
zone = self.storage.get_zone(context, zone_id)
- target = {'tenant_id': context.project_id}
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('create_zone_export', context, target)
+ self._is_valid_project_id(context.project_id)
+
values = {
'status': 'PENDING',
'message': None,
@@ -2886,7 +3189,11 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def find_zone_exports(self, context, criterion=None, marker=None,
limit=None, sort_key=None, sort_dir=None):
- target = {'tenant_id': context.project_id}
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
policy.check('find_zone_exports', context, target)
if not criterion:
@@ -2901,7 +3208,12 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
def get_zone_export(self, context, zone_export_id):
- target = {'tenant_id': context.project_id}
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: context.project_id}
+ else:
+ target = {'tenant_id': context.project_id}
+
policy.check('get_zone_export', context, target)
return self.storage.get_zone_export(context, zone_export_id)
@@ -2909,9 +3221,12 @@ class Service(service.RPCService):
@rpc.expected_exceptions()
@notification('dns.zone_export.update')
def update_zone_export(self, context, zone_export):
- target = {
- 'tenant_id': zone_export.tenant_id,
- }
+
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: zone_export.tenant_id}
+ else:
+ target = {'tenant_id': zone_export.tenant_id}
+
policy.check('update_zone_export', context, target)
return self.storage.update_zone_export(context, zone_export)
@@ -2920,10 +3235,18 @@ class Service(service.RPCService):
@notification('dns.zone_export.delete')
@transaction
def delete_zone_export(self, context, zone_export_id):
- target = {
- 'zone_export_id': zone_export_id,
- 'tenant_id': context.project_id
- }
+
+ if policy.enforce_new_defaults():
+ target = {
+ 'zone_export_id': zone_export_id,
+ constants.RBAC_PROJECT_ID: context.project_id
+ }
+ else:
+ target = {
+ 'zone_export_id': zone_export_id,
+ 'tenant_id': context.project_id
+ }
+
policy.check('delete_zone_export', context, target)
zone_export = self.storage.delete_zone_export(context, zone_export_id)
diff --git a/designate/common/constants.py b/designate/common/constants.py
index 295ee8b7..3857e02f 100644
--- a/designate/common/constants.py
+++ b/designate/common/constants.py
@@ -22,3 +22,7 @@ QUOTA_ZONE_RECORDSETS = 'zone_recordsets'
QUOTA_ZONES = 'zones'
VALID_QUOTAS = [QUOTA_API_EXPORT_SIZE, QUOTA_RECORDSET_RECORDS,
QUOTA_ZONE_RECORDS, QUOTA_ZONE_RECORDSETS, QUOTA_ZONES]
+
+# RBAC related constants
+RBAC_PROJECT_ID = 'project_id'
+RBAC_TARGET_PROJECT_ID = 'target_project_id'
diff --git a/designate/common/policies/base.py b/designate/common/policies/base.py
index adb2a6c6..c09298db 100644
--- a/designate/common/policies/base.py
+++ b/designate/common/policies/base.py
@@ -12,17 +12,14 @@
# License for the specific language governing permissions and limitations
# under the License.
-
+from oslo_log import versionutils
from oslo_policy import policy
-RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
-RULE_ADMIN = 'rule:admin'
-RULE_ZONE_PRIMARY_OR_ADMIN = \
- "('PRIMARY':%(zone_type)s and rule:admin_or_owner) "\
- "OR ('SECONDARY':%(zone_type)s AND is_admin:True)"
-RULE_ZONE_TRANSFER = "rule:admin_or_owner OR tenant:%(target_tenant_id)s " \
- "OR None:%(target_tenant_id)s"
+DEPRECATED_REASON = """
+The designate API now supports system scope and default roles.
+"""
+
RULE_ANY = "@"
# Generic policy check string for system administrators. These are the people
@@ -59,37 +56,56 @@ SYSTEM_OR_PROJECT_READER = (
'(' + SYSTEM_READER + ') or (' + PROJECT_READER + ')'
)
+# Designate specific "secure RBAC" rules
+ALL_TENANTS = 'True:%(all_tenants)s'
+
+ALL_TENANTS_READER = ALL_TENANTS + ' and role:reader'
+
+SYSTEM_OR_PROJECT_READER_OR_ALL_TENANTS_READER = (
+ '(' + SYSTEM_READER + ') or (' + PROJECT_READER + ') or (' +
+ ALL_TENANTS_READER + ')'
+)
+
+RULE_ZONE_TRANSFER = (
+ '(' + SYSTEM_ADMIN_OR_PROJECT_MEMBER + ') or '
+ 'project_id:%(target_project_id)s or '
+ 'None:%(target_project_id)s')
+
+
+# Deprecated in Wallaby as part of the "secure RBAC" work.
+# TODO(johnsom) remove when the deprecated RBAC rules are removed.
+RULE_ADMIN = 'rule:admin'
+RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
+LEGACY_RULE_ZONE_TRANSFER = "rule:admin_or_owner OR " \
+ "tenant:%(target_tenant_id)s " \
+ "OR None:%(target_tenant_id)s"
+
+deprecated_default = policy.DeprecatedRule(
+ name="default",
+ check_str=RULE_ADMIN_OR_OWNER,
+ deprecated_reason=DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+
rules = [
+ # TODO(johnsom) remove when the deprecated RBAC rules are removed.
policy.RuleDefault(
name="admin",
check_str="role:admin or is_admin:True"),
- policy.RuleDefault(
- name="primary_zone",
- check_str="target.zone_type:SECONDARY"),
+ # TODO(johnsom) remove when the deprecated RBAC rules are removed.
policy.RuleDefault(
name="owner",
check_str="tenant:%(tenant_id)s"),
+ # TODO(johnsom) remove when the deprecated RBAC rules are removed.
policy.RuleDefault(
name="admin_or_owner",
check_str="rule:admin or rule:owner"),
+
+ # Default policy
policy.RuleDefault(
name="default",
- check_str="rule:admin_or_owner"),
- policy.RuleDefault(
- name="target",
- check_str="tenant:%(target_tenant_id)s"),
- policy.RuleDefault(
- name="owner_or_target",
- check_str="rule:target or rule:owner"),
- policy.RuleDefault(
- name="admin_or_owner_or_target",
- check_str="rule:owner_or_target or rule:admin"),
- policy.RuleDefault(
- name="admin_or_target",
- check_str="rule:admin or rule:target"),
- policy.RuleDefault(
- name="zone_primary_or_admin",
- check_str=RULE_ZONE_PRIMARY_OR_ADMIN)
+ check_str=SYSTEM_ADMIN_OR_PROJECT_MEMBER,
+ deprecated_rule=deprecated_default),
]
diff --git a/designate/common/policies/context.py b/designate/common/policies/context.py
index 08a528f3..81ab54d5 100644
--- a/designate/common/policies/context.py
+++ b/designate/common/policies/context.py
@@ -13,28 +13,62 @@
# under the License.
+from oslo_log import versionutils
from oslo_policy import policy
from designate.common.policies import base
+deprecated_all_tenants = policy.DeprecatedRule(
+ name="all_tenants",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+deprecated_edit_managed_records = policy.DeprecatedRule(
+ name="edit_managed_records",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+deprecated_use_low_ttl = policy.DeprecatedRule(
+ name="use_low_ttl",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+deprecated_use_sudo = policy.DeprecatedRule(
+ name="use_sudo",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+
rules = [
policy.RuleDefault(
name="all_tenants",
- check_str=base.RULE_ADMIN,
- description='Action on all tenants.'),
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Action on all tenants.',
+ deprecated_rule=deprecated_all_tenants),
policy.RuleDefault(
name="edit_managed_records",
- check_str=base.RULE_ADMIN,
- description='Edit managed records.'),
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Edit managed records.',
+ deprecated_rule=deprecated_edit_managed_records),
policy.RuleDefault(
name="use_low_ttl",
- check_str=base.RULE_ADMIN,
- description='Use low TTL.'),
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Use low TTL.',
+ deprecated_rule=deprecated_use_low_ttl),
policy.RuleDefault(
name="use_sudo",
- check_str=base.RULE_ADMIN,
- description='Accept sudo from user to tenant.')
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Accept sudo from user to tenant.',
+ deprecated_rule=deprecated_use_sudo)
]
diff --git a/designate/common/policies/diagnostics.py b/designate/common/policies/diagnostics.py
index 9b903231..55574bf5 100644
--- a/designate/common/policies/diagnostics.py
+++ b/designate/common/policies/diagnostics.py
@@ -12,29 +12,62 @@
# License for the specific language governing permissions and limitations
# under the License.
-
+from oslo_log import versionutils
from oslo_policy import policy
from designate.common.policies import base
+deprecated_diagnostics_ping = policy.DeprecatedRule(
+ name="diagnostics_ping",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+deprecated_diagnostics_sync_zones = policy.DeprecatedRule(
+ name="diagnostics_sync_zones",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+deprecated_diagnostics_sync_zone = policy.DeprecatedRule(
+ name="diagnostics_sync_zone",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+deprecated_diagnostics_sync_record = policy.DeprecatedRule(
+ name="diagnostics_sync_record",
+ check_str=base.RULE_ADMIN,
+ deprecated_reason=base.DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+
rules = [
policy.RuleDefault(
name="diagnostics_ping",
- check_str=base.RULE_ADMIN,
- description='Diagnose ping.'),
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Diagnose ping.',
+ deprecated_rule=deprecated_diagnostics_ping),
policy.RuleDefault(
name="diagnostics_sync_zones",
- check_str=base.RULE_ADMIN,
- description='Diagnose sync zones.'),
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Diagnose sync zones.',
+ deprecated_rule=deprecated_diagnostics_sync_zones),
policy.RuleDefault(
name="diagnostics_sync_zone",
- check_str=base.RULE_ADMIN,
- description='Diagnose sync zone.'),
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Diagnose sync zone.',
+ deprecated_rule=deprecated_diagnostics_sync_zone),
policy.RuleDefault(
name="diagnostics_sync_record",
- check_str=base.RULE_ADMIN,
- description='Diagnose sync record.')
+ check_str=base.SYSTEM_ADMIN,
+ scope_types=['system'],
+ description='Diagnose sync record.',
+ deprecated_rule=deprecated_diagnostics_sync_record)
]
diff --git a/designate/common/policies/quota.py b/designate/common/policies/quota.py
index f430d9a7..0ddb4459 100644
--- a/designate/common/policies/quota.py
+++ b/designate/common/policies/quota.py
@@ -50,7 +50,7 @@ deprecated_reset_quotas = policy.DeprecatedRule(
rules = [
policy.DocumentedRuleDefault(
name="get_quotas",
- check_str=base.SYSTEM_OR_PROJECT_READER,
+ check_str=base.SYSTEM_OR_PROJECT_READER_OR_ALL_TENANTS_READER,
scope_types=['system', 'project'],
description="View Current Project's Quotas.",
operations=[
diff --git a/designate/common/policies/recordset.py b/designate/common/policies/recordset.py
index e77025eb..6dad34fc 100644
--- a/designate/common/policies/recordset.py
+++ b/designate/common/policies/recordset.py
@@ -22,9 +22,15 @@ DEPRECATED_REASON = """
The record set API now supports system scope and default roles.
"""
+# Deprecated in Wallaby as part of the "secure RBAC" work.
+# TODO(johnsom) remove when the deprecated RBAC rules are removed.
+RULE_ZONE_PRIMARY_OR_ADMIN = (
+ "('PRIMARY':%(zone_type)s and rule:admin_or_owner) "
+ "OR ('SECONDARY':%(zone_type)s AND is_admin:True)")
+
deprecated_create_recordset = policy.DeprecatedRule(
name="create_recordset",
- check_str=base.RULE_ZONE_PRIMARY_OR_ADMIN,
+ check_str=RULE_ZONE_PRIMARY_OR_ADMIN,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
@@ -40,15 +46,27 @@ deprecated_get_recordset = policy.DeprecatedRule(
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
+deprecated_find_recordset = policy.DeprecatedRule(
+ name="find_recordset",
+ check_str=base.RULE_ADMIN_OR_OWNER,
+ deprecated_reason=DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
+deprecated_find_recordsets = policy.DeprecatedRule(
+ name="find_recordsets",
+ check_str=base.RULE_ADMIN_OR_OWNER,
+ deprecated_reason=DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
deprecated_update_recordset = policy.DeprecatedRule(
name="update_recordset",
- check_str=base.RULE_ZONE_PRIMARY_OR_ADMIN,
+ check_str=RULE_ZONE_PRIMARY_OR_ADMIN,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
deprecated_delete_recordset = policy.DeprecatedRule(
name="delete_recordset",
- check_str=base.RULE_ZONE_PRIMARY_OR_ADMIN,
+ check_str=RULE_ZONE_PRIMARY_OR_ADMIN,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
@@ -69,7 +87,7 @@ SYSTEM_ADMIN_AND_SECONDARY_ZONE = (
'(' + base.SYSTEM_ADMIN + ') and (\'SECONDARY\':%(zone_type)s)'
)
-SYSTEM_ADMIN_OR_PROJECT_MEMBER = ''.join(
+SYSTEM_ADMIN_OR_PROJECT_MEMBER_ZONE_TYPE = ' or '.join(
[PROJECT_MEMBER_AND_PRIMARY_ZONE,
SYSTEM_ADMIN_AND_PRIMARY_ZONE,
SYSTEM_ADMIN_AND_SECONDARY_ZONE]
@@ -79,16 +97,13 @@ SYSTEM_ADMIN_OR_PROJECT_MEMBER = ''.join(
rules = [
policy.DocumentedRuleDefault(
name="create_recordset",
- check_str=SYSTEM_ADMIN_AND_SECONDARY_ZONE,
+ check_str=SYSTEM_ADMIN_OR_PROJECT_MEMBER_ZONE_TYPE,
scope_types=['system', 'project'],
description="Create Recordset",
operations=[
{
'path': '/v2/zones/{zone_id}/recordsets',
'method': 'POST'
- }, {
- 'path': '/v2/reverse/floatingips/{region}:{floatingip_id}',
- 'method': 'PATCH'
}
],
deprecated_rule=deprecated_create_recordset
@@ -108,35 +123,46 @@ rules = [
{
'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
'method': 'GET'
- }, {
- 'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
- 'method': 'DELETE'
- }, {
- 'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
- 'method': 'PUT'
}
],
deprecated_rule=deprecated_get_recordset
),
+ policy.RuleDefault(
+ name="find_recordset",
+ check_str=base.SYSTEM_OR_PROJECT_READER,
+ scope_types=['system', 'project'],
+ description="List a Recordset in a Zone",
+ deprecated_rule=deprecated_find_recordset
+ ),
+ policy.DocumentedRuleDefault(
+ name="find_recordsets",
+ check_str=base.SYSTEM_OR_PROJECT_READER,
+ scope_types=['system', 'project'],
+ description="List Recordsets in a Zone",
+ operations=[
+ {
+ 'path': '/v2/zones/{zone_id}/recordsets',
+ 'method': 'GET'
+ },
+ ],
+ deprecated_rule=deprecated_find_recordsets
+ ),
policy.DocumentedRuleDefault(
name="update_recordset",
- check_str=SYSTEM_ADMIN_AND_SECONDARY_ZONE,
+ check_str=SYSTEM_ADMIN_OR_PROJECT_MEMBER_ZONE_TYPE,
scope_types=['system', 'project'],
description="Update recordset",
operations=[
{
'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
'method': 'PUT'
- }, {
- 'path': '/v2/reverse/floatingips/{region}:{floatingip_id}',
- 'method': 'PATCH'
}
],
deprecated_rule=deprecated_update_recordset
),
policy.DocumentedRuleDefault(
name="delete_recordset",
- check_str=SYSTEM_ADMIN_AND_SECONDARY_ZONE,
+ check_str=SYSTEM_ADMIN_OR_PROJECT_MEMBER_ZONE_TYPE,
scope_types=['system', 'project'],
description="Delete RecordSet",
operations=[
diff --git a/designate/common/policies/tsigkey.py b/designate/common/policies/tsigkey.py
index b3562315..2df26f3e 100644
--- a/designate/common/policies/tsigkey.py
+++ b/designate/common/policies/tsigkey.py
@@ -89,9 +89,6 @@ rules = [
operations=[
{
'path': '/v2/tsigkeys/{tsigkey_id}',
- 'method': 'PATCH'
- }, {
- 'path': '/v2/tsigkeys/{tsigkey_id}',
'method': 'GET'
}
],
diff --git a/designate/common/policies/zone.py b/designate/common/policies/zone.py
index 74552597..028dcff5 100644
--- a/designate/common/policies/zone.py
+++ b/designate/common/policies/zone.py
@@ -46,6 +46,12 @@ deprecated_get_zone_servers = policy.DeprecatedRule(
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
+deprecated_get_zone_ns_records = policy.DeprecatedRule(
+ name="get_zone_ns_records",
+ check_str=base.RULE_ADMIN_OR_OWNER,
+ deprecated_reason=DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
deprecated_find_zones = policy.DeprecatedRule(
name="find_zones",
check_str=base.RULE_ADMIN_OR_OWNER,
@@ -131,12 +137,6 @@ rules = [
{
'path': '/v2/zones/{zone_id}',
'method': 'GET'
- }, {
- 'path': '/v2/zones/{zone_id}',
- 'method': 'PATCH'
- }, {
- 'path': '/v2/zones/{zone_id}/recordsets/{recordset_id}',
- 'method': 'PUT'
}
],
deprecated_rule=deprecated_get_zone
@@ -148,6 +148,19 @@ rules = [
deprecated_rule=deprecated_get_zone_servers
),
policy.DocumentedRuleDefault(
+ name="get_zone_ns_records",
+ check_str=base.SYSTEM_OR_PROJECT_READER,
+ scope_types=['system', 'project'],
+ description="Get the Name Servers for a Zone",
+ operations=[
+ {
+ 'path': '/v2/zones/{zone_id}/nameservers',
+ 'method': 'GET'
+ }
+ ],
+ deprecated_rule=deprecated_get_zone_ns_records
+ ),
+ policy.DocumentedRuleDefault(
name="find_zones",
check_str=base.SYSTEM_OR_PROJECT_READER,
scope_types=['system', 'project'],
diff --git a/designate/common/policies/zone_export.py b/designate/common/policies/zone_export.py
index c3f02443..ca45971b 100644
--- a/designate/common/policies/zone_export.py
+++ b/designate/common/policies/zone_export.py
@@ -52,6 +52,12 @@ deprecated_update_zone_export = policy.DeprecatedRule(
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
+deprecated_delete_zone_export = policy.DeprecatedRule(
+ name="delete_zone_export",
+ check_str=base.RULE_ADMIN_OR_OWNER,
+ deprecated_reason=DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
+)
rules = [
@@ -103,9 +109,6 @@ rules = [
{
'path': '/v2/zones/tasks/exports/{zone_export_id}',
'method': 'GET'
- }, {
- 'path': '/v2/zones/tasks/exports/{zone_export_id}/export',
- 'method': 'GET'
}
],
deprecated_rule=deprecated_get_zone_export
@@ -122,7 +125,20 @@ rules = [
}
],
deprecated_rule=deprecated_update_zone_export
- )
+ ),
+ policy.DocumentedRuleDefault(
+ name="delete_zone_export",
+ check_str=base.SYSTEM_ADMIN_OR_PROJECT_MEMBER,
+ scope_types=['system', 'project'],
+ description="Delete a zone export",
+ operations=[
+ {
+ 'path': '/v2/zones/tasks/exports/{zone_export_id}',
+ 'method': 'DELETE'
+ }
+ ],
+ deprecated_rule=deprecated_delete_zone_export
+ ),
]
diff --git a/designate/common/policies/zone_import.py b/designate/common/policies/zone_import.py
index 02a383e1..8d4f2b17 100644
--- a/designate/common/policies/zone_import.py
+++ b/designate/common/policies/zone_import.py
@@ -115,7 +115,7 @@ rules = [
operations=[
{
'path': '/v2/zones/tasks/imports/{zone_import_id}',
- 'method': 'GET'
+ 'method': 'DELETE'
}
],
deprecated_rule=deprecated_delete_zone_import
diff --git a/designate/common/policies/zone_transfer_accept.py b/designate/common/policies/zone_transfer_accept.py
index eeca7435..05e18a64 100644
--- a/designate/common/policies/zone_transfer_accept.py
+++ b/designate/common/policies/zone_transfer_accept.py
@@ -24,7 +24,7 @@ The zone transfer accept API now supports system scope and default roles.
deprecated_create_zone_transfer_accept = policy.DeprecatedRule(
name="create_zone_transfer_accept",
- check_str=base.RULE_ZONE_TRANSFER,
+ check_str=base.LEGACY_RULE_ZONE_TRANSFER,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
@@ -64,13 +64,15 @@ rules = [
policy.DocumentedRuleDefault(
name="create_zone_transfer_accept",
check_str=base.RULE_ZONE_TRANSFER,
+ scope_types=['system', 'project'],
description="Create Zone Transfer Accept",
operations=[
{
'path': '/v2/zones/tasks/transfer_accepts',
'method': 'POST'
}
- ]
+ ],
+ deprecated_rule=deprecated_create_zone_transfer_accept
),
policy.DocumentedRuleDefault(
name="get_zone_transfer_accept",
diff --git a/designate/common/policies/zone_transfer_request.py b/designate/common/policies/zone_transfer_request.py
index 0ed2c8d3..5178aaf6 100644
--- a/designate/common/policies/zone_transfer_request.py
+++ b/designate/common/policies/zone_transfer_request.py
@@ -23,14 +23,14 @@ The zone transfer request API now supports system scope and default roles.
"""
deprecated_create_zone_transfer_request = policy.DeprecatedRule(
- name="create_zone_transfer_request",
- check_str=base.RULE_ADMIN_OR_OWNER,
- deprecated_reason=DEPRECATED_REASON,
- deprecated_since=versionutils.deprecated.WALLABY
+ name="create_zone_transfer_request",
+ check_str=base.RULE_ADMIN_OR_OWNER,
+ deprecated_reason=DEPRECATED_REASON,
+ deprecated_since=versionutils.deprecated.WALLABY
)
deprecated_get_zone_transfer_request = policy.DeprecatedRule(
name="get_zone_transfer_request",
- check_str=base.RULE_ZONE_TRANSFER,
+ check_str=base.LEGACY_RULE_ZONE_TRANSFER,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
@@ -40,12 +40,6 @@ deprecated_get_zone_transfer_request_detailed = policy.DeprecatedRule(
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY
)
-deprecated_find_zone_transfer_requests = policy.DeprecatedRule(
- name="find_zone_transfer_requests",
- check_str=base.RULE_ANY,
- deprecated_reason=DEPRECATED_REASON,
- deprecated_since=versionutils.deprecated.WALLABY
-)
deprecated_update_zone_transfer_request = policy.DeprecatedRule(
name="update_zone_transfer_request",
check_str=base.RULE_ADMIN_OR_OWNER,
@@ -77,16 +71,15 @@ rules = [
policy.DocumentedRuleDefault(
name="get_zone_transfer_request",
check_str=base.RULE_ZONE_TRANSFER,
+ scope_types=['system', 'project'],
description="Show a Zone Transfer Request",
operations=[
{
'path': '/v2/zones/tasks/transfer_requests/{zone_transfer_request_id}', # noqa
'method': 'GET'
- }, {
- 'path': '/v2/zones/tasks/transfer_requests/{zone_transfer_request_id}', # noqa
- 'method': 'PATCH'
}
- ]
+ ],
+ deprecated_rule=deprecated_get_zone_transfer_request
),
policy.RuleDefault(
name="get_zone_transfer_request_detailed",
@@ -103,7 +96,7 @@ rules = [
'path': '/v2/zones/tasks/transfer_requests',
'method': 'GET'
}
- ]
+ ],
),
policy.RuleDefault(
name="find_zone_transfer_request",
diff --git a/designate/context.py b/designate/context.py
index 13ccda13..b5c2c1e8 100644
--- a/designate/context.py
+++ b/designate/context.py
@@ -107,6 +107,8 @@ class DesignateContext(context.RequestContext):
# NOTE(kiall): Ugly - required to match http://tinyurl.com/o3y8qmw
context.roles.append('admin')
+ if policy.enforce_new_defaults():
+ context.system_scope = 'all'
if show_deleted is not None:
context.show_deleted = show_deleted
@@ -132,7 +134,8 @@ class DesignateContext(context.RequestContext):
def get_admin_context(cls, **kwargs):
# TODO(kiall): Remove Me
kwargs['is_admin'] = True
- kwargs['roles'] = ['admin']
+ kwargs['roles'] = ['admin', 'reader']
+ kwargs['system_scope'] = 'all'
return cls(None, **kwargs)
diff --git a/designate/exceptions.py b/designate/exceptions.py
index 2aa73127..071addf5 100644
--- a/designate/exceptions.py
+++ b/designate/exceptions.py
@@ -256,6 +256,10 @@ class IncorrectZoneTransferKey(Forbidden):
error_type = 'invalid_key'
+class InvalidTokenScope(Forbidden):
+ error_type = 'invalid_token_scope'
+
+
class Duplicate(DesignateException):
expected = True
error_code = 409
@@ -473,3 +477,12 @@ class LastServerDeleteNotAllowed(BadRequest):
class ResourceNotFound(NotFound):
# TODO(kiall): Should this be extending NotFound??
pass
+
+
+class MissingProjectID(BadRequest):
+ # Note: This should be 400, but is 401 for compatibility with
+ # previous versions of the API.
+ # https://github.com/openstack/designate/blob/stable/wallaby/ \
+ # designate/api/middleware.py#L132
+ error_code = 401
+ error_type = 'missing_project_id'
diff --git a/designate/objects/adapters/api_v2/zone_transfer_request.py b/designate/objects/adapters/api_v2/zone_transfer_request.py
index 56b9790f..5e430d9a 100644
--- a/designate/objects/adapters/api_v2/zone_transfer_request.py
+++ b/designate/objects/adapters/api_v2/zone_transfer_request.py
@@ -12,10 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from designate.objects.adapters.api_v2 import base
+from designate.common import constants
+from designate import exceptions
from designate import objects
+from designate.objects.adapters.api_v2 import base
from designate import policy
-from designate import exceptions
class ZoneTransferRequestAPIv2Adapter(base.APIv2Adapter):
@@ -66,9 +67,10 @@ class ZoneTransferRequestAPIv2Adapter(base.APIv2Adapter):
object, *args, **kwargs)
try:
- target = {
- 'tenant_id': object.tenant_id,
- }
+ if policy.enforce_new_defaults():
+ target = {constants.RBAC_PROJECT_ID: object.tenant_id}
+ else:
+ target = {'tenant_id': object.tenant_id}
policy.check(
'get_zone_transfer_request_detailed',
diff --git a/designate/objects/blacklist.py b/designate/objects/blacklist.py
index 1a5eb388..a0dd4fcf 100644
--- a/designate/objects/blacklist.py
+++ b/designate/objects/blacklist.py
@@ -20,8 +20,8 @@ from designate.objects import fields
class Blacklist(base.DictObjectMixin, base.PersistentObjectMixin,
base.DesignateObject):
fields = {
- 'pattern': fields.StringFields(maxLength=255),
- 'description': fields.StringFields(maxLength=160, nullable=True),
+ 'pattern': fields.DenylistFields(maxLength=255),
+ 'description': fields.DenylistFields(maxLength=160, nullable=True),
}
STRING_KEYS = [
diff --git a/designate/objects/fields.py b/designate/objects/fields.py
index 9b533438..dd3268c8 100644
--- a/designate/objects/fields.py
+++ b/designate/objects/fields.py
@@ -451,3 +451,25 @@ class IPOrHost(IPV4AndV6AddressField):
if not re.match(StringFields.RE_ZONENAME, value):
raise ValueError("%s is not IP address or host name" % value)
return value
+
+
+class DenylistFields(StringFields):
+ def __init__(self, **kwargs):
+ super(DenylistFields, self).__init__(**kwargs)
+
+ def coerce(self, obj, attr, value):
+ value = super(DenylistFields, self).coerce(obj, attr, value)
+
+ if value is None:
+ return self._null(obj, attr)
+
+ # determine the validity if a regex expression filter has been used.
+ msg = ("%s is not a valid regular expression" % value)
+ if not len(value):
+ raise ValueError(msg)
+ try:
+ re.compile(value)
+ except Exception:
+ raise ValueError(msg)
+
+ return value
diff --git a/designate/policy.py b/designate/policy.py
index 863f0ddc..041e7f41 100644
--- a/designate/policy.py
+++ b/designate/policy.py
@@ -73,10 +73,17 @@ def init(default_rule=None, policy_file=None):
def check(rule, ctxt, target=None, do_raise=True, exc=exceptions.Forbidden):
- creds = ctxt.to_dict()
+ if enforce_new_defaults():
+ creds = ctxt.to_policy_values()
+ else:
+ creds = ctxt.to_dict()
target = target or {}
try:
result = _ENFORCER.enforce(rule, target, creds, do_raise, exc)
+ except policy.InvalidScope:
+ result = False
+ if do_raise:
+ raise exceptions.InvalidTokenScope
except Exception:
result = False
raise
@@ -93,3 +100,9 @@ def check(rule, ctxt, target=None, do_raise=True, exc=exceptions.Forbidden):
LOG.info("Policy check failed for rule '%(rule)s' "
"on target %(target)s",
{'rule': rule, 'target': repr(target)}, extra=extra)
+
+
+def enforce_new_defaults():
+ if CONF.get('oslo_policy'):
+ return CONF['oslo_policy'].get('enforce_new_defaults', False)
+ return False
diff --git a/designate/storage/impl_sqlalchemy/__init__.py b/designate/storage/impl_sqlalchemy/__init__.py
index 7449f9e2..fe46a88b 100644
--- a/designate/storage/impl_sqlalchemy/__init__.py
+++ b/designate/storage/impl_sqlalchemy/__init__.py
@@ -1488,6 +1488,12 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
).select_from(ljoin)
if not context.all_tenants:
+ # If we have a system scoped token with no project_id and
+ # all_tenants was not used, we don't know what records to return,
+ # so return an empty list.
+ if not context.project_id:
+ return objects.ZoneTransferRequestList()
+
query = query.where(or_(
table.c.tenant_id == context.project_id,
table.c.target_tenant_id == context.project_id))
@@ -1498,7 +1504,8 @@ class SQLAlchemyStorage(sqlalchemy_base.SQLAlchemy, storage_base.Storage):
exceptions.ZoneTransferRequestNotFound,
criterion,
one=one, marker=marker, limit=limit, sort_dir=sort_dir,
- sort_key=sort_key, query=query, apply_tenant_criteria=False
+ sort_key=sort_key, query=query,
+ apply_tenant_criteria=False
)
def create_zone_transfer_request(self, context, zone_transfer_request):
diff --git a/designate/tests/__init__.py b/designate/tests/__init__.py
index 364fad82..07bf510d 100644
--- a/designate/tests/__init__.py
+++ b/designate/tests/__init__.py
@@ -388,6 +388,8 @@ class TestCase(base.BaseTestCase):
self.central_service = self.start_service('central')
self.admin_context = self.get_admin_context()
+ self.admin_context_all_tenants = self.get_admin_context(
+ all_tenants=True)
storage_driver = CONF['service:central'].storage_driver
self.storage = storage.get_storage(storage_driver)
@@ -437,10 +439,11 @@ class TestCase(base.BaseTestCase):
def get_context(self, **kwargs):
return DesignateContext(**kwargs)
- def get_admin_context(self):
+ def get_admin_context(self, **kwargs):
return DesignateContext.get_admin_context(
project_id=utils.generate_uuid(),
- user_id=utils.generate_uuid())
+ user_id=utils.generate_uuid(),
+ **kwargs)
# Fixture methods
def get_quota_fixture(self, fixture=0, values=None):
@@ -795,7 +798,7 @@ class TestCase(base.BaseTestCase):
# Retrieve it, and ensure it's the same
zone_import = self.central_service.get_zone_import(
- self.admin_context, zone_import_id)
+ self.admin_context_all_tenants, zone_import_id)
# If the import is done, we're done
if zone_import.status == 'COMPLETE':
diff --git a/designate/tests/test_api/test_middleware.py b/designate/tests/test_api/test_middleware.py
index 3425c1c6..6bb8be79 100644
--- a/designate/tests/test_api/test_middleware.py
+++ b/designate/tests/test_api/test_middleware.py
@@ -102,7 +102,8 @@ class KeystoneContextMiddlewareTest(ApiTestCase):
# Process the request
response = app(request)
- self.assertEqual(401, response.status_code)
+ # Ensure request was not blocked
+ self.assertEqual(response, 'FakeResponse')
class NoAuthContextMiddlewareTest(ApiTestCase):
diff --git a/designate/tests/test_api/test_v2/test_blacklists.py b/designate/tests/test_api/test_v2/test_blacklists.py
index 2dff9b8c..0677b7e1 100644
--- a/designate/tests/test_api/test_v2/test_blacklists.py
+++ b/designate/tests/test_api/test_v2/test_blacklists.py
@@ -165,3 +165,48 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
url = '/blacklists?description=test'
self.policy({'find_blacklists': '@'})
self._assert_exception('bad_request', 400, self.client.get, url)
+
+ def test_create_invalid_denylist_pattern(self):
+ self.policy({'create_blacklist': '@'})
+ body = {
+ 'description': u'This is the description.'
+ }
+
+ url = '/blacklists/'
+
+ # doing each pattern individually so upon error one can trace
+ # back to the exact line number
+ body['pattern'] = ''
+ self._assert_exception(
+ 'invalid_object', 400, self.client.post_json, url, body)
+
+ body['pattern'] = '#(*&^%$%$#@$'
+ self._assert_exception(
+ 'invalid_object', 400, self.client.post_json, url, body)
+
+ body['pattern'] = 'a' * 1000
+ self._assert_exception(
+ 'invalid_object', 400, self.client.post_json, url, body)
+
+ def test_update_invalid_denylist_pattern(self):
+ blacklist = self.create_blacklist(fixture=0)
+ self.policy({'update_blacklist': '@'})
+
+ url = ('/blacklists/%s' % blacklist['id'])
+
+ # doing each pattern individually so upon error one can trace
+ # back to the exact line number
+ body = {'pattern': ''}
+ self._assert_exception(
+ 'invalid_object', 400, self.client.patch_json, url, body,
+ status=400)
+
+ body = {'pattern': '#(*&^%$%$#@$'}
+ self._assert_exception(
+ 'invalid_object', 400, self.client.patch_json, url, body,
+ status=400)
+
+ body = {'pattern': 'a' * 1000}
+ self._assert_exception(
+ 'invalid_object', 400, self.client.patch_json, url, body,
+ status=400)
diff --git a/designate/tests/test_central/test_service.py b/designate/tests/test_central/test_service.py
index 2c6f8cf5..5db47b4f 100644
--- a/designate/tests/test_central/test_service.py
+++ b/designate/tests/test_central/test_service.py
@@ -33,9 +33,10 @@ from oslo_messaging.rpc import dispatcher as rpc_dispatcher
from designate import exceptions
from designate import objects
from designate.mdns import rpcapi as mdns_api
+from designate.storage.impl_sqlalchemy import tables
from designate.tests import fixtures
from designate.tests.test_central import CentralTestCase
-from designate.storage.impl_sqlalchemy import tables
+from designate import utils
LOG = logging.getLogger(__name__)
@@ -3534,7 +3535,7 @@ class CentralServiceTest(CentralTestCase):
# Zone Import Tests
def test_create_zone_import(self):
# Create a Zone Import
- context = self.get_context()
+ context = self.get_context(project_id=utils.generate_uuid())
request_body = self.get_zonefile_fixture()
zone_import = self.central_service.create_zone_import(context,
request_body)
@@ -3548,7 +3549,7 @@ class CentralServiceTest(CentralTestCase):
self.wait_for_import(zone_import.id)
def test_find_zone_imports(self):
- context = self.get_context()
+ context = self.get_context(project_id=utils.generate_uuid())
# Ensure we have no zone_imports to start with.
zone_imports = self.central_service.find_zone_imports(
@@ -3565,7 +3566,7 @@ class CentralServiceTest(CentralTestCase):
# Ensure we can retrieve the newly created zone_import
zone_imports = self.central_service.find_zone_imports(
- self.admin_context)
+ self.admin_context_all_tenants)
self.assertEqual(1, len(zone_imports))
# Create a second zone_import
@@ -3578,14 +3579,14 @@ class CentralServiceTest(CentralTestCase):
# Ensure we can retrieve both zone_imports
zone_imports = self.central_service.find_zone_imports(
- self.admin_context)
+ self.admin_context_all_tenants)
self.assertEqual(2, len(zone_imports))
self.assertEqual('COMPLETE', zone_imports[0].status)
self.assertEqual('COMPLETE', zone_imports[1].status)
def test_get_zone_import(self):
# Create a Zone Import
- context = self.get_context()
+ context = self.get_context(project_id=utils.generate_uuid())
request_body = self.get_zonefile_fixture()
zone_import = self.central_service.create_zone_import(
context, request_body)
@@ -3595,7 +3596,7 @@ class CentralServiceTest(CentralTestCase):
# Retrieve it, and ensure it's the same
zone_import = self.central_service.get_zone_import(
- self.admin_context, zone_import.id)
+ self.admin_context_all_tenants, zone_import.id)
self.assertEqual(zone_import.id, zone_import['id'])
self.assertEqual(zone_import.status, zone_import['status'])
@@ -3603,7 +3604,7 @@ class CentralServiceTest(CentralTestCase):
def test_update_zone_import(self):
# Create a Zone Import
- context = self.get_context()
+ context = self.get_context(project_id=utils.generate_uuid())
request_body = self.get_zonefile_fixture()
zone_import = self.central_service.create_zone_import(
context, request_body)
@@ -3615,7 +3616,7 @@ class CentralServiceTest(CentralTestCase):
# Perform the update
zone_import = self.central_service.update_zone_import(
- self.admin_context, zone_import)
+ self.admin_context_all_tenants, zone_import)
# Fetch the zone_import again
zone_import = self.central_service.get_zone_import(context,
@@ -3626,7 +3627,7 @@ class CentralServiceTest(CentralTestCase):
def test_delete_zone_import(self):
# Create a Zone Import
- context = self.get_context()
+ context = self.get_context(project_id=utils.generate_uuid())
request_body = self.get_zonefile_fixture()
zone_import = self.central_service.create_zone_import(
context, request_body)
diff --git a/designate/tests/unit/test_central/test_basic.py b/designate/tests/unit/test_central/test_basic.py
index 688ca340..e75922e3 100644
--- a/designate/tests/unit/test_central/test_basic.py
+++ b/designate/tests/unit/test_central/test_basic.py
@@ -256,6 +256,7 @@ class CentralBasic(TestCase):
'set_rules',
'init',
'check',
+ 'enforce_new_defaults',
])
designate.central.service.quota = mock.NonCallableMock(spec_set=[
@@ -932,7 +933,7 @@ class CentralZoneTestCase(CentralBasic):
n, ctx, target = designate.central.service.policy.check.call_args[0]
self.assertEqual(CentralZoneTestCase.zone__id, target['zone_id'])
self.assertEqual('foo', target['zone_name'])
- self.assertEqual('2', target['tenant_id'])
+ self.assertEqual('2', target['project_id'])
def test_get_zone_servers(self):
self.service.storage.get_zone.return_value = RoObject(
@@ -995,6 +996,7 @@ class CentralZoneTestCase(CentralBasic):
'set_rules',
'init',
'check',
+ 'enforce_new_defaults',
])
self.context.abandon = True
self.service.storage.count_zones.return_value = 0
@@ -1187,7 +1189,7 @@ class CentralZoneTestCase(CentralBasic):
'zone_id': CentralZoneTestCase.zone__id_2,
'zone_name': 'example.org.',
'recordset_id': CentralZoneTestCase.recordset__id,
- 'tenant_id': '2'}, target)
+ 'project_id': '2'}, target)
def test_find_recordsets(self):
self.context = mock.Mock()
@@ -1196,7 +1198,7 @@ class CentralZoneTestCase(CentralBasic):
self.assertTrue(self.service.storage.find_recordsets.called)
n, ctx, target = designate.central.service.policy.check.call_args[0]
self.assertEqual('find_recordsets', n)
- self.assertEqual({'tenant_id': 't'}, target)
+ self.assertEqual({'project_id': 't'}, target)
def test_find_recordset(self):
self.context = mock.Mock()
@@ -1205,7 +1207,7 @@ class CentralZoneTestCase(CentralBasic):
self.assertTrue(self.service.storage.find_recordset.called)
n, ctx, target = designate.central.service.policy.check.call_args[0]
self.assertEqual('find_recordset', n)
- self.assertEqual({'tenant_id': 't'}, target)
+ self.assertEqual({'project_id': 't'}, target)
def test_update_recordset_fail_on_changes(self):
self.service.storage.get_zone.return_value = RoObject()
@@ -1298,7 +1300,7 @@ class CentralZoneTestCase(CentralBasic):
'zone_name': 'example.org.',
'zone_type': 'foo',
'recordset_id': '9c85d9b0-1e9d-4e99-aede-a06664f1af2e',
- 'tenant_id': '2'}, target)
+ 'project_id': '2'}, target)
def test__update_recordset_in_storage(self):
recordset = mock.Mock()
@@ -1532,7 +1534,7 @@ class CentralZoneTestCase(CentralBasic):
self.service.count_recordsets(self.context)
n, ctx, target = designate.central.service.policy.check.call_args[0]
self.assertEqual('count_recordsets', n)
- self.assertEqual({'tenant_id': None}, target)
+ self.assertEqual({'project_id': None}, target)
self.assertEqual(
{},
self.service.storage.count_recordsets.call_args[0][1]
@@ -1587,7 +1589,7 @@ class CentralZoneTestCase(CentralBasic):
'zone_type': 'foo',
'recordset_id': CentralZoneTestCase.recordset__id,
'recordset_name': 'rs',
- 'tenant_id': '2'}, target)
+ 'project_id': '2'}, target)
def test_create_record_worker(self):
self._test_create_record()
@@ -1689,7 +1691,7 @@ class CentralZoneTestCase(CentralBasic):
'record_id': CentralZoneTestCase.record__id,
'recordset_id': CentralZoneTestCase.recordset__id_2,
'recordset_name': 'foo',
- 'tenant_id': 2}, target)
+ 'project_id': 2}, target)
def test_update_record_fail_on_changes(self):
self.service.storage.get_zone.return_value = RoObject(
@@ -1789,7 +1791,7 @@ class CentralZoneTestCase(CentralBasic):
'record_id': 'abc12a-1e9d-4e99-aede-a06664f1af2e',
'recordset_id': 'abc12a-1e9d-4e99-aede-a06664f1af2e',
'recordset_name': 'rsn',
- 'tenant_id': 'tid'}, target)
+ 'project_id': 'tid'}, target)
def test__update_record_in_storage(self):
self.service._update_zone_in_storage = mock.Mock()
@@ -1893,7 +1895,7 @@ class CentralZoneTestCase(CentralBasic):
'record_id': CentralZoneTestCase.record__id_2,
'recordset_id': CentralZoneTestCase.recordset__id_2,
'recordset_name': 'rsn',
- 'tenant_id': 'tid'}, target)
+ 'project_id': 'tid'}, target)
def test_delete_record_in_storage(self):
self.service._delete_record_in_storage(
@@ -1911,7 +1913,7 @@ class CentralZoneTestCase(CentralBasic):
self.service.count_records(self.context)
t, ctx, target = designate.central.service.policy.check.call_args[0]
self.assertEqual('count_records', t)
- self.assertEqual({'tenant_id': None}, target)
+ self.assertEqual({'project_id': None}, target)
def test_sync_zones(self):
self.service._sync_zone = mock.Mock()
@@ -1938,7 +1940,7 @@ class CentralZoneTestCase(CentralBasic):
t, ctx, target = designate.central.service.policy.check.call_args[0]
self.assertEqual('diagnostics_sync_zone', t)
- self.assertEqual({'tenant_id': 'tid',
+ self.assertEqual({'project_id': 'tid',
'zone_id': CentralZoneTestCase.zone__id,
'zone_name': 'n'}, target)
@@ -1965,7 +1967,7 @@ class CentralZoneTestCase(CentralBasic):
'record_id': CentralZoneTestCase.record__id,
'recordset_id': CentralZoneTestCase.recordset__id,
'recordset_name': 'n',
- 'tenant_id': 'tid'}, target)
+ 'project_id': 'tid'}, target)
def test_ping(self):
self.service.storage.ping.return_value = True
@@ -2118,7 +2120,7 @@ class CentralZoneExportTests(CentralBasic):
n, ctx, target = designate.central.service.policy.check.call_args[0]
# Check arguments to policy
- self.assertEqual('t', target['tenant_id'])
+ self.assertEqual('t', target['project_id'])
# Check output
self.assertEqual(CentralZoneTestCase.zone__id, out.zone_id)
diff --git a/releasenotes/notes/Fix-to-address-denylist-invalid-patterns-not-being-checked-ec1f1316ccc6cb1d.yaml b/releasenotes/notes/Fix-to-address-denylist-invalid-patterns-not-being-checked-ec1f1316ccc6cb1d.yaml
new file mode 100644
index 00000000..43a3ca94
--- /dev/null
+++ b/releasenotes/notes/Fix-to-address-denylist-invalid-patterns-not-being-checked-ec1f1316ccc6cb1d.yaml
@@ -0,0 +1,16 @@
+---
+fixes:
+ - |
+ Fixes `bug 1934252`_ which ignored invalid denylist patterns. The fix
+ entailed checking the pattern string via regular expression compiler and
+ testing for zero length.
+
+ Previously you could create blacklist/denylist using string that cannot
+ be used either as a regex or as a zone name, for example:
+ patterns = ['', ``'#(*&^%$%$#@$']``
+
+ In addition, the server will return a 400 BadRequest response to an
+ invalid pattern.
+
+ .. _Bug 1934252: https://bugs.launchpad.net/designate/+bug/1934252
+
diff --git a/releasenotes/notes/Support-scoped-tokens-6b7d6052a258cd11.yaml b/releasenotes/notes/Support-scoped-tokens-6b7d6052a258cd11.yaml
new file mode 100644
index 00000000..3571dbb3
--- /dev/null
+++ b/releasenotes/notes/Support-scoped-tokens-6b7d6052a258cd11.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Adds support for keystone default roles and scoped tokens.