diff --git a/devstack/lib/fault b/devstack/lib/fault index 614fe31d..a1f0bb76 100644 --- a/devstack/lib/fault +++ b/devstack/lib/fault @@ -2,7 +2,9 @@ # # SPDX-License-Identifier: Apache-2.0 # -# Copyright (C) 2019 Intel Corporation +# Copyright (c) 2019 Intel Corporation +# +# Copyright (c) 2022 Wind River Systems, Inc. # # lib/fault # Functions to control the configuration and operation of the **fault** service @@ -37,6 +39,7 @@ GITDIR["fm-core"]=$STX_FAULT_DIR/fm-common/sources FM_RESTAPI_CONF=$STX_FAULT_CONF_DIR/fm.conf FM_RESTAPI_PASTE_INI=$STX_FAULT_CONF_DIR/api-paste.ini FM_EVENT_YAML=$STX_FAULT_CONF_DIR/events.yaml +FM_POLICY_YAML=$STX_FAULT_CONF_DIR/policy.yaml FM_RESTAPI_AUTH_CACHE_DIR=${FM_RESTAPI_AUTH_CACHE_DIR:-/var/cache/fault} FM_RESTAPI_DIR=$STX_FAULT_DIR/fm-rest-api/fm @@ -191,7 +194,7 @@ function cleanup_fm_mgr { function cleanup_fm_rest_api { sudo pip uninstall -y fm - sudo rm -rf $FM_RESTAPI_AUTH_CACHE_DIR $FM_RESTAPI_CONF $FM_RESTAPI_PASTE_INI $FM_EVENT_YAML + sudo rm -rf $FM_RESTAPI_AUTH_CACHE_DIR $FM_RESTAPI_CONF $FM_RESTAPI_PASTE_INI $FM_EVENT_YAML $FM_POLICY_YAML dropdb -h 127.0.0.1 -Uroot fm } @@ -208,6 +211,7 @@ function configure_fm_rest_api { cp -p $STX_FAULT_DIR/devstack/files/api-paste.ini $FM_RESTAPI_PASTE_INI cp -p $STX_FAULT_DIR/fm-doc/fm_doc/events.yaml $FM_EVENT_YAML + cp -p $STX_FAULT_DIR/fm-rest-api/fm/fm/policy.yaml $FM_POLICY_YAML configure_auth_token_middleware $FM_RESTAPI_CONF fm $FM_RESTAPI_AUTH_CACHE_DIR diff --git a/fm-rest-api/centos/fm-rest-api.spec b/fm-rest-api/centos/fm-rest-api.spec index b28592bc..6eab40f1 100644 --- a/fm-rest-api/centos/fm-rest-api.spec +++ b/fm-rest-api/centos/fm-rest-api.spec @@ -69,6 +69,7 @@ install -p -D -m 644 fm-api-pmond.conf %{buildroot}%{local_etc_pmond}/fm-api.con # install default config files cd %{_builddir}/%{name}-%{version} && oslo-config-generator --config-file fm/config-generator.conf --output-file %{_builddir}/%{name}-%{version}/fm.conf.sample install -p -D -m 600 %{_builddir}/%{name}-%{version}/fm.conf.sample %{buildroot}%{_sysconfdir}/fm/fm.conf +install -p -D -m 600 fm/policy.yaml %{buildroot}%{_sysconfdir}/fm/policy.yaml %clean echo "CLEAN CALLED" @@ -90,6 +91,7 @@ rm -rf $RPM_BUILD_ROOT %{pythonroot}/fm-%{version}*.egg-info %config(noreplace) %attr(600,fm,fm)%{_sysconfdir}/fm/fm.conf +%config(noreplace) %attr(600,fm,fm)%{_sysconfdir}/fm/policy.yaml # systemctl service files %{_unitdir}/fm-api.service diff --git a/fm-rest-api/debian/deb_folder/fm-rest-api.install b/fm-rest-api/debian/deb_folder/fm-rest-api.install index 69e1eeb5..3e0c4c46 100644 --- a/fm-rest-api/debian/deb_folder/fm-rest-api.install +++ b/fm-rest-api/debian/deb_folder/fm-rest-api.install @@ -1,4 +1,5 @@ debian/systemd/00-fm-rest-api.preset etc/systemd/system-preset etc/fm/fm.conf +etc/fm/policy.yaml etc/init.d etc/pmon.d/fm-api.conf diff --git a/fm-rest-api/debian/deb_folder/postinst b/fm-rest-api/debian/deb_folder/postinst index ff26b856..e628dd62 100644 --- a/fm-rest-api/debian/deb_folder/postinst +++ b/fm-rest-api/debian/deb_folder/postinst @@ -3,5 +3,6 @@ set -e chown fm:fm /etc/fm/fm.conf +chown fm:fm /etc/fm/policy.yaml #DEBHELPER# diff --git a/fm-rest-api/debian/deb_folder/rules b/fm-rest-api/debian/deb_folder/rules index 1c17e506..81a031d2 100755 --- a/fm-rest-api/debian/deb_folder/rules +++ b/fm-rest-api/debian/deb_folder/rules @@ -18,11 +18,12 @@ override_dh_auto_install: oslo-config-generator --config-file fm/config-generator.conf --output-file fm.conf.sample install -d -m 755 $(FMCONFDIR) install -p -D -m 600 fm.conf.sample $(FMCONFDIR)/fm.conf + install -p -D -m 600 fm/policy.yaml $(FMCONFDIR)/policy.yaml dh_auto_install override_dh_fixperms: - dh_fixperms -Xfm.conf + dh_fixperms -Xfm.conf -Xpolicy.yaml override_dh_installsystemd: dh_installsystemd --no-enable --name fm-api diff --git a/fm-rest-api/fm/fm/api/app.py b/fm-rest-api/fm/fm/api/app.py index 7fd5a927..7e160db3 100644 --- a/fm-rest-api/fm/fm/api/app.py +++ b/fm-rest-api/fm/fm/api/app.py @@ -10,7 +10,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Copyright (c) 2018 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -23,6 +23,7 @@ from oslo_log import log import pecan from fm.api import config +from fm.api import hooks from fm.api import middleware from fm.common import policy from fm.common.i18n import _ @@ -48,6 +49,8 @@ def setup_app(config=None): pecan.configuration.set_config(dict(config), overwrite=True) app_conf = dict(config.app) + if app_conf['enable_acl']: + app_conf['hooks'].append(hooks.AccessPolicyHook()) app = pecan.make_app( app_conf.pop('root'), diff --git a/fm-rest-api/fm/fm/api/config.py b/fm-rest-api/fm/fm/api/config.py index de6ccee7..7fc60422 100644 --- a/fm-rest-api/fm/fm/api/config.py +++ b/fm-rest-api/fm/fm/api/config.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2022 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -45,10 +45,7 @@ app = { hooks.DBHook(), hooks.AuditLogging(), ], - 'acl_public_routes': [ - '/', - '/v1', - ], + 'enable_acl': True } diff --git a/fm-rest-api/fm/fm/api/controllers/v1/alarm.py b/fm-rest-api/fm/fm/api/controllers/v1/alarm.py index aee0c808..dc2a2a78 100644 --- a/fm-rest-api/fm/fm/api/controllers/v1/alarm.py +++ b/fm-rest-api/fm/fm/api/controllers/v1/alarm.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2021 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -25,7 +25,9 @@ from fm.api.controllers.v1 import types from fm.api.controllers.v1 import utils as api_utils from fm.common import exceptions from fm.common import constants +from fm.common import policy from fm import objects +from fm.api.policies import alarm as alarm_policy from fm.api.controllers.v1.query import Query from fm_api import constants as fm_constants @@ -438,3 +440,21 @@ class AlarmController(rest.RestController): return err alarm_dict = alm.as_dict() return json.dumps({"uuid": alarm_dict['uuid']}) + + def enforce_policy(self, method_name, request): + """Check policy rules for each action of this controller.""" + context_dict = request.context.to_dict() + if method_name == "delete": + policy.authorize(alarm_policy.POLICY_ROOT % "delete", {}, + context_dict) + elif method_name in ["detail", "get_all", "get_one", "summary"]: + policy.authorize(alarm_policy.POLICY_ROOT % "get", {}, + context_dict) + elif method_name == "post": + policy.authorize(alarm_policy.POLICY_ROOT % "create", {}, + context_dict) + elif method_name == "put": + policy.authorize(alarm_policy.POLICY_ROOT % "modify", {}, + context_dict) + else: + raise exceptions.PolicyNotFound() diff --git a/fm-rest-api/fm/fm/api/controllers/v1/event_log.py b/fm-rest-api/fm/fm/api/controllers/v1/event_log.py index e5d012d5..4fe7a58f 100644 --- a/fm-rest-api/fm/fm/api/controllers/v1/event_log.py +++ b/fm-rest-api/fm/fm/api/controllers/v1/event_log.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018-2019 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -23,7 +23,9 @@ from fm.api.controllers.v1 import collection from fm.api.controllers.v1 import link from fm.api.controllers.v1.query import Query from fm.api.controllers.v1 import types +from fm.api.policies import event_log as event_log_policy from fm.common import exceptions +from fm.common import policy from fm.common.i18n import _ LOG = log.getLogger(__name__) @@ -292,3 +294,12 @@ class EventLogController(rest.RestController): pecan.request.context, id) return EventLog.convert_with_links(rpc_ilog) + + def enforce_policy(self, method_name, request): + """Check policy rules for each action of this controller.""" + context_dict = request.context.to_dict() + if method_name in ["detail", "get_all", "get_one"]: + policy.authorize(event_log_policy.POLICY_ROOT % "get", {}, + context_dict) + else: + raise exceptions.PolicyNotFound() diff --git a/fm-rest-api/fm/fm/api/controllers/v1/event_suppression.py b/fm-rest-api/fm/fm/api/controllers/v1/event_suppression.py index 8402256c..ee673f68 100644 --- a/fm-rest-api/fm/fm/api/controllers/v1/event_suppression.py +++ b/fm-rest-api/fm/fm/api/controllers/v1/event_suppression.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -20,7 +20,10 @@ from fm.api.controllers.v1 import link from fm.api.controllers.v1.query import Query from fm.api.controllers.v1 import types from fm.api.controllers.v1 import utils as api_utils +from fm.api.policies import event_suppression as event_suppression_policy from fm.common import constants +from fm.common import exceptions +from fm.common import policy from fm.common import utils as cutils from fm.common.i18n import _ @@ -213,3 +216,15 @@ class EventSuppressionController(rest.RestController): pecan.request.dbapi.event_suppression_update(uuid, updates) return EventSuppression.convert_with_links(updated_event_suppression) + + def enforce_policy(self, method_name, request): + """Check policy rules for each action of this controller.""" + context_dict = request.context.to_dict() + if method_name in ["get_all", "get_one"]: + policy.authorize(event_suppression_policy.POLICY_ROOT % "get", + {}, context_dict) + elif method_name == "patch": + policy.authorize(event_suppression_policy.POLICY_ROOT % "modify", + {}, context_dict) + else: + raise exceptions.PolicyNotFound() diff --git a/fm-rest-api/fm/fm/api/hooks.py b/fm-rest-api/fm/fm/api/hooks.py index 10c3488a..33bef849 100644 --- a/fm-rest-api/fm/fm/api/hooks.py +++ b/fm-rest-api/fm/fm/api/hooks.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2022 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -13,6 +13,7 @@ from oslo_config import cfg from oslo_log import log from oslo_serialization import jsonutils from oslo_utils import uuidutils +from webob import exc from fm.common import context from fm.db import api as dbapi @@ -59,7 +60,7 @@ class ContextHook(hooks.PecanHook): environ = state.request.environ user_name = headers.get('X-User-Name') user_id = headers.get('X-User-Id') - project = headers.get('X-Project-Name') + project_name = headers.get('X-Project-Name') project_id = headers.get('X-Project-Id') domain_id = headers.get('X-User-Domain-Id') domain_name = headers.get('X-User-Domain-Name') @@ -83,7 +84,7 @@ class ContextHook(hooks.PecanHook): auth_token_info=auth_token_info, user_name=user_name, user_id=user_id, - project_name=project, + project_name=project_name, project_id=project_id, domain_id=domain_id, domain_name=domain_name, @@ -146,7 +147,10 @@ class AuditLogging(hooks.PecanHook): def json_post_data(rest_state): if 'form-data' in rest_state.request.headers.get('Content-Type'): return " POST: {}".format(rest_state.request.params) - if not hasattr(rest_state.request, 'json'): + try: + if not hasattr(rest_state.request, 'json'): + return "" + except Exception: return "" return " POST: {}".format(rest_state.request.json) @@ -195,3 +199,19 @@ class AuditLogging(hooks.PecanHook): def on_error(self, state, e): auditLOG.exception("Exception in AuditLogging passed to event 'on_error': " + str(e)) + + +class AccessPolicyHook(hooks.PecanHook): + """Verify that the user has the needed privilege to execute the action.""" + def before(self, state): + is_public_api = state.request.environ.get('is_public_api', False) + if not is_public_api: + controller = state.controller.__self__ + if hasattr(controller, 'enforce_policy'): + try: + controller_method = state.controller.__name__ + controller.enforce_policy(controller_method, state.request) + except Exception: + raise exc.HTTPForbidden() + else: + raise exc.HTTPForbidden() diff --git a/fm-rest-api/fm/fm/api/policies/__init__.py b/fm-rest-api/fm/fm/api/policies/__init__.py new file mode 100644 index 00000000..c8007d7a --- /dev/null +++ b/fm-rest-api/fm/fm/api/policies/__init__.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import itertools + +from fm.api.policies import base +from fm.api.policies import alarm +from fm.api.policies import event_log +from fm.api.policies import event_suppression + + +def list_rules(): + return itertools.chain( + base.list_rules(), + alarm.list_rules(), + event_log.list_rules(), + event_suppression.list_rules() + ) diff --git a/fm-rest-api/fm/fm/api/policies/alarm.py b/fm-rest-api/fm/fm/api/policies/alarm.py new file mode 100644 index 00000000..0f92295f --- /dev/null +++ b/fm-rest-api/fm/fm/api/policies/alarm.py @@ -0,0 +1,74 @@ +# +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from oslo_policy import policy +from fm.api.policies import base + +POLICY_ROOT = 'fm_api:alarm:%s' + + +alarm_rules = [ + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'create', + check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS, + description="Create an alarm.", + operations=[ + { + 'method': 'POST', + 'path': '/v1/alarms' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'delete', + check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS, + description="Delete an alarm.", + operations=[ + { + 'method': 'DELETE', + 'path': '/v1/alarms/{alarm_uuid}' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'get', + check_str='role:reader', + description="Get alarms.", + operations=[ + { + 'method': 'GET', + 'path': '/v1/alarms' + }, + { + 'method': 'GET', + 'path': '/v1/alarms/{alarm_uuid}' + }, + { + 'method': 'GET', + 'path': '/v1/alarms/detail' + }, + { + 'method': 'GET', + 'path': '/v1/alarms/summary' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'modify', + check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS, + description="Modify an alarm.", + operations=[ + { + 'method': 'PUT', + 'path': '/v1/alarms' + } + ] + ) +] + + +def list_rules(): + return alarm_rules diff --git a/fm-rest-api/fm/fm/api/policies/base.py b/fm-rest-api/fm/fm/api/policies/base.py new file mode 100644 index 00000000..a4455a24 --- /dev/null +++ b/fm-rest-api/fm/fm/api/policies/base.py @@ -0,0 +1,30 @@ +# +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from oslo_policy import policy + +ADMIN_IN_SYSTEM_PROJECTS = 'admin_in_system_projects' +READER_IN_SYSTEM_PROJECTS = 'reader_in_system_projects' + + +base_rules = [ + policy.RuleDefault( + name=ADMIN_IN_SYSTEM_PROJECTS, + check_str='role:admin and (project_name:admin or ' + + 'project_name:services)', + description="Base rule.", + ), + policy.RuleDefault( + name=READER_IN_SYSTEM_PROJECTS, + check_str='role:reader and (project_name:admin or ' + + 'project_name:services)', + description="Base rule." + ) +] + + +def list_rules(): + return base_rules diff --git a/fm-rest-api/fm/fm/api/policies/event_log.py b/fm-rest-api/fm/fm/api/policies/event_log.py new file mode 100644 index 00000000..00a691ce --- /dev/null +++ b/fm-rest-api/fm/fm/api/policies/event_log.py @@ -0,0 +1,36 @@ +# +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from oslo_policy import policy + +POLICY_ROOT = 'fm_api:event_log:%s' + + +event_log_rules = [ + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'get', + check_str='role:reader', + description="Get event logs.", + operations=[ + { + 'method': 'GET', + 'path': '/v1/event_log' + }, + { + 'method': 'GET', + 'path': '/v1/event_log/{log_uuid}' + }, + { + 'method': 'GET', + 'path': '/v1/event_log/detail' + } + ] + ) +] + + +def list_rules(): + return event_log_rules diff --git a/fm-rest-api/fm/fm/api/policies/event_suppression.py b/fm-rest-api/fm/fm/api/policies/event_suppression.py new file mode 100644 index 00000000..9ce21dd5 --- /dev/null +++ b/fm-rest-api/fm/fm/api/policies/event_suppression.py @@ -0,0 +1,44 @@ +# +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +from oslo_policy import policy +from fm.api.policies import base + +POLICY_ROOT = 'fm_api:event_suppression:%s' + + +event_suppression_rules = [ + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'get', + check_str='role:reader', + description="Get event suppressions.", + operations=[ + { + 'method': 'GET', + 'path': '/v1/event_suppression' + }, + { + 'method': 'GET', + 'path': '/v1/event_suppression/{event_suppression_uuid}' + } + ] + ), + policy.DocumentedRuleDefault( + name=POLICY_ROOT % 'modify', + check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS, + description="Modify the value of an event suppression.", + operations=[ + { + 'method': 'PATCH', + 'path': '/v1/event_suppression/{event_suppression_uuid}' + } + ] + ) +] + + +def list_rules(): + return event_suppression_rules diff --git a/fm-rest-api/fm/fm/common/context.py b/fm-rest-api/fm/fm/common/context.py index 9dd811ef..c1e3652c 100644 --- a/fm-rest-api/fm/fm/common/context.py +++ b/fm-rest-api/fm/fm/common/context.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -9,6 +9,7 @@ from oslo_config import cfg from keystoneauth1 import plugin from keystoneauth1.access import service_catalog as k_service_catalog +from fm.api.policies import base as base_policy from fm.common import policy @@ -95,7 +96,9 @@ class RequestContext(context.RequestContext): self.user_auth_plugin = user_auth_plugin if is_admin is None: - self.is_admin = policy.check_is_admin(self) + self.is_admin = policy.authorize( + base_policy.ADMIN_IN_SYSTEM_PROJECTS, {}, self.to_dict(), + do_raise=False) else: self.is_admin = is_admin diff --git a/fm-rest-api/fm/fm/common/exceptions.py b/fm-rest-api/fm/fm/common/exceptions.py index 873f0910..b0780aa1 100644 --- a/fm-rest-api/fm/fm/common/exceptions.py +++ b/fm-rest-api/fm/fm/common/exceptions.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -104,6 +104,10 @@ class PolicyNotAuthorized(ApiError): code = webob.exc.HTTPUnauthorized.code +class PolicyNotFound(Invalid): + message = _("Policy not found for requested action.") + + class Conflict(ApiError): message = _('HTTP Conflict.') # 409 - HTTPConflict diff --git a/fm-rest-api/fm/fm/common/policy.py b/fm-rest-api/fm/fm/common/policy.py index 16a7320d..20c45579 100644 --- a/fm-rest-api/fm/fm/common/policy.py +++ b/fm-rest-api/fm/fm/common/policy.py @@ -13,77 +13,47 @@ # License for the specific language governing permissions and limitations # under the License. # -# Copyright (c) 2018 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # """Policy Engine For FM.""" from oslo_config import cfg - from oslo_policy import policy -from oslo_log import log +from fm.api import policies as controller_policies -base_rules = [ - policy.RuleDefault('admin_required', 'role:admin or is_admin:1', - description='Who is considered an admin'), - policy.RuleDefault('admin_api', 'is_admin_required:True', - description='admin API requirement'), - policy.RuleDefault('default', 'rule:admin_api', - description='default rule'), -] - CONF = cfg.CONF - - -LOG = log.getLogger(__name__) - _ENFORCER = None -# 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): +def reset(): + """Discard current Enforcer object.""" + global _ENFORCER + _ENFORCER = None + + +def init(policy_file='policy.yaml'): """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. + :param policy_file: Custom policy file to be used. + + :return: Returns a Enforcer instance. """ global _ENFORCER if not _ENFORCER: # https://docs.openstack.org/oslo.policy/latest/user/usage.html _ENFORCER = policy.Enforcer(CONF, policy_file=policy_file, - rules=rules, - default_rule=default_rule, - use_conf=use_conf, - overwrite=overwrite) - _ENFORCER.register_defaults(base_rules) + default_rule='default', + use_conf=True, + overwrite=True) + _ENFORCER.register_defaults(controller_policies.list_rules()) return _ENFORCER -def check_is_admin(context): - """Whether or not role contains 'admin' role according to policy setting. - - """ +def authorize(rule, target, creds, do_raise=True): + """A wrapper around 'authorize' from 'oslo_policy.policy'.""" init() - - target = {} - credentials = context.to_dict() - - return _ENFORCER.enforce('context_is_admin', target, credentials) + return _ENFORCER.authorize(rule, target, creds, do_raise=do_raise) diff --git a/fm-rest-api/fm/fm/policy.yaml b/fm-rest-api/fm/fm/policy.yaml new file mode 100644 index 00000000..510f9246 --- /dev/null +++ b/fm-rest-api/fm/fm/policy.yaml @@ -0,0 +1,16 @@ +--- +# The commented lines below contains the default values for presented rules. + +# admin_in_system_projects: role:admin and (project_name:admin or project_name:services) +# reader_in_system_projects: role:reader and (project_name:admin or project_name:services) + +# fm_api:alarm:create: rule:admin_in_system_projects +# fm_api:alarm:delete: rule:admin_in_system_projects +# fm_api:alarm:get: role:reader +# fm_api:alarm:modify: rule:admin_in_system_projects + +# fm_api:event_log:get: role:reader + +# fm_api:event_suppression:get: role:reader +# fm_api:event_suppression:modify: rule:admin_in_system_projects + diff --git a/fm-rest-api/fm/fm/tests/api/base.py b/fm-rest-api/fm/fm/tests/api/base.py index d8506099..cc318119 100644 --- a/fm-rest-api/fm/fm/tests/api/base.py +++ b/fm-rest-api/fm/fm/tests/api/base.py @@ -12,6 +12,11 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +# +# Copyright (c) 2022 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# """Base classes for API tests.""" from oslo_config import cfg @@ -45,7 +50,7 @@ class FunctionalTest(base.TestCase): 'app': { 'root': 'fm.api.controllers.root.RootController', 'modules': ['fm.api'], - 'acl_public_routes': ['/', '/v1'], + 'enable_acl': False }, } diff --git a/fm-rest-api/opensuse/fm-rest-api.spec b/fm-rest-api/opensuse/fm-rest-api.spec index ca06f9de..31625e33 100644 --- a/fm-rest-api/opensuse/fm-rest-api.spec +++ b/fm-rest-api/opensuse/fm-rest-api.spec @@ -79,6 +79,7 @@ install -m 640 fm/db/sqlalchemy/migrate_repo/migrate.cfg %{buildroot}%{pythonroo # install default config files oslo-config-generator --config-file fm/config-generator.conf --output-file %{_builddir}/fm.conf.sample install -p -D -m 644 %{_builddir}/fm.conf.sample %{buildroot}%{_sysconfdir}/fm/fm.conf +install -p -D -m 600 fm/policy.yaml %{buildroot}%{_sysconfdir}/fm/policy.yaml %fdupes %{buildroot}%{pythonroot}/fm @@ -99,6 +100,7 @@ install -p -D -m 644 %{_builddir}/fm.conf.sample %{buildroot}%{_sysconfdir}/fm/f %dir %{_sysconfdir}/fm %config(noreplace) %{_sysconfdir}/fm/fm.conf +%config(noreplace) %attr(600,fm,fm)%{_sysconfdir}/fm/policy.yaml # systemctl service files %{_unitdir}/fm-api.service diff --git a/python-fmclient/fmclient/fmclient/v1/event_suppression_shell.py b/python-fmclient/fmclient/fmclient/v1/event_suppression_shell.py index 7064dc17..f57ce23c 100644 --- a/python-fmclient/fmclient/fmclient/v1/event_suppression_shell.py +++ b/python-fmclient/fmclient/fmclient/v1/event_suppression_shell.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2018 Wind River Systems, Inc. +# Copyright (c) 2018-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -96,10 +96,10 @@ def event_suppression_update(cc, data, suppress=False): patch = [] for event_id in event_suppression_list: if event_id.alarm_id in alarm_id_list: - print("Alarm ID: {} {}.".format(event_id.alarm_id, patch_value)) uuid = event_id.uuid patch.append(dict(path='/' + 'suppression_status', value=patch_value, op='replace')) cc.event_suppression.update(uuid, patch) + print("Alarm ID: {} {}.".format(event_id.alarm_id, patch_value)) @utils.arg('--include-unsuppressed', action='store_true', @@ -196,8 +196,8 @@ def do_event_unsuppress_all(cc, args): if suppression_status == 'suppressed': uuid = alarm_type.uuid patch.append(dict(path='/' + 'suppression_status', value='unsuppressed', op='replace')) - print("Alarm ID: {} unsuppressed.".format(alarm_type.alarm_id)) cc.event_suppression.update(uuid, patch) + print("Alarm ID: {} unsuppressed.".format(alarm_type.alarm_id)) no_paging = args.nopaging includeUUID = args.uuid