Files
update/sw-patch/cgcs-patch/cgcs_patch/authapi/hooks.py
Joao Victor Portal 790d7e5e2e Change access control in patching API
The access control for patching API was changed to accept GET requests
from users with reader role and presence in either admin or services
project. For other requests, it is required from the user that it has
admin role and presence in either project admin or services.

As all default system users have admin role and are present in either
admin or services project, this change should not cause regressions.

Test Plan:

PASS: Successfully deploy an AIO-SX using a Debian image with this
change present and create user "readeruser" with reader role. Log in the
Horizon interface using "readeruser" user, access page "Admin" ->
"Software Management" with no errors (a GET patches list request is
executed successfully here), try to upload a patch and check that it
fails. Repeat the steps for user "admin" and check that the patch upload
succeeds.
PASS: Successfully deploy a DC with 1 subcloud using a Debian image with
this change present and create user "readeruser" with reader role. Log
in the Horizon interface using "readeruser" user, access page
"Distributed Cloud Admin" -> "Software Management" with no errors (a GET
patches list request is executed successfully here), try to upload a
patch and check that it fails. Repeat the steps for user "admin" and
check that the patch upload succeeds.

Story: 2010149
Task: 46561

Depends-on: https://review.opendev.org/c/starlingx/gui/+/860701
Signed-off-by: Joao Victor Portal <Joao.VictorPortal@windriver.com>
Change-Id: I1b0b06ebeaadc82cd14174a46bf148c564dc7c08
2022-10-13 16:30:11 -03:00

135 lines
4.7 KiB
Python
Executable File

# -*- encoding: utf-8 -*-
#
# Copyright © 2012 New Dream Network, LLC (DreamHost)
#
# Author: Doug Hellmann <doug.hellmann@dreamhost.com> # noqa: H105
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Copyright (c) 2013-2022 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from oslo_config import cfg
from oslo_serialization import jsonutils
from pecan import hooks
from webob import exc
from cgcs_patch.authapi.context import RequestContext
from cgcs_patch.authapi import policy
from cgcs_patch import utils
class ConfigHook(hooks.PecanHook):
"""Attach the config object to the request so controllers can get to it."""
def before(self, state):
state.request.cfg = cfg.CONF
class ContextHook(hooks.PecanHook):
"""Configures a request context and attaches it to the request.
The following HTTP request headers are used:
X-User-Id or X-User:
Used for context.user_id.
X-Tenant-Id or X-Tenant:
Used for context.tenant.
X-Auth-Token:
Used for context.auth_token.
X-Roles:
Used for setting context.is_admin flag to either True or False.
The flag is set to True, if X-Roles contains either an administrator
or admin substring. Otherwise it is set to False.
X-Project-Name:
Used for context.project_name.
"""
def __init__(self, public_api_routes):
self.public_api_routes = public_api_routes
super(ContextHook, self).__init__()
def before(self, state):
user_id = state.request.headers.get('X-User-Id')
user_id = state.request.headers.get('X-User', user_id)
tenant = state.request.headers.get('X-Tenant-Id')
tenant = state.request.headers.get('X-Tenant', tenant)
project_name = state.request.headers.get('X-Project-Name')
domain_id = state.request.headers.get('X-User-Domain-Id')
domain_name = state.request.headers.get('X-User-Domain-Name')
auth_token = state.request.headers.get('X-Auth-Token', None)
roles = state.request.headers.get('X-Roles', '').split(',')
catalog_header = state.request.headers.get('X-Service-Catalog')
service_catalog = None
if catalog_header:
try:
service_catalog = jsonutils.loads(catalog_header)
except ValueError:
raise exc.HTTPInternalServerError(
'Invalid service catalog json.')
credentials = {
'project_name': project_name,
'roles': roles
}
is_admin = policy.authorize('admin_in_system_projects', {},
credentials, do_raise=False)
path = utils.safe_rstrip(state.request.path, '/')
is_public_api = path in self.public_api_routes
state.request.context = RequestContext(
auth_token=auth_token,
user=user_id,
tenant=tenant,
domain_id=domain_id,
domain_name=domain_name,
is_admin=is_admin,
is_public_api=is_public_api,
project_name=project_name,
roles=roles,
service_catalog=service_catalog)
class AccessPolicyHook(hooks.PecanHook):
"""Verify that the user has the needed credentials
to execute the action.
"""
def before(self, state):
context = state.request.context
if not context.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:
method = state.request.method
if method == 'GET':
has_api_access = policy.authorize(
'reader_in_system_projects', {},
context.to_dict(), do_raise=False)
else:
has_api_access = policy.authorize(
'admin_in_system_projects', {},
context.to_dict(), do_raise=False)
if not has_api_access:
raise exc.HTTPForbidden()