Implement access control for FM API

This commit implements the access control for all FM APIs. An incomplete
list of FM APIs can be found at
"https://docs.starlingx.io/api-ref/fault/api-ref-fm-v1-fault.html". Unit
tests will be created in other task.

All access control rules can be overwritten through file
"/etc/fm/policy.yaml". Any change in file "/etc/fm/policy.yaml" is
automatically detected by policy engine and the rules are updated.

Differently from other APIs, which have as default rule to enforce that
all users using the API are present in either project "admin" or
"services", all read-only actions (GET requests) of FM API are allowed
for any user, so it only requires "reader" role (that is the lowest
role). Other actions require the user to have "admin" role and to be
present in either project "admin" or "services".

As all system users of StarlingX have "admin" role and are present in
either project "admin" or "services", the default rules for FM API
allows any system users to execute any action, so there should be no
regression with the change introduced here.

To test the access control of FM API, the following commands will be
used:
fm alarm-list
fm alarm-show <uuid>
fm alarm-summary
fm alarm-delete <uuid>
fm event-list
fm event-show <uuid>
fm event-suppress --alarm_id <alarm_id>
fm event-suppress-list
fm event-unsuppress --alarm_id <alarm_id>
fm event-unsuppress-all
On test plan, these commands will be reffered as "test commands".

Note: there is one FM API that is not tested by the commands above,
that is the creation of alarms ("fm_api:alarm:create"). This API will
be tested indirectly by observing the system successfully creating
alarms in the deployed environment.

Test Plan:

PASS: Successfully deploy an AIO-SX using an Debian image with this
commit present. Successfully create, through openstack CLI, the users:
'testreader' with role 'reader' in project 'admin',
'adminsvc' with role 'admin' in project 'services' and
'otheradmin' with role 'admin' in project 'notadminproject'.
Create openrc files for all new users. Note: the other user that will be
used is the already existing 'admin' with role 'admin' in project
'admin'.
PASS: In the deployed AIO-SX, check the behavior of test commands
through different users: for "admin" and "adminsvc" users, all commands
are successful; for users "testreader" and "otheradmin", only the
commands "alarm-delete", "event-suppress", "event-unsuppress" and
"event-unsuppress-all" fail. Observe also that the system is able to
create alarms during its operation.
PASS: In the deployed AIO-SX, add the following lines in file
"/etc/fm/policy.yaml":
fm_api:alarm:create: role:admin
fm_api:alarm:delete: role:admin
fm_api:alarm:get: role:admin
fm_api:alarm:modify: role:admin
fm_api:event_log:get: role:admin
fm_api:event_suppression:get: role:admin
fm_api:event_suppression:modify: role:admin
and check that all test commands are successful through user
"otheradmin" and that all test commands fail through user "testreader".
Observe also that the system is able to create alarms during its
operation.
PASS: In the deployed AIO-SX, to assert that public API works without
authentication, execute the commands:
"curl -v http://<MGMT_IP>:18002/" and
"curl -v http://<MGMT_IP>:18002/v1/" and
verify that they are accepted and that the HTTP response is 200,
and execute the commands:
"curl -v http://<MGMT_IP>:18002/v1/alarms" and
"curl -v http://<MGMT_IP>:18002/v1/event_log" and
verify that they are rejected and that the HTTP response is 401.
PASS: In the deployed AIO-SX, check through Horizon interface that Fault
Management works correctly (showing alarms and events, allowing events
to be suppressed).
PASS: Repeat all tests above changing the deploy to AIO-DX using an
CentOS image.

Story: 2010149
Task: 46123

Signed-off-by: Joao Victor Portal <Joao.VictorPortal@windriver.com>
Change-Id: I3db6d0464d8d53c4dfbc761663be1712141b8b93
changes/11/853311/20
Joao Victor Portal 3 months ago
parent 571b0665ae
commit 99eba3afb8
  1. 8
      devstack/lib/fault
  2. 2
      fm-rest-api/centos/fm-rest-api.spec
  3. 1
      fm-rest-api/debian/deb_folder/fm-rest-api.install
  4. 1
      fm-rest-api/debian/deb_folder/postinst
  5. 3
      fm-rest-api/debian/deb_folder/rules
  6. 5
      fm-rest-api/fm/fm/api/app.py
  7. 7
      fm-rest-api/fm/fm/api/config.py
  8. 22
      fm-rest-api/fm/fm/api/controllers/v1/alarm.py
  9. 13
      fm-rest-api/fm/fm/api/controllers/v1/event_log.py
  10. 17
      fm-rest-api/fm/fm/api/controllers/v1/event_suppression.py
  11. 28
      fm-rest-api/fm/fm/api/hooks.py
  12. 21
      fm-rest-api/fm/fm/api/policies/__init__.py
  13. 74
      fm-rest-api/fm/fm/api/policies/alarm.py
  14. 30
      fm-rest-api/fm/fm/api/policies/base.py
  15. 36
      fm-rest-api/fm/fm/api/policies/event_log.py
  16. 44
      fm-rest-api/fm/fm/api/policies/event_suppression.py
  17. 7
      fm-rest-api/fm/fm/common/context.py
  18. 6
      fm-rest-api/fm/fm/common/exceptions.py
  19. 66
      fm-rest-api/fm/fm/common/policy.py
  20. 16
      fm-rest-api/fm/fm/policy.yaml
  21. 7
      fm-rest-api/fm/fm/tests/api/base.py
  22. 2
      fm-rest-api/opensuse/fm-rest-api.spec
  23. 6
      python-fmclient/fmclient/fmclient/v1/event_suppression_shell.py

@ -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

@ -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

@ -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

@ -3,5 +3,6 @@
set -e
chown fm:fm /etc/fm/fm.conf
chown fm:fm /etc/fm/policy.yaml
#DEBHELPER#

@ -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

@ -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'),

@ -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
}

@ -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()

@ -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()

@ -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()

@ -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()

@ -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()
)

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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
_ENFORCER = None
LOG = log.getLogger(__name__)
_ENFORCER = None
def reset():
"""Discard current Enforcer object."""
global _ENFORCER
_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 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)

@ -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

@ -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
},
}

@ -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

@ -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

Loading…
Cancel
Save