Add new default rules and mapping in policy base class

Cyborg Policy Default Refresh is one of the planned blueprints for victoria
release, the specification[0] has been merged in ussuri. To be brief, we need
to do the followings to incorporate authorization scopes into cyborg:
1. Add protection test for all APIs.
   A protection test is similar to an API test, but purely focused on the
   authoritative outcome.In other words, protection testing is sufficient when
   we can assert that a user is or isn’t allowed to do or see something. For
   example, users with a reader role on the system or a project shouldn’t be
   able to make writable changes.
2. Add the following applicable seven personas to cyborg and mark old
   ones as deprecated roles:
   * project reader
   * project member
   * project admin
   * system reader
   * system admin
   * system admin or owner
   * system or project reader
3. Rewrite check string(authorization rules) using new personas for all APIs
4. Update policy documentation on cyborg-doc page

This patch refreshed cyborg default RBAC policy to scoped RBAC policy, and
reorganized the policy framework into a more logical way:
    1) added seven personas to basic policies and marked legacy roles
       as deprecated ones.
    2) extract API_policies from policy.py to indenpendent policy files
    3) extract authorize_wsgi.py out from policy.py

[0]https://specs.openstack.org/openstack/cyborg-specs/specs/ussuri/approved/policy-defaults-refresh.html

Story: 2007024
Task: 40835

Change-Id: I948d0202ddcd82a532c4de2c1850893cbfaf003d
This commit is contained in:
Yumeng Bao 2020-07-09 13:48:04 +08:00
parent 27d5a7de39
commit 270d12e9a8
10 changed files with 389 additions and 222 deletions

View File

@ -26,10 +26,10 @@ from cyborg.api.controllers import types
from cyborg.api.controllers.v2 import utils
from cyborg.api.controllers.v2 import versions
from cyborg.api import expose
from cyborg.common import authorize_wsgi
from cyborg.common import constants
from cyborg.common import exception
from cyborg.common.i18n import _
from cyborg.common import policy
from cyborg import objects
LOG = log.getLogger(__name__)
@ -115,7 +115,7 @@ class ARQsController(base.CyborgController):
except Exception:
return None
@policy.authorize_wsgi("cyborg:arq", "create", False)
@authorize_wsgi.authorize_wsgi("cyborg:arq", "create", False)
@expose.expose(ARQCollection, body=types.jsontype,
status_code=http_client.CREATED)
def post(self, req):
@ -169,7 +169,7 @@ class ARQsController(base.CyborgController):
LOG.info('[arqs] post returned: %s', ret)
return ret
@policy.authorize_wsgi("cyborg:arq", "get_one")
@authorize_wsgi.authorize_wsgi("cyborg:arq", "get_one")
@expose.expose(ARQ, wtypes.text)
def get_one(self, uuid):
"""Get a single ARQ by UUID."""
@ -177,7 +177,7 @@ class ARQsController(base.CyborgController):
extarq = objects.ExtARQ.get(context, uuid)
return ARQ.convert_with_links(extarq.arq)
@policy.authorize_wsgi("cyborg:arq", "get_all", False)
@authorize_wsgi.authorize_wsgi("cyborg:arq", "get_all", False)
@expose.expose(ARQCollection, wtypes.text, types.uuid)
def get_all(self, bind_state=None, instance=None):
"""Retrieve a list of arqs."""
@ -216,7 +216,7 @@ class ARQsController(base.CyborgController):
LOG.info('[arqs:get_all] Returned: %s', ret)
return ret
@policy.authorize_wsgi("cyborg:arq", "delete", False)
@authorize_wsgi.authorize_wsgi("cyborg:arq", "delete", False)
@expose.expose(None, wtypes.text, wtypes.text,
status_code=http_client.NO_CONTENT)
def delete(self, arqs=None, instance=None):
@ -304,7 +304,7 @@ class ARQsController(base.CyborgController):
reason = msg.format(instance_uuid)
raise exception.PatchError(reason=reason)
@policy.authorize_wsgi("cyborg:arq", "update", False)
@authorize_wsgi.authorize_wsgi("cyborg:arq", "update", False)
@expose.expose(None, body=types.jsontype,
status_code=http_client.ACCEPTED)
def patch(self, patch_list):

View File

