0de98e5993
Change-Id: I1f16bda22a67cee53b60677f9d4c09ae34a16c11
181 lines
7.0 KiB
ReStructuredText
181 lines
7.0 KiB
ReStructuredText
============================================================
|
|
Horizon Policy Enforcement (RBAC: Role Based Access Control)
|
|
============================================================
|
|
|
|
Introduction
|
|
============
|
|
|
|
Horizon's policy enforcement builds on the oslo_policy engine.
|
|
The basis of which is ``openstack_auth/policy.py``.
|
|
Services in OpenStack use the oslo policy engine to define policy rules
|
|
to limit access to APIs based primarily on role grants and resource
|
|
ownership.
|
|
|
|
The Keystone v3 API provides an interface for creating/reading/updating
|
|
policy files in the keystone database. However, at this time services
|
|
do not load the policy files into Keystone. Thus, the implementation in
|
|
Horizon is based on copies of policy.json files found in the service's
|
|
source code. The long-term goal is to read/utilize/update these policy
|
|
files in Horizon.
|
|
|
|
The service rules files are loaded into the policy engine to determine
|
|
access rights to actions and service APIs.
|
|
|
|
Horizon Settings
|
|
================
|
|
|
|
There are a few settings that must be in place for the Horizon policy
|
|
engine to work.
|
|
|
|
``POLICY_FILES_PATH``
|
|
---------------------
|
|
|
|
Default: ``os.path.join(ROOT_PATH, "conf")``
|
|
|
|
Specifies where service based policy files are located. These are used to
|
|
define the policy rules actions are verified against. This value must contain
|
|
the files listed in ``POLICY_FILES`` or all policy checks will pass.
|
|
|
|
.. note::
|
|
|
|
The path to deployment specific policy files can be specified in
|
|
``local_settings.py`` to override the default location.
|
|
|
|
|
|
``POLICY_FILES``
|
|
----------------
|
|
|
|
Default: ``{'identity': 'keystone_policy.json', 'compute': 'nova_policy.json'}``
|
|
|
|
This should essentially be the mapping of the contents of ``POLICY_FILES_PATH``
|
|
to service types. When policy.json files are added to the directory
|
|
``POLICY_FILES_PATH``, they should be included here too. Without this mapping,
|
|
there is no way to map service types with policy rules, thus two policy.json
|
|
files containing a "default" rule would be ambiguous.
|
|
|
|
.. note::
|
|
|
|
Deployment specific policy files can be specified in ``local_settings.py``
|
|
to override the default policy files. It is imperative that these policy
|
|
files match those deployed in the target OpenStack installation. Otherwise,
|
|
the displayed actions and the allowed action will not match.
|
|
|
|
``POLICY_CHECK_FUNCTION``
|
|
-------------------------
|
|
|
|
Default: ``policy.check``
|
|
|
|
This value should not be changed, although removing it would be a means to
|
|
bypass all policy checks. Set it to ``None`` in ``local_settings.py`` to
|
|
do this.
|
|
|
|
|
|
How user's roles are determined
|
|
===============================
|
|
|
|
Each policy check uses information about the user stored on the request to
|
|
determine the user's roles. This information was extracted from the scoped
|
|
token received from Keystone when authenticating.
|
|
|
|
Entity ownership is also a valid role. To verify access to specific entities
|
|
like a project, the target must be specified. See the section
|
|
:ref:`rule targets <rule_targets>` later in this document.
|
|
|
|
How to Utilize RBAC
|
|
===================
|
|
|
|
The primary way to add role based access control checks to panels is in the
|
|
definition of table actions. When implementing a derived action class,
|
|
setting the :attr:`~horizon.tables.Action.policy_rules` attribute to valid
|
|
policy rules will force a policy check before the
|
|
:meth:`horizon.tables.Action.allowed` method is called on the action. These
|
|
rules are defined in the policy files pointed to by ``POLICY_PATH`` and
|
|
``POLICY_FILES``. The rules are role based, where entity owner is also a
|
|
role. The format for the ``policy_rules`` is a list of two item tuples. The
|
|
first component of the tuple is the scope of the policy rule, this is the
|
|
service type. This informs the policy engine which policy file to reference.
|
|
The second component is the rule to enforce from the policy file specified by
|
|
the scope. An example tuple is::
|
|
|
|
("identity", "identity:get_user")
|
|
|
|
x tuples can be added to enforce x rules.
|
|
|
|
.. note::
|
|
|
|
If a rule specified is not found in the policy file, the policy check
|
|
will return False and the action will not be allowed.
|
|
|
|
The secondary way to add a role based check is to directly use the
|
|
:meth:`~openstack_dashboard.policy.check` method. The method takes a list
|
|
of actions, same format as the :attr:`~horizon.tables.Action.policy_rules`
|
|
attribute detailed above; the current request object; and a dictionary of
|
|
action targets. This is the method that :class:`horizon.tables.Action` class
|
|
utilizes. Examples look like::
|
|
|
|
from openstack_dashboard import policy
|
|
|
|
allowed = policy.check((("identity", "identity:get_user"),
|
|
("identity", "identity:get_project"),), request)
|
|
|
|
can_see = policy.check((("identity", "identity:get_user"),), request,
|
|
target={"domain_id": domainId})
|
|
|
|
.. note::
|
|
|
|
Any time multiple rules are specified in a single `policy.check` method
|
|
call, the result is the logical `and` of each rule check. So, if any
|
|
rule fails verification, the result is `False`.
|
|
|
|
The third way to add a role based check is in javascript files. Use the method
|
|
'ifAllowed()' in file 'openstack_dashboard.static.app.core.policy.service.js'.
|
|
The method takes a list of actions, similar format with the
|
|
:attr:`~horizon.tables.Action.policy_rules` attribute detailed above.
|
|
An Example looks like::
|
|
|
|
angular
|
|
.module('horizon.dashboard.identity.users')
|
|
.controller('identityUsersTableController', identityUsersTableController);
|
|
|
|
identityUsersTableController.$inject = [
|
|
'horizon.app.core.openstack-service-api.policy',
|
|
];
|
|
|
|
function identityUsersTableController(toast, gettext, policy, keystone) {
|
|
var rules = [['identity', 'identity:list_users']];
|
|
policy.ifAllowed({ rules: rules }).then(policySuccess, policyFailed);
|
|
}
|
|
|
|
The fourth way to add a role based check is in html files. Use angular directive 'hz-if-policies'
|
|
in file 'openstack_dashboard/static/app/core/cloud-services/hz-if-policies.directive.js'.
|
|
Assume you have the following policy defined in your angular controller::
|
|
|
|
ctrl.policy = { rules: [["identity", "identity:update_user"]] }
|
|
|
|
Then in your HTML, use it like so::
|
|
|
|
<div hz-if-policies='ctrl.policy'>
|
|
<span>I am visible if the policy is allowed!</span>
|
|
</div>
|
|
|
|
.. _rule_targets:
|
|
|
|
Rule Targets
|
|
============
|
|
|
|
Some rules allow access if the user owns the entity. Policy check targets
|
|
specify particular entities to check for user ownership. The target parameter
|
|
to the :meth:`~openstack_dashboard.policy.check` method is a simple dictionary.
|
|
For instance, the target for checking access a project looks like::
|
|
|
|
{"project_id": "0905760626534a74979afd3f4a9d67f1"}
|
|
|
|
If the value matches the ``project_id`` to which the user's token is scoped,
|
|
then access is allowed.
|
|
|
|
When deriving the :class:`horizon.tables.Action` class for use in a table, if
|
|
a policy check is desired for a particular target, the implementer should
|
|
override the :meth:`horizon.tables.Action.get_policy_target` method. This
|
|
allows a programmatic way to specify the target based on the current datum. The
|
|
value returned should be the target dictionary.
|