Add policies for API access control to watcher project.
Change-Id: Ibdbe494c636dfaeca9cf2ef8724d0dade1f19c7f blueprint: watcher-policies
This commit is contained in:
parent
6f2c82316c
commit
bc06a7d419
@ -1,5 +1,37 @@
|
||||
{
|
||||
"admin_api": "role:admin or role:administrator",
|
||||
"show_password": "!",
|
||||
"default": "rule:admin_api"
|
||||
"default": "rule:admin_api",
|
||||
|
||||
"action:detail": "rule:default",
|
||||
"action:get": "rule:default",
|
||||
"action:get_all": "rule:default",
|
||||
|
||||
"action_plan:delete": "rule:default",
|
||||
"action_plan:detail": "rule:default",
|
||||
"action_plan:get": "rule:default",
|
||||
"action_plan:get_all": "rule:default",
|
||||
"action_plan:update": "rule:default",
|
||||
|
||||
"audit:create": "rule:default",
|
||||
"audit:delete": "rule:default",
|
||||
"audit:detail": "rule:default",
|
||||
"audit:get": "rule:default",
|
||||
"audit:get_all": "rule:default",
|
||||
"audit:update": "rule:default",
|
||||
|
||||
"audit_template:create": "rule:default",
|
||||
"audit_template:delete": "rule:default",
|
||||
"audit_template:detail": "rule:default",
|
||||
"audit_template:get": "rule:default",
|
||||
"audit_template:get_all": "rule:default",
|
||||
"audit_template:update": "rule:default",
|
||||
|
||||
"goal:detail": "rule:default",
|
||||
"goal:get": "rule:default",
|
||||
"goal:get_all": "rule:default",
|
||||
|
||||
"strategy:detail": "rule:default",
|
||||
"strategy:get": "rule:default",
|
||||
"strategy:get_all": "rule:default"
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ from watcher.api.controllers.v1 import collection
|
||||
from watcher.api.controllers.v1 import types
|
||||
from watcher.api.controllers.v1 import utils as api_utils
|
||||
from watcher.common import exception
|
||||
from watcher.common import policy
|
||||
from watcher import objects
|
||||
|
||||
|
||||
@ -303,6 +304,10 @@ class ActionsController(rest.RestController):
|
||||
:param audit_uuid: Optional UUID of an audit,
|
||||
to get only actions for that audit.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'action:get_all',
|
||||
action='action:get_all')
|
||||
|
||||
if action_plan_uuid and audit_uuid:
|
||||
raise exception.ActionFilterCombinationProhibited
|
||||
|
||||
@ -327,6 +332,10 @@ class ActionsController(rest.RestController):
|
||||
:param audit_uuid: Optional UUID of an audit,
|
||||
to get only actions for that audit.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'action:detail',
|
||||
action='action:detail')
|
||||
|
||||
# NOTE(lucasagomes): /detail should only work agaist collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "actions":
|
||||
@ -350,8 +359,10 @@ class ActionsController(rest.RestController):
|
||||
if self.from_actions:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
action = objects.Action.get_by_uuid(pecan.request.context,
|
||||
action_uuid)
|
||||
context = pecan.request.context
|
||||
action = api_utils.get_resource('Action', action_uuid)
|
||||
policy.enforce(context, 'action:get', action, action='action:get')
|
||||
|
||||
return Action.convert_with_links(action)
|
||||
|
||||
@wsme_pecan.wsexpose(Action, body=Action, status_code=201)
|
||||
|
@ -72,6 +72,7 @@ from watcher.api.controllers.v1 import types
|
||||
from watcher.api.controllers.v1 import utils as api_utils
|
||||
from watcher.applier import rpcapi
|
||||
from watcher.common import exception
|
||||
from watcher.common import policy
|
||||
from watcher import objects
|
||||
from watcher.objects import action_plan as ap_objects
|
||||
|
||||
@ -358,6 +359,10 @@ class ActionPlansController(rest.RestController):
|
||||
:param audit_uuid: Optional UUID of an audit, to get only actions
|
||||
for that audit.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'action_plan:get_all',
|
||||
action='action_plan:get_all')
|
||||
|
||||
return self._get_action_plans_collection(
|
||||
marker, limit, sort_key, sort_dir, audit_uuid=audit_uuid)
|
||||
|
||||
@ -374,6 +379,10 @@ class ActionPlansController(rest.RestController):
|
||||
:param audit_uuid: Optional UUID of an audit, to get only actions
|
||||
for that audit.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'action_plan:detail',
|
||||
action='action_plan:detail')
|
||||
|
||||
# NOTE(lucasagomes): /detail should only work agaist collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "action_plans":
|
||||
@ -395,8 +404,11 @@ class ActionPlansController(rest.RestController):
|
||||
if self.from_actionsPlans:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
action_plan = objects.ActionPlan.get_by_uuid(
|
||||
pecan.request.context, action_plan_uuid)
|
||||
context = pecan.request.context
|
||||
action_plan = api_utils.get_resource('ActionPlan', action_plan_uuid)
|
||||
policy.enforce(
|
||||
context, 'action_plan:get', action_plan, action='action_plan:get')
|
||||
|
||||
return ActionPlan.convert_with_links(action_plan)
|
||||
|
||||
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)
|
||||
@ -405,11 +417,12 @@ class ActionPlansController(rest.RestController):
|
||||
|
||||
:param action_plan_uuid: UUID of a action.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
action_plan = api_utils.get_resource('ActionPlan', action_plan_uuid)
|
||||
policy.enforce(context, 'action_plan:delete', action_plan,
|
||||
action='action_plan:delete')
|
||||
|
||||
action_plan_to_delete = objects.ActionPlan.get_by_uuid(
|
||||
pecan.request.context,
|
||||
action_plan_uuid)
|
||||
action_plan_to_delete.soft_delete()
|
||||
action_plan.soft_delete()
|
||||
|
||||
@wsme.validate(types.uuid, [ActionPlanPatchType])
|
||||
@wsme_pecan.wsexpose(ActionPlan, types.uuid,
|
||||
@ -424,9 +437,12 @@ class ActionPlansController(rest.RestController):
|
||||
if self.from_actionsPlans:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
action_plan_to_update = objects.ActionPlan.get_by_uuid(
|
||||
pecan.request.context,
|
||||
action_plan_uuid)
|
||||
context = pecan.request.context
|
||||
action_plan_to_update = api_utils.get_resource('ActionPlan',
|
||||
action_plan_uuid)
|
||||
policy.enforce(context, 'action_plan:update', action_plan_to_update,
|
||||
action='action_plan:update')
|
||||
|
||||
try:
|
||||
action_plan_dict = action_plan_to_update.as_dict()
|
||||
action_plan = ActionPlan(**api_utils.apply_jsonpatch(
|
||||
|
@ -44,6 +44,7 @@ from watcher.api.controllers.v1 import collection
|
||||
from watcher.api.controllers.v1 import types
|
||||
from watcher.api.controllers.v1 import utils as api_utils
|
||||
from watcher.common import exception
|
||||
from watcher.common import policy
|
||||
from watcher.common import utils
|
||||
from watcher.decision_engine import rpcapi
|
||||
from watcher import objects
|
||||
@ -308,6 +309,9 @@ class AuditsController(rest.RestController):
|
||||
:param audit_template: Optional UUID or name of an audit
|
||||
template, to get only audits for that audit template.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'audit:get_all',
|
||||
action='audit:get_all')
|
||||
return self._get_audits_collection(marker, limit, sort_key,
|
||||
sort_dir,
|
||||
audit_template=audit_template)
|
||||
@ -323,6 +327,9 @@ class AuditsController(rest.RestController):
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'audit:detail',
|
||||
action='audit:detail')
|
||||
# NOTE(lucasagomes): /detail should only work agaist collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "audits":
|
||||
@ -343,8 +350,10 @@ class AuditsController(rest.RestController):
|
||||
if self.from_audits:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
rpc_audit = objects.Audit.get_by_uuid(pecan.request.context,
|
||||
audit_uuid)
|
||||
context = pecan.request.context
|
||||
rpc_audit = api_utils.get_resource('Audit', audit_uuid)
|
||||
policy.enforce(context, 'audit:get', rpc_audit, action='audit:get')
|
||||
|
||||
return Audit.convert_with_links(rpc_audit)
|
||||
|
||||
@wsme_pecan.wsexpose(Audit, body=AuditPostType, status_code=201)
|
||||
@ -353,6 +362,10 @@ class AuditsController(rest.RestController):
|
||||
|
||||
:param audit_p: a audit within the request body.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'audit:create',
|
||||
action='audit:create')
|
||||
|
||||
audit = audit_p.as_audit()
|
||||
if self.from_audits:
|
||||
raise exception.OperationNotPermitted
|
||||
@ -388,6 +401,12 @@ class AuditsController(rest.RestController):
|
||||
if self.from_audits:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
context = pecan.request.context
|
||||
audit_to_update = api_utils.get_resource('Audit',
|
||||
audit_uuid)
|
||||
policy.enforce(context, 'audit:update', audit_to_update,
|
||||
action='audit:update')
|
||||
|
||||
audit_to_update = objects.Audit.get_by_uuid(pecan.request.context,
|
||||
audit_uuid)
|
||||
try:
|
||||
@ -417,8 +436,9 @@ class AuditsController(rest.RestController):
|
||||
|
||||
:param audit_uuid: UUID of a audit.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
audit_to_delete = api_utils.get_resource('Audit', audit_uuid)
|
||||
policy.enforce(context, 'audit:update', audit_to_delete,
|
||||
action='audit:update')
|
||||
|
||||
audit_to_delete = objects.Audit.get_by_uuid(
|
||||
pecan.request.context,
|
||||
audit_uuid)
|
||||
audit_to_delete.soft_delete()
|
||||
|
@ -64,6 +64,7 @@ from watcher.api.controllers.v1 import types
|
||||
from watcher.api.controllers.v1 import utils as api_utils
|
||||
from watcher.common import context as context_utils
|
||||
from watcher.common import exception
|
||||
from watcher.common import policy
|
||||
from watcher.common import utils as common_utils
|
||||
from watcher import objects
|
||||
|
||||
@ -493,6 +494,9 @@ class AuditTemplatesController(rest.RestController):
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'audit_template:get_all',
|
||||
action='audit_template:get_all')
|
||||
filters = {}
|
||||
if goal:
|
||||
if common_utils.is_uuid_like(goal):
|
||||
@ -522,6 +526,10 @@ class AuditTemplatesController(rest.RestController):
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'audit_template:detail',
|
||||
action='audit_template:detail')
|
||||
|
||||
# NOTE(lucasagomes): /detail should only work agaist collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "audit_templates":
|
||||
@ -555,14 +563,11 @@ class AuditTemplatesController(rest.RestController):
|
||||
if self.from_audit_templates:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
if common_utils.is_uuid_like(audit_template):
|
||||
rpc_audit_template = objects.AuditTemplate.get_by_uuid(
|
||||
pecan.request.context,
|
||||
audit_template)
|
||||
else:
|
||||
rpc_audit_template = objects.AuditTemplate.get_by_name(
|
||||
pecan.request.context,
|
||||
audit_template)
|
||||
context = pecan.request.context
|
||||
rpc_audit_template = api_utils.get_resource('AuditTemplate',
|
||||
audit_template)
|
||||
policy.enforce(context, 'audit_template:get', rpc_audit_template,
|
||||
action='audit_template:get')
|
||||
|
||||
return AuditTemplate.convert_with_links(rpc_audit_template)
|
||||
|
||||
@ -578,6 +583,10 @@ class AuditTemplatesController(rest.RestController):
|
||||
if self.from_audit_templates:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'audit_template:create',
|
||||
action='audit_template:create')
|
||||
|
||||
context = pecan.request.context
|
||||
audit_template = audit_template_postdata.as_audit_template()
|
||||
audit_template_dict = audit_template.as_dict()
|
||||
@ -602,6 +611,13 @@ class AuditTemplatesController(rest.RestController):
|
||||
if self.from_audit_templates:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
context = pecan.request.context
|
||||
audit_template_to_update = api_utils.get_resource('AuditTemplate',
|
||||
audit_template)
|
||||
policy.enforce(context, 'audit_template:update',
|
||||
audit_template_to_update,
|
||||
action='audit_template:update')
|
||||
|
||||
if common_utils.is_uuid_like(audit_template):
|
||||
audit_template_to_update = objects.AuditTemplate.get_by_uuid(
|
||||
pecan.request.context,
|
||||
@ -639,14 +655,11 @@ class AuditTemplatesController(rest.RestController):
|
||||
|
||||
:param audit template_uuid: UUID or name of an audit template.
|
||||
"""
|
||||
|
||||
if common_utils.is_uuid_like(audit_template):
|
||||
audit_template_to_delete = objects.AuditTemplate.get_by_uuid(
|
||||
pecan.request.context,
|
||||
audit_template)
|
||||
else:
|
||||
audit_template_to_delete = objects.AuditTemplate.get_by_name(
|
||||
pecan.request.context,
|
||||
audit_template)
|
||||
context = pecan.request.context
|
||||
audit_template_to_delete = api_utils.get_resource('AuditTemplate',
|
||||
audit_template)
|
||||
policy.enforce(context, 'audit_template:update',
|
||||
audit_template_to_delete,
|
||||
action='audit_template:update')
|
||||
|
||||
audit_template_to_delete.soft_delete()
|
||||
|
@ -46,7 +46,7 @@ from watcher.api.controllers.v1 import collection
|
||||
from watcher.api.controllers.v1 import types
|
||||
from watcher.api.controllers.v1 import utils as api_utils
|
||||
from watcher.common import exception
|
||||
from watcher.common import utils as common_utils
|
||||
from watcher.common import policy
|
||||
from watcher import objects
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -201,6 +201,9 @@ class GoalsController(rest.RestController):
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'goal:get_all',
|
||||
action='goal:get_all')
|
||||
return self._get_goals_collection(marker, limit, sort_key, sort_dir)
|
||||
|
||||
@wsme_pecan.wsexpose(GoalCollection, wtypes.text, int,
|
||||
@ -213,6 +216,9 @@ class GoalsController(rest.RestController):
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'goal:detail',
|
||||
action='goal:detail')
|
||||
# NOTE(lucasagomes): /detail should only work agaist collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "goals":
|
||||
@ -231,11 +237,8 @@ class GoalsController(rest.RestController):
|
||||
if self.from_goals:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
if common_utils.is_uuid_like(goal):
|
||||
get_goal_func = objects.Goal.get_by_uuid
|
||||
else:
|
||||
get_goal_func = objects.Goal.get_by_name
|
||||
|
||||
rpc_goal = get_goal_func(pecan.request.context, goal)
|
||||
context = pecan.request.context
|
||||
rpc_goal = api_utils.get_resource('Goal', goal)
|
||||
policy.enforce(context, 'goal:get', rpc_goal, action='goal:get')
|
||||
|
||||
return Goal.convert_with_links(rpc_goal)
|
||||
|
@ -41,6 +41,7 @@ from watcher.api.controllers.v1 import collection
|
||||
from watcher.api.controllers.v1 import types
|
||||
from watcher.api.controllers.v1 import utils as api_utils
|
||||
from watcher.common import exception
|
||||
from watcher.common import policy
|
||||
from watcher.common import utils as common_utils
|
||||
from watcher import objects
|
||||
|
||||
@ -223,6 +224,9 @@ class StrategiesController(rest.RestController):
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'strategy:get_all',
|
||||
action='strategy:get_all')
|
||||
filters = {}
|
||||
if goal:
|
||||
if common_utils.is_uuid_like(goal):
|
||||
@ -245,6 +249,9 @@ class StrategiesController(rest.RestController):
|
||||
:param sort_key: column to sort results by. Default: id.
|
||||
:param sort_dir: direction to sort. "asc" or "desc". Default: asc.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
policy.enforce(context, 'strategy:detail',
|
||||
action='strategy:detail')
|
||||
# NOTE(lucasagomes): /detail should only work agaist collections
|
||||
parent = pecan.request.path.split('/')[:-1][-1]
|
||||
if parent != "strategies":
|
||||
@ -271,11 +278,9 @@ class StrategiesController(rest.RestController):
|
||||
if self.from_strategies:
|
||||
raise exception.OperationNotPermitted
|
||||
|
||||
if common_utils.is_uuid_like(strategy):
|
||||
get_strategy_func = objects.Strategy.get_by_uuid
|
||||
else:
|
||||
get_strategy_func = objects.Strategy.get_by_name
|
||||
|
||||
rpc_strategy = get_strategy_func(pecan.request.context, strategy)
|
||||
context = pecan.request.context
|
||||
rpc_strategy = api_utils.get_resource('Strategy', strategy)
|
||||
policy.enforce(context, 'strategy:get', rpc_strategy,
|
||||
action='strategy:get')
|
||||
|
||||
return Strategy.convert_with_links(rpc_strategy)
|
||||
|
@ -15,9 +15,12 @@
|
||||
|
||||
import jsonpatch
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
import pecan
|
||||
import wsme
|
||||
|
||||
from watcher._i18n import _
|
||||
from watcher import objects
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@ -75,3 +78,19 @@ def as_filters_dict(**filters):
|
||||
filters_dict[filter_name] = filter_value
|
||||
|
||||
return filters_dict
|
||||
|
||||
|
||||
def get_resource(resource, resource_ident):
|
||||
"""Get the resource from the uuid or logical name.
|
||||
|
||||
:param resource: the resource type.
|
||||
:param resource_ident: the UUID or logical name of the resource.
|
||||
|
||||
:returns: The resource.
|
||||
"""
|
||||
resource = getattr(objects, resource)
|
||||
|
||||
if uuidutils.is_uuid_like(resource_ident):
|
||||
return resource.get_by_uuid(pecan.request.context, resource_ident)
|
||||
|
||||
return resource.get_by_name(pecan.request.context, resource_ident)
|
||||
|
@ -56,6 +56,8 @@ class ContextHook(hooks.PecanHook):
|
||||
auth_token = headers.get('X-Auth-Token', auth_token)
|
||||
show_deleted = headers.get('X-Show-Deleted')
|
||||
auth_token_info = state.request.environ.get('keystone.token_info')
|
||||
roles = (headers.get('X-Roles', None) and
|
||||
headers.get('X-Roles').split(','))
|
||||
|
||||
auth_url = headers.get('X-Auth-Url')
|
||||
if auth_url is None:
|
||||
@ -72,7 +74,8 @@ class ContextHook(hooks.PecanHook):
|
||||
project_id=project_id,
|
||||
domain_id=domain_id,
|
||||
domain_name=domain_name,
|
||||
show_deleted=show_deleted)
|
||||
show_deleted=show_deleted,
|
||||
roles=roles)
|
||||
|
||||
|
||||
class NoExceptionTracebackHook(hooks.PecanHook):
|
||||
|
@ -20,7 +20,7 @@ class RequestContext(context.RequestContext):
|
||||
domain_name=None, user=None, user_id=None, project=None,
|
||||
project_id=None, is_admin=False, is_public_api=False,
|
||||
read_only=False, show_deleted=False, request_id=None,
|
||||
trust_id=None, auth_token_info=None):
|
||||
trust_id=None, auth_token_info=None, roles=None):
|
||||
"""Stores several additional request parameters:
|
||||
|
||||
:param domain_id: The ID of the domain.
|
||||
@ -44,7 +44,8 @@ class RequestContext(context.RequestContext):
|
||||
is_admin=is_admin,
|
||||
read_only=read_only,
|
||||
show_deleted=show_deleted,
|
||||
request_id=request_id)
|
||||
request_id=request_id,
|
||||
roles=roles)
|
||||
|
||||
def to_dict(self):
|
||||
return {'auth_token': self.auth_token,
|
||||
@ -61,7 +62,8 @@ class RequestContext(context.RequestContext):
|
||||
'show_deleted': self.show_deleted,
|
||||
'request_id': self.request_id,
|
||||
'trust_id': self.trust_id,
|
||||
'auth_token_info': self.auth_token_info}
|
||||
'auth_token_info': self.auth_token_info,
|
||||
'roles': self.roles}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, values):
|
||||
|
@ -123,6 +123,10 @@ class NotAuthorized(WatcherException):
|
||||
code = 403
|
||||
|
||||
|
||||
class PolicyNotAuthorized(NotAuthorized):
|
||||
msg_fmt = _("Policy doesn't allow %(action)s to be performed.")
|
||||
|
||||
|
||||
class OperationNotPermitted(NotAuthorized):
|
||||
msg_fmt = _("Operation not permitted")
|
||||
|
||||
|
@ -15,55 +15,80 @@
|
||||
|
||||
"""Policy Engine For Watcher."""
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_config import cfg
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
from watcher.common import exception
|
||||
|
||||
|
||||
_ENFORCER = None
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@lockutils.synchronized('policy_enforcer', 'watcher-')
|
||||
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.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.default_rule will
|
||||
be used if none is specified.
|
||||
:param use_conf: Whether to load rules from config file.
|
||||
# we can get a policy enforcer by this init.
|
||||
# oslo policy support change policy rule dynamically.
|
||||
# at present, policy.enforce will reload the policy rules when it checks
|
||||
# the policy files have been touched.
|
||||
def init(policy_file=None, rules=None,
|
||||
default_rule=None, use_conf=True, overwrite=True):
|
||||
"""Init an Enforcer class.
|
||||
|
||||
:param policy_file: Custom policy file to use, if none is
|
||||
specified, ``conf.policy_file`` will be
|
||||
used.
|
||||
:param rules: Default dictionary / Rules to use. It will be
|
||||
considered just in the first instantiation. If
|
||||
:meth:`load_rules` with ``force_reload=True``,
|
||||
:meth:`clear` or :meth:`set_rules` with
|
||||
``overwrite=True`` is called this will be overwritten.
|
||||
:param default_rule: Default rule to use, conf.default_rule will
|
||||
be used if none is specified.
|
||||
:param use_conf: Whether to load rules from cache or config file.
|
||||
:param overwrite: Whether to overwrite existing rules when reload rules
|
||||
from config file.
|
||||
"""
|
||||
global _ENFORCER
|
||||
|
||||
if _ENFORCER:
|
||||
return
|
||||
|
||||
_ENFORCER = policy.Enforcer(policy_file=policy_file,
|
||||
rules=rules,
|
||||
default_rule=default_rule,
|
||||
use_conf=use_conf)
|
||||
|
||||
|
||||
def get_enforcer():
|
||||
"""Provides access to the single instance of Policy enforcer."""
|
||||
|
||||
if not _ENFORCER:
|
||||
init_enforcer()
|
||||
|
||||
# http://docs.openstack.org/developer/oslo.policy/usage.html
|
||||
_ENFORCER = policy.Enforcer(CONF,
|
||||
policy_file=policy_file,
|
||||
rules=rules,
|
||||
default_rule=default_rule,
|
||||
use_conf=use_conf,
|
||||
overwrite=overwrite)
|
||||
return _ENFORCER
|
||||
|
||||
|
||||
def enforce(rule, target, creds, do_raise=False, exc=None, *args, **kwargs):
|
||||
"""A shortcut for policy.Enforcer.enforce()
|
||||
def enforce(context, rule=None, target=None,
|
||||
do_raise=True, exc=None, *args, **kwargs):
|
||||
|
||||
Checks authorization of a rule against the target and credentials.
|
||||
"""Checks authorization of a rule against the target and credentials.
|
||||
|
||||
:param dict context: As much information about the user performing the
|
||||
action as possible.
|
||||
:param rule: The rule to evaluate.
|
||||
:param dict target: As much information about the object being operated
|
||||
on as possible.
|
||||
:param do_raise: Whether to raise an exception or not if check
|
||||
fails.
|
||||
:param exc: Class of the exception to raise if the check fails.
|
||||
Any remaining arguments passed to :meth:`enforce` (both
|
||||
positional and keyword arguments) will be passed to
|
||||
the exception class. If not specified,
|
||||
:class:`PolicyNotAuthorized` will be used.
|
||||
|
||||
:return: ``False`` if the policy does not allow the action and `exc` is
|
||||
not provided; otherwise, returns a value that evaluates to
|
||||
``True``. Note: for rules using the "case" expression, this
|
||||
``True`` value will be the specified string from the
|
||||
expression.
|
||||
"""
|
||||
enforcer = get_enforcer()
|
||||
return enforcer.enforce(rule, target, creds, do_raise=do_raise,
|
||||
exc=exc, *args, **kwargs)
|
||||
enforcer = init()
|
||||
credentials = context.to_dict()
|
||||
if not exc:
|
||||
exc = exception.PolicyNotAuthorized
|
||||
if target is None:
|
||||
target = {'project_id': context.project_id,
|
||||
'user_id': context.user_id}
|
||||
return enforcer.enforce(rule, target, credentials,
|
||||
do_raise=do_raise, exc=exc, *args, **kwargs)
|
||||
|
@ -21,14 +21,17 @@
|
||||
# https://bugs.launchpad.net/watcher/+bug/1255115.
|
||||
|
||||
# NOTE(deva): import auth_token so we can override a config option
|
||||
from keystonemiddleware import auth_token # noqa
|
||||
# import mock
|
||||
|
||||
import copy
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
import pecan
|
||||
import pecan.testing
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
from watcher.api import hooks
|
||||
from watcher.common import context as watcher_context
|
||||
from watcher.tests.db import base
|
||||
|
||||
PATH_PREFIX = '/v1'
|
||||
@ -243,3 +246,41 @@ class FunctionalTest(base.DbTestCase):
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
class AdminRoleTest(base.DbTestCase):
|
||||
def setUp(self):
|
||||
super(AdminRoleTest, self).setUp()
|
||||
token_info = {
|
||||
'token': {
|
||||
'project': {
|
||||
'id': 'admin'
|
||||
},
|
||||
'user': {
|
||||
'id': 'admin'
|
||||
}
|
||||
}
|
||||
}
|
||||
self.context = watcher_context.RequestContext(
|
||||
auth_token_info=token_info,
|
||||
project_id='admin',
|
||||
user_id='admin')
|
||||
|
||||
def make_context(*args, **kwargs):
|
||||
# If context hasn't been constructed with token_info
|
||||
if not kwargs.get('auth_token_info'):
|
||||
kwargs['auth_token_info'] = copy.deepcopy(token_info)
|
||||
if not kwargs.get('project_id'):
|
||||
kwargs['project_id'] = 'admin'
|
||||
if not kwargs.get('user_id'):
|
||||
kwargs['user_id'] = 'admin'
|
||||
if not kwargs.get('roles'):
|
||||
kwargs['roles'] = ['admin']
|
||||
|
||||
context = watcher_context.RequestContext(*args, **kwargs)
|
||||
return watcher_context.RequestContext.from_dict(context.to_dict())
|
||||
|
||||
p = mock.patch.object(watcher_context, 'make_context',
|
||||
side_effect=make_context)
|
||||
self.mock_make_context = p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
@ -11,8 +11,9 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
import json
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from wsme import types as wtypes
|
||||
|
||||
@ -482,3 +483,49 @@ class TestDelete(api_base.FunctionalTest):
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
||||
class TestActionPolicyEnforcement(api_base.FunctionalTest):
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
rule: "rule:defaut"})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(
|
||||
"Policy doesn't allow %s to be performed." % rule,
|
||||
json.loads(response.json['error_message'])['faultstring'])
|
||||
|
||||
def test_policy_disallow_get_all(self):
|
||||
self._common_policy_check(
|
||||
"action:get_all", self.get_json, '/actions',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_get_one(self):
|
||||
action = obj_utils.create_test_action(self.context)
|
||||
self._common_policy_check(
|
||||
"action:get", self.get_json,
|
||||
'/actions/%s' % action.uuid,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_detail(self):
|
||||
self._common_policy_check(
|
||||
"action:detail", self.get_json,
|
||||
'/actions/detail',
|
||||
expect_errors=True)
|
||||
|
||||
|
||||
class TestActionPolicyEnforcementWithAdminContext(TestListAction,
|
||||
api_base.AdminRoleTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestActionPolicyEnforcementWithAdminContext, self).setUp()
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
"action:detail": "rule:default",
|
||||
"action:get": "rule:default",
|
||||
"action:get_all": "rule:default"})
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
import datetime
|
||||
import itertools
|
||||
import json
|
||||
import mock
|
||||
import pecan
|
||||
|
||||
@ -575,3 +576,65 @@ class TestPatchStateTransitionOk(api_base.FunctionalTest):
|
||||
self.assertEqual(self.new_state, updated_ap['state'])
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertEqual(200, response.status_code)
|
||||
|
||||
|
||||
class TestActionPlanPolicyEnforcement(api_base.FunctionalTest):
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
rule: "rule:defaut"})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(
|
||||
"Policy doesn't allow %s to be performed." % rule,
|
||||
json.loads(response.json['error_message'])['faultstring'])
|
||||
|
||||
def test_policy_disallow_get_all(self):
|
||||
self._common_policy_check(
|
||||
"action_plan:get_all", self.get_json, '/action_plans',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_get_one(self):
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
self._common_policy_check(
|
||||
"action_plan:get", self.get_json,
|
||||
'/action_plans/%s' % action_plan.uuid,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_detail(self):
|
||||
self._common_policy_check(
|
||||
"action_plan:detail", self.get_json,
|
||||
'/action_plans/detail',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_update(self):
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
self._common_policy_check(
|
||||
"action_plan:update", self.patch_json,
|
||||
'/action_plans/%s' % action_plan.uuid,
|
||||
[{'path': '/state', 'value': 'DELETED', 'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_delete(self):
|
||||
action_plan = obj_utils.create_test_action_plan(self.context)
|
||||
self._common_policy_check(
|
||||
"action_plan:delete", self.delete,
|
||||
'/action_plans/%s' % action_plan.uuid, expect_errors=True)
|
||||
|
||||
|
||||
class TestActionPlanPolicyEnforcementWithAdminContext(TestListActionPlan,
|
||||
api_base.AdminRoleTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestActionPlanPolicyEnforcementWithAdminContext, self).setUp()
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
"action_plan:delete": "rule:default",
|
||||
"action_plan:detail": "rule:default",
|
||||
"action_plan:get": "rule:default",
|
||||
"action_plan:get_all": "rule:default",
|
||||
"action_plan:update": "rule:default"})
|
||||
|
@ -12,8 +12,9 @@
|
||||
|
||||
import datetime
|
||||
import itertools
|
||||
|
||||
import json
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
from six.moves.urllib import parse as urlparse
|
||||
@ -654,3 +655,81 @@ class TestDelete(api_base.FunctionalTest):
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
||||
class TestAuaditTemplatePolicyEnforcement(api_base.FunctionalTest):
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
rule: "rule:defaut"})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(
|
||||
"Policy doesn't allow %s to be performed." % rule,
|
||||
json.loads(response.json['error_message'])['faultstring'])
|
||||
|
||||
def test_policy_disallow_get_all(self):
|
||||
self._common_policy_check(
|
||||
"audit_template:get_all", self.get_json, '/audit_templates',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_get_one(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
self._common_policy_check(
|
||||
"audit_template:get", self.get_json,
|
||||
'/audit_templates/%s' % audit_template.uuid,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_detail(self):
|
||||
self._common_policy_check(
|
||||
"audit_template:detail", self.get_json,
|
||||
'/audit_templates/detail',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_update(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
self._common_policy_check(
|
||||
"audit_template:update", self.patch_json,
|
||||
'/audit_templates/%s' % audit_template.uuid,
|
||||
[{'path': '/state', 'value': 'SUBMITTED', 'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_create(self):
|
||||
fake_goal1 = obj_utils.get_test_goal(
|
||||
self.context, id=1, uuid=utils.generate_uuid(), name="dummy_1")
|
||||
fake_goal1.create()
|
||||
fake_strategy1 = obj_utils.get_test_strategy(
|
||||
self.context, id=1, uuid=utils.generate_uuid(), name="strategy_1",
|
||||
goal_id=fake_goal1.id)
|
||||
fake_strategy1.create()
|
||||
|
||||
audit_template_dict = post_get_test_audit_template(
|
||||
goal=fake_goal1.uuid,
|
||||
strategy=fake_strategy1.uuid)
|
||||
self._common_policy_check(
|
||||
"audit_template:create", self.post_json, '/audit_templates',
|
||||
audit_template_dict, expect_errors=True)
|
||||
|
||||
def test_policy_disallow_delete(self):
|
||||
audit_template = obj_utils.create_test_audit_template(self.context)
|
||||
self._common_policy_check(
|
||||
"audit_template:delete", self.delete,
|
||||
'/audit_templates/%s' % audit_template.uuid, expect_errors=True)
|
||||
|
||||
|
||||
class TestAuditTemplatePolicyWithAdminContext(TestListAuditTemplate,
|
||||
api_base.AdminRoleTest):
|
||||
def setUp(self):
|
||||
super(TestAuditTemplatePolicyWithAdminContext, self).setUp()
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
"audit_template:create": "rule:default",
|
||||
"audit_template:delete": "rule:default",
|
||||
"audit_template:detail": "rule:default",
|
||||
"audit_template:get": "rule:default",
|
||||
"audit_template:get_all": "rule:default",
|
||||
"audit_template:update": "rule:default"})
|
||||
|
@ -11,8 +11,9 @@
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
import json
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
from wsme import types as wtypes
|
||||
@ -606,3 +607,74 @@ class TestDelete(api_base.FunctionalTest):
|
||||
self.assertEqual(404, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
|
||||
class TestAuaditPolicyEnforcement(api_base.FunctionalTest):
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
rule: "rule:defaut"})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(
|
||||
"Policy doesn't allow %s to be performed." % rule,
|
||||
json.loads(response.json['error_message'])['faultstring'])
|
||||
|
||||
def test_policy_disallow_get_all(self):
|
||||
self._common_policy_check(
|
||||
"audit:get_all", self.get_json, '/audits',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_get_one(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
self._common_policy_check(
|
||||
"audit:get", self.get_json,
|
||||
'/audits/%s' % audit.uuid,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_detail(self):
|
||||
self._common_policy_check(
|
||||
"audit:detail", self.get_json,
|
||||
'/audits/detail',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_update(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
self._common_policy_check(
|
||||
"audit:update", self.patch_json,
|
||||
'/audits/%s' % audit.uuid,
|
||||
[{'path': '/state', 'value': 'SUBMITTED', 'op': 'replace'}],
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_create(self):
|
||||
audit_dict = post_get_test_audit(state=objects.audit.State.PENDING)
|
||||
del audit_dict['uuid']
|
||||
del audit_dict['state']
|
||||
self._common_policy_check(
|
||||
"audit:create", self.post_json, '/audits', audit_dict,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_delete(self):
|
||||
audit = obj_utils.create_test_audit(self.context)
|
||||
self._common_policy_check(
|
||||
"audit:delete", self.delete,
|
||||
'/audits/%s' % audit.uuid, expect_errors=True)
|
||||
|
||||
|
||||
class TestAuditEnforcementWithAdminContext(TestListAudit,
|
||||
api_base.AdminRoleTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAuditEnforcementWithAdminContext, self).setUp()
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
"audit:create": "rule:default",
|
||||
"audit:delete": "rule:default",
|
||||
"audit:detail": "rule:default",
|
||||
"audit:get": "rule:default",
|
||||
"audit:get_all": "rule:default",
|
||||
"audit:update": "rule:default"})
|
||||
|
@ -10,6 +10,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from oslo_config import cfg
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
@ -118,3 +120,49 @@ class TestListGoal(api_base.FunctionalTest):
|
||||
cfg.CONF.set_override('max_limit', 3, 'api', enforce_type=True)
|
||||
response = self.get_json('/goals')
|
||||
self.assertEqual(3, len(response['goals']))
|
||||
|
||||
|
||||
class TestGoalPolicyEnforcement(api_base.FunctionalTest):
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
rule: "rule:defaut"})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(
|
||||
"Policy doesn't allow %s to be performed." % rule,
|
||||
json.loads(response.json['error_message'])['faultstring'])
|
||||
|
||||
def test_policy_disallow_get_all(self):
|
||||
self._common_policy_check(
|
||||
"goal:get_all", self.get_json, '/goals',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_get_one(self):
|
||||
goal = obj_utils.create_test_goal(self.context)
|
||||
self._common_policy_check(
|
||||
"goal:get", self.get_json,
|
||||
'/goals/%s' % goal.uuid,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_detail(self):
|
||||
self._common_policy_check(
|
||||
"goal:detail", self.get_json,
|
||||
'/goals/detail',
|
||||
expect_errors=True)
|
||||
|
||||
|
||||
class TestGoalPolicyEnforcementWithAdminContext(TestListGoal,
|
||||
api_base.AdminRoleTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestGoalPolicyEnforcementWithAdminContext, self).setUp()
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
"goal:detail": "rule:default",
|
||||
"goal:get_all": "rule:default",
|
||||
"goal:get_one": "rule:default"})
|
||||
|
@ -10,6 +10,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
|
||||
from oslo_config import cfg
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
@ -187,3 +189,49 @@ class TestListStrategy(api_base.FunctionalTest):
|
||||
self.assertEqual(2, len(strategies))
|
||||
for strategy in strategies:
|
||||
self.assertEqual(goal1['uuid'], strategy['goal_uuid'])
|
||||
|
||||
|
||||
class TestStrategyPolicyEnforcement(api_base.FunctionalTest):
|
||||
|
||||
def _common_policy_check(self, rule, func, *arg, **kwarg):
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
rule: "rule:defaut"})
|
||||
response = func(*arg, **kwarg)
|
||||
self.assertEqual(403, response.status_int)
|
||||
self.assertEqual('application/json', response.content_type)
|
||||
self.assertTrue(
|
||||
"Policy doesn't allow %s to be performed." % rule,
|
||||
json.loads(response.json['error_message'])['faultstring'])
|
||||
|
||||
def test_policy_disallow_get_all(self):
|
||||
self._common_policy_check(
|
||||
"strategy:get_all", self.get_json, '/strategies',
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_get_one(self):
|
||||
strategy = obj_utils.create_test_strategy(self.context)
|
||||
self._common_policy_check(
|
||||
"strategy:get", self.get_json,
|
||||
'/strategies/%s' % strategy.uuid,
|
||||
expect_errors=True)
|
||||
|
||||
def test_policy_disallow_detail(self):
|
||||
self._common_policy_check(
|
||||
"strategy:detail", self.get_json,
|
||||
'/strategies/detail',
|
||||
expect_errors=True)
|
||||
|
||||
|
||||
class TestStrategyEnforcementWithAdminContext(TestListStrategy,
|
||||
api_base.AdminRoleTest):
|
||||
|
||||
def setUp(self):
|
||||
super(TestStrategyEnforcementWithAdminContext, self).setUp()
|
||||
self.policy.set_rules({
|
||||
"admin_api": "(role:admin or role:administrator)",
|
||||
"default": "rule:admin_api",
|
||||
"strategy:detail": "rule:default",
|
||||
"strategy:get": "rule:default",
|
||||
"strategy:get_all": "rule:default"})
|
||||
|
@ -29,6 +29,7 @@ import testscenarios
|
||||
from watcher.common import context as watcher_context
|
||||
from watcher.objects import base as objects_base
|
||||
from watcher.tests import conf_fixture
|
||||
from watcher.tests import policy_fixture
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -68,6 +69,8 @@ class TestCase(BaseTestCase):
|
||||
project_id='fake_project',
|
||||
user_id='fake_user')
|
||||
|
||||
self.policy = self.useFixture(policy_fixture.PolicyFixture())
|
||||
|
||||
def make_context(*args, **kwargs):
|
||||
# If context hasn't been constructed with token_info
|
||||
if not kwargs.get('auth_token_info'):
|
||||
|
@ -16,9 +16,40 @@
|
||||
policy_data = """
|
||||
{
|
||||
"admin_api": "role:admin or role:administrator",
|
||||
"public_api": "is_public_api:True",
|
||||
"trusted_call": "rule:admin_api or rule:public_api",
|
||||
"default": "rule:trusted_call",
|
||||
"show_password": "!",
|
||||
"default": "rule:admin_api",
|
||||
|
||||
"action:detail": "",
|
||||
"action:get": "",
|
||||
"action:get_all": "",
|
||||
|
||||
"action_plan:delete": "",
|
||||
"action_plan:detail": "",
|
||||
"action_plan:get": "",
|
||||
"action_plan:get_all": "",
|
||||
"action_plan:update": "",
|
||||
|
||||
"audit:create": "",
|
||||
"audit:delete": "",
|
||||
"audit:detail": "",
|
||||
"audit:get": "",
|
||||
"audit:get_all": "",
|
||||
"audit:update": "",
|
||||
|
||||
"audit_template:create": "",
|
||||
"audit_template:delete": "",
|
||||
"audit_template:detail": "",
|
||||
"audit_template:get": "",
|
||||
"audit_template:get_all": "",
|
||||
"audit_template:update": "",
|
||||
|
||||
"goal:detail": "",
|
||||
"goal:get": "",
|
||||
"goal:get_all": "",
|
||||
|
||||
"strategy:detail": "",
|
||||
"strategy:get": "",
|
||||
"strategy:get_all": ""
|
||||
}
|
||||
"""
|
||||
|
||||
|
@ -16,25 +16,29 @@ import os
|
||||
|
||||
import fixtures
|
||||
from oslo_config import cfg
|
||||
from oslo_policy import _parser
|
||||
from oslo_policy import opts as policy_opts
|
||||
|
||||
from watcher.common import policy as w_policy
|
||||
from watcher.common import policy as watcher_policy
|
||||
from watcher.tests import fake_policy
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class PolicyFixture(fixtures.Fixture):
|
||||
def __init__(self, compat=None):
|
||||
self.compat = compat
|
||||
|
||||
def setUp(self):
|
||||
super(PolicyFixture, self).setUp()
|
||||
def _setUp(self):
|
||||
self.policy_dir = self.useFixture(fixtures.TempDir())
|
||||
self.policy_file_name = os.path.join(self.policy_dir.path,
|
||||
'policy.json')
|
||||
with open(self.policy_file_name, 'w') as policy_file:
|
||||
policy_file.write(fake_policy.get_policy_data(self.compat))
|
||||
CONF.set_override('policy_file', self.policy_file_name,
|
||||
enforce_type=True)
|
||||
w_policy._ENFORCER = None
|
||||
self.addCleanup(w_policy.get_enforcer().clear)
|
||||
policy_file.write(fake_policy.policy_data)
|
||||
policy_opts.set_defaults(CONF)
|
||||
CONF.set_override('policy_file', self.policy_file_name, 'oslo_policy')
|
||||
watcher_policy._ENFORCER = None
|
||||
self.addCleanup(watcher_policy.init().clear)
|
||||
|
||||
def set_rules(self, rules):
|
||||
policy = watcher_policy._ENFORCER
|
||||
policy.set_rules({k: _parser.parse_rule(v)
|
||||
for k, v in rules.items()})
|
||||
|
Loading…
Reference in New Issue
Block a user