@ -23,7 +23,7 @@ from cyborg.api.controllers import base
from cyborg.api.controllers import link
from cyborg.api.controllers import types
from cyborg.api import expose
from cyborg.common import policy
from cyborg.common import authorize_wsgi
from cyborg import objects
@ -110,7 +110,7 @@ class DeployablesController(base.CyborgController,
DeployableCollection):
"""REST controller for Deployables."""
@policy.authorize_wsgi("cyborg:deployable", "get_one")
@authorize_wsgi.authorize_wsgi("cyborg:deployable", "get_one")
@expose.expose(Deployable, types.uuid)
def get_one(self, uuid):
"""Retrieve information about the given deployable.
@ -120,7 +120,7 @@ class DeployablesController(base.CyborgController,
obj_dep = objects.Deployable.get(pecan.request.context, uuid)
return self.convert_with_link(obj_dep)
@policy.authorize_wsgi("cyborg:deployable", "get_all")
@authorize_wsgi.authorize_wsgi("cyborg:deployable", "get_all")
@expose.expose(DeployableCollection, wtypes.ArrayType(types.FilterType))
def get_all(self, filters=None):
"""Retrieve a list of deployables.

View File

@ -26,8 +26,8 @@ from cyborg.api.controllers import base
from cyborg.api.controllers import link
from cyborg.api.controllers import types
from cyborg.api import expose
from cyborg.common import authorize_wsgi
from cyborg.common import exception
from cyborg.common import policy
from cyborg import objects
LOG = log.getLogger(__name__)
@ -83,7 +83,7 @@ class DeviceProfilesController(base.CyborgController,
DeviceProfileCollection):
"""REST controller for Device Profiles."""
@policy.authorize_wsgi("cyborg:device_profile", "create", False)
@authorize_wsgi.authorize_wsgi("cyborg:device_profile", "create", False)
@expose.expose('json', body=types.jsontype,
status_code=http_client.CREATED)
def post(self, req_devprof_list):
@ -163,7 +163,7 @@ class DeviceProfilesController(base.CyborgController,
return api_obj_devprofs
@policy.authorize_wsgi("cyborg:device_profile", "get_all", False)
@authorize_wsgi.authorize_wsgi("cyborg:device_profile", "get_all", False)
@expose.expose('json', wtypes.text)
def get_all(self, name=None):
"""Retrieve a list of device profiles."""
@ -180,7 +180,7 @@ class DeviceProfilesController(base.CyborgController,
return wsme.api.Response(ret, status_code=http_client.OK,
return_type=wsme.types.DictType)
@policy.authorize_wsgi("cyborg:device_profile", "get_one")
@authorize_wsgi.authorize_wsgi("cyborg:device_profile", "get_one")
@expose.expose('json', wtypes.text)
def get_one(self, uuid):
"""Retrieve a single device profile by uuid."""
@ -201,7 +201,7 @@ class DeviceProfilesController(base.CyborgController,
return wsme.api.Response(ret, status_code=http_client.OK,
return_type=wsme.types.DictType)
@policy.authorize_wsgi("cyborg:device_profile", "delete")
@authorize_wsgi.authorize_wsgi("cyborg:device_profile", "delete")
@expose.expose(None, wtypes.text, status_code=http_client.NO_CONTENT)
def delete(self, value):
"""Delete one or more device_profiles.

View File

@ -23,7 +23,7 @@ from cyborg.api.controllers import base
from cyborg.api.controllers import link
from cyborg.api.controllers import types
from cyborg.api import expose
from cyborg.common import policy
from cyborg.common import authorize_wsgi
from cyborg import objects
LOG = log.getLogger(__name__)
@ -93,7 +93,7 @@ class DeviceCollection(base.APIBase):
class DevicesController(base.CyborgController):
"""REST controller for Devices."""
@policy.authorize_wsgi("cyborg:device", "get_one")
@authorize_wsgi.authorize_wsgi("cyborg:device", "get_one")
@expose.expose(Device, wtypes.text)
def get_one(self, uuid):
"""Get a single device by UUID.
@ -103,7 +103,7 @@ class DevicesController(base.CyborgController):
device = objects.Device.get(context, uuid)
return Device.convert_with_links(device)
@policy.authorize_wsgi("cyborg:device", "get_all", False)
@authorize_wsgi.authorize_wsgi("cyborg:device", "get_all", False)
@expose.expose(DeviceCollection, wtypes.text, wtypes.text, wtypes.text,
wtypes.ArrayType(types.FilterType))
def get_all(self, type=None, vendor=None, hostname=None, filters=None):

View File

@ -0,0 +1,167 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Policy Authorize Engine For Cyborg."""
import functools
import sys
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log
from oslo_policy import policy
from oslo_versionedobjects import base as object_base
import pecan
import wsme
from cyborg.common import exception
from cyborg import policies
_ENFORCER = None
CONF = cfg.CONF
LOG = log.getLogger(__name__)
@lockutils.synchronized('policy_enforcer', 'cyborg-')
def init_enforcer(policy_file=None, rules=None,
default_rule=None, use_conf=True):
"""Synchronously initializes the policy enforcer
:param policy_file: Custom policy file to use, if none is specified,
`CONF.oslo_policy.policy_file` will be used.
:param rules: Default dictionary / Rules to use. It will be
considered just in the first instantiation.
:param default_rule: Default rule to use,
CONF.oslo_policy.policy_default_rule will
be used if none is specified.
:param use_conf: Whether to load rules from config file.
"""
global _ENFORCER
if _ENFORCER:
return
# NOTE: Register defaults for policy-in-code here so that they are
# loaded exactly once - when this module-global is initialized.
# Defining these in the relevant API modules won't work
# because API classes lack singletons and don't use globals.
_ENFORCER = policy.Enforcer(CONF, policy_file=policy_file,
rules=rules,
default_rule=default_rule,
use_conf=use_conf)
_ENFORCER.register_defaults(policies.list_policies())
def get_enforcer():
"""Provides access to the single accelerator of policy enforcer."""
global _ENFORCER
if not _ENFORCER:
init_enforcer()
return _ENFORCER
# NOTE: We can't call these methods from within decorators because the
# 'target' and 'creds' parameter must be fetched from the call time
# context-local pecan.request magic variable, but decorators are compiled
# at module-load time.
def authorize(rule, target, creds, do_raise=False, *args, **kwargs):
"""A shortcut for policy.Enforcer.authorize()
Checks authorization of a rule against the target and credentials, and
raises an exception if the rule is not defined.
"""
enforcer = get_enforcer()
try:
return enforcer.authorize(rule, target, creds, do_raise=do_raise,
*args, **kwargs)
except policy.PolicyNotAuthorized:
raise exception.HTTPForbidden(resource=rule)
# This decorator MUST appear first (the outermost decorator)
# on an API method for it to work correctly
def authorize_wsgi(api_name, act=None, need_target=True):
"""This is a decorator to simplify wsgi action policy rule check.
:param api_name: The collection name to be evaluate.
:param act: The function name of wsgi action.
:param need_target: Whether need target for authorization. Such as,
when create some resource , maybe target is not needed.
example:
from cyborg.common import authorize_wsgi
class AcceleratorController(rest.RestController):
....
@authorize_wsgi.authorize_wsgi("cyborg:accelerator",
"create", False)
@wsme_pecan.wsexpose(Accelerator, body=types.jsontype,
status_code=http_client.CREATED)
def post(self, values):
...
"""
def wraper(fn):
action = '%s:%s' % (api_name, act or fn.__name__)
# In this authorize method, we return a dict data when authorization
# fails or exception comes out. Maybe we can consider to use
# wsme.api.Response in future.
def return_error(resp_status):
exception_info = sys.exc_info()
orig_exception = exception_info[1]
orig_code = getattr(orig_exception, 'code', None)
pecan.response.status = orig_code or resp_status
data = wsme.api.format_exception(
exception_info,
pecan.conf.get('wsme', {}).get('debug', False)
)
del exception_info
return data
@functools.wraps(fn)
def handle(self, *args, **kwargs):
context = pecan.request.context
credentials = context.to_policy_values()
credentials['is_admin'] = context.is_admin
target = {}
# maybe we can pass "_get_resource" to authorize_wsgi
if need_target and hasattr(self, "_get_resource"):
try:
resource = getattr(self, "_get_resource")(*args, **kwargs)
# just support object, other type will just keep target as
# empty, then follow authorize method will fail and throw
# an exception
if isinstance(resource,
object_base.VersionedObjectDictCompat):
target = {'project_id': resource.project_id,
'user_id': resource.user_id}
except Exception:
return return_error(500)
elif need_target:
# if developer do not set _get_resource, just set target as
# empty, then follow authorize method will fail and throw an
# exception
target = {}
else:
# for create method, before resource exsites, we can check the
# the credentials with itself.
target = {'project_id': context.tenant,
'user_id': context.user}
try:
authorize(action, target, credentials, do_raise=True)
except Exception:
return return_error(403)
return fn(self, *args, **kwargs)
return handle
return wraper

View File

@ -13,61 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Policy Engine For Cyborg."""
import functools
import sys
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log
"""legacy old_policies, the following old_policies will be removed once
new policies are implemented.
"""
from oslo_policy import policy
from oslo_versionedobjects import base as object_base
import pecan
import wsme
from cyborg.common import exception
_ENFORCER = None
CONF = cfg.CONF
LOG = log.getLogger(__name__)
default_policies = [
# Legacy setting, don't remove. Likely to be overridden by operators who
# forget to update their policy.json configuration file.
# This gets rolled into the new "is_admin" rule below.
policy.RuleDefault('admin_api',
'role:admin or role:administrator',
description='Legacy rule for cloud admin access'),
# is_public_api is set in the environment from AuthTokenMiddleware
policy.RuleDefault('public_api',
'is_public_api:True',
description='Internal flag for public API routes'),
# The policy check "@" will always accept an access. The empty list
# (``[]``) or the empty string (``""``) is equivalent to the "@"
policy.RuleDefault('allow',
'@',
description='any access will be passed'),
# the policy check "!" will always reject an access.
policy.RuleDefault('deny',
'!',
description='all access will be forbidden'),
policy.RuleDefault('is_admin',
'rule:admin_api',
description='Full read/write API access'),
policy.RuleDefault('admin_or_owner',
'is_admin:True or project_id:%(project_id)s',
description='Admin or owner API access'),
policy.RuleDefault('admin_or_user',
'is_admin:True or user_id:%(user_id)s',
description='Admin or user API access'),
policy.RuleDefault('default',
'rule:admin_or_owner',
description='Default API access rule'),
]
# NOTE: to follow policy-in-code spec, we define defaults for
# the granular policies in code, rather than in policy.json.
# All of these may be overridden by configuration, but we can
@ -136,153 +86,3 @@ fpga_policies = [
'rule:allow',
description='Update fpga records'),
]
def list_policies():
return default_policies \
+ fpga_policies \
+ accelerator_request_policies \
+ device_profile_policies \
+ device_policies \
+ deployable_policies
@lockutils.synchronized('policy_enforcer', 'cyborg-')
def init_enforcer(policy_file=None, rules=None,
default_rule=None, use_conf=True):
"""Synchronously initializes the policy enforcer
:param policy_file: Custom policy file to use, if none is specified,
`CONF.oslo_policy.policy_file` will be used.
:param rules: Default dictionary / Rules to use. It will be
considered just in the first instantiation.
:param default_rule: Default rule to use,
CONF.oslo_policy.policy_default_rule will
be used if none is specified.
:param use_conf: Whether to load rules from config file.
"""
global _ENFORCER
if _ENFORCER:
return
# NOTE: Register defaults for policy-in-code here so that they are
# loaded exactly once - when this module-global is initialized.
# Defining these in the relevant API modules won't work
# because API classes lack singletons and don't use globals.
_ENFORCER = policy.Enforcer(CONF, policy_file=policy_file,
rules=rules,
default_rule=default_rule,
use_conf=use_conf)
_ENFORCER.register_defaults(list_policies())
def get_enforcer():
"""Provides access to the single accelerator of policy enforcer."""
global _ENFORCER
if not _ENFORCER:
init_enforcer()
return _ENFORCER
# NOTE: We can't call these methods from within decorators because the
# 'target' and 'creds' parameter must be fetched from the call time
# context-local pecan.request magic variable, but decorators are compiled
# at module-load time.
def authorize(rule, target, creds, do_raise=False, *args, **kwargs):
"""A shortcut for policy.Enforcer.authorize()
Checks authorization of a rule against the target and credentials, and
raises an exception if the rule is not defined.
"""
enforcer = get_enforcer()
try:
return enforcer.authorize(rule, target, creds, do_raise=do_raise,
*args, **kwargs)
except policy.PolicyNotAuthorized:
raise exception.HTTPForbidden(resource=rule)
# This decorator MUST appear first (the outermost decorator)
# on an API method for it to work correctly
def authorize_wsgi(api_name, act=None, need_target=True):
"""This is a decorator to simplify wsgi action policy rule check.
:param api_name: The collection name to be evaluate.
:param act: The function name of wsgi action.
:param need_target: Whether need target for authorization. Such as,
when create some resource , maybe target is not needed.
example:
from cyborg.common import policy
class AcceleratorController(rest.RestController):
....
@policy.authorize_wsgi("cyborg:accelerator", "create", False)
@wsme_pecan.wsexpose(Accelerator, body=types.jsontype,
status_code=http_client.CREATED)
def post(self, values):
...
"""
def wraper(fn):
action = '%s:%s' % (api_name, act or fn.__name__)
# In this authorize method, we return a dict data when authorization
# fails or exception comes out. Maybe we can consider to use
# wsme.api.Response in future.
def return_error(resp_status):
exception_info = sys.exc_info()
orig_exception = exception_info[1]
orig_code = getattr(orig_exception, 'code', None)
pecan.response.status = orig_code or resp_status
data = wsme.api.format_exception(
exception_info,
pecan.conf.get('wsme', {}).get('debug', False)
)
del exception_info
return data
@functools.wraps(fn)
def handle(self, *args, **kwargs):
context = pecan.request.context
credentials = context.to_policy_values()
credentials['is_admin'] = context.is_admin
target = {}
# maybe we can pass "_get_resource" to authorize_wsgi
if need_target and hasattr(self, "_get_resource"):
try:
resource = getattr(self, "_get_resource")(*args, **kwargs)
# just support object, other type will just keep target as
# empty, then follow authorize method will fail and throw
# an exception
if isinstance(resource,
object_base.VersionedObjectDictCompat):
target = {'project_id': resource.project_id,
'user_id': resource.user_id}
except Exception:
return return_error(500)
elif need_target:
# if developer do not set _get_resource, just set target as
# empty, then follow authorize method will fail and throw an
# exception
target = {}
else:
# for create method, before resource exsites, we can check the
# the credentials with itself.
target = {'project_id': context.tenant,
'user_id': context.user}
try:
authorize(action, target, credentials, do_raise=True)
except Exception:
return return_error(403)
return fn(self, *args, **kwargs)
return handle
return wraper

View File

@ -0,0 +1,33 @@
# Copyright 2020 ZTE Corporation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import itertools
from cyborg.common import policy as old_policy
from cyborg.policies import base
def list_policies():
return itertools.chain(
base.list_policies(),
# NOTE(yumeng)old_policies will also be loaded before they are replaced
# by new policies
old_policy.device_profile_policies,
old_policy.device_policies,
old_policy.deployable_policies,
old_policy.accelerator_request_policies,
old_policy.fpga_policies,
)

167
cyborg/policies/base.py Normal file
View File

@ -0,0 +1,167 @@
# Copyright 2020 ZTE Corporation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import versionutils
from oslo_policy import policy
# All legacy policy and new policy mapping for all V2 APIs can be found
# here:https://wiki.openstack.org/wiki/Cyborg/Policy
# TODO(yumeng) Special string ``system_scope:all``
# We are explicitly setting system_scope:all in these check strings because
# they provide backwards compatibility in the event a deployment sets
# ``cyborg.conf [oslo_policy] enforce_scope = False``, which the default.
# Otherwise, this might open up APIs to be more permissive unintentionally if a
# deployment isn't enforcing scope. For example, the new rule for action
# 'cyborg:device_profile:create' will be System Scoped Admin with
# ``role:admin`` and scope_type=['system']. However, it would be possible for
# users with the ``admin`` role on a project to access the
# 'cyborg:device_profile:create' until enforce_scope=True is set by default.
# Once cyborg defaults ``cyborg.conf [oslo_policy] enforce_scope = True``,
# the the ``system_scope:all`` bits of these check strings
# can be removed since that will be handled automatically by scope_types in
# oslo.policy's RuleDefault objects.
SYSTEM_ADMIN = 'rule:system_admin_api'
SYSTEM_READER = 'rule:system_reader_api'
PROJECT_ADMIN = 'rule:project_admin_api'
PROJECT_MEMBER = 'rule:project_member_api'
PROJECT_READER = 'rule:project_reader_api'
PROJECT_MEMBER_OR_SYSTEM_ADMIN = 'rule:system_admin_or_owner'
PROJECT_READER_OR_SYSTEM_READER = 'rule:system_or_project_reader'
# NOTE(yumeng): Keystone already support implied roles means assignment
# of one role implies the assignment of another. New defaults roles
# `reader`, `member` also has been added in bootstrap. If the bootstrap
# process is re-run, and a `reader`, `member`, or `admin` role already
# exists, a role implication chain will be created: `admin` implies
# `member` implies `reader`.
# For example: If we give access to 'reader' it means the 'admin' and
# 'member' also get access.
# NOTE(yumeng) the rules listed include both old rules and new rules.
# legacy rules list: 'public_api','allow','deny','admin_api','is_admin',
# 'admin_or_owner','admin_or_user'.
# new rules list: system_admin_api,system_reader_api,project_admin_api,
# project_member_api, project_reader_api, system_admin_or_owner,
# system_or_project_reader .
default_policies = [
policy.RuleDefault(
name="system_admin_api",
check_str='role:admin and system_scope:all',
description="Default rule for System Admin APIs."),
policy.RuleDefault(
name="system_reader_api",
check_str="role:reader and system_scope:all",
description="Default rule for System level read only APIs."),
policy.RuleDefault(
name="project_admin_api",
check_str="role:admin and project_id:%(project_id)s",
description="Default rule for Project level admin APIs."),
policy.RuleDefault(
name="project_member_api",
check_str="role:member and project_id:%(project_id)s",
description="Default rule for Project level non admin APIs."),
policy.RuleDefault(
name="project_reader_api",
check_str="role:reader and project_id:%(project_id)s",
description="Default rule for Project level read only APIs."),
policy.RuleDefault(
name="system_admin_or_owner",
check_str="rule:system_admin_api or rule:project_member_api",
description="Default rule for system_admin+owner APIs."),
policy.RuleDefault(
name="system_or_project_reader",
check_str="rule:system_reader_api or rule:project_reader_api",
description="Default rule for System+Project read only APIs.")
]
DEPRECATED_REASON = """
Cyborg API policies are introducing new default roles with scope_type
capabilities. We will start to deprecate old policies from WALLABY release,
and are going to ignore all the old policies silently from X release.
Be sure to take these new defaults into consideration if you are
relying on overrides in your deployment for the policy API.
"""
deprecated_default = 'rule:admin_or_owner'
deprecated_is_admin = 'rule:is_admin'
deprecated_default_policies = [
# is_public_api is set in the environment from AuthTokenMiddleware
policy.RuleDefault(
name='public_api',
check_str='is_public_api:True',
description='legacy rule of Internal flag for public API routes',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
# The policy check "@" will always accept an access. The empty list
# (``[]``) or the empty string (``""``) is equivalent to the "@"
policy.RuleDefault(
name='allow',
check_str='@',
description='legacy rule: any access will be passed',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
# the policy check "!" will always reject an access.
policy.RuleDefault(
name='deny',
check_str='!',
description='legacy rule: all access will be forbidden',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
policy.RuleDefault(
name='default',
check_str='rule:admin_or_owner',
description='Legacy rule for default rule',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
policy.RuleDefault(
name='admin_api',
check_str='role:admin or role:administrator',
description='Legacy rule for cloud admin access',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
policy.RuleDefault(
name='is_admin',
check_str='rule:admin_api',
description='Full read/write API access',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
policy.RuleDefault(
name='admin_or_owner',
check_str='is_admin:True or project_id:%(project_id)s',
description='Admin or owner API access',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
policy.RuleDefault(
name='admin_or_user',
check_str='is_admin:True or user_id:%(user_id)s',
description='Admin or user API access',
deprecated_for_removal=True,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY),
]
def list_policies():
return default_policies \
+ deprecated_default_policies

View File

@ -19,7 +19,7 @@ import fixtures
from oslo_config import cfg
from oslo_policy import opts as policy_opts
from cyborg.common import policy as cyborg_policy
from cyborg.common import authorize_wsgi as cyborg_policy
CONF = cfg.CONF

View File

@ -31,7 +31,7 @@ data_files =
[entry_points]
oslo.policy.policies =
cyborg.api = cyborg.common.policy:list_policies
cyborg.api = cyborg.policies:list_policies
console_scripts =
cyborg-api = cyborg.cmd.api:main