Enhancement of Tacker API resource access control

For fine-grained access control based on user and VNF information
for API resources, this patch does the following things:
1.Add three comparison attributes of area, vendor, and tenant
  for the enhanced Tacker policy.
2.Convert special roles to API attributes in context.
3.Modify the API process to support Tacker policy authorize.
4.Add the Tacker policy filter to the list API processes.

Implements: blueprint enhance-api-policy
Change-Id: I5b4c39387860133a3bcf4544f18a6353c80773f6
This commit is contained in:
kexuesheng 2023-01-20 16:00:36 +09:00 committed by Yasufumi Ogawa
parent cb89433bb3
commit 40a15b5945
49 changed files with 9231 additions and 61 deletions

View File

@ -683,6 +683,38 @@
setup_multi_az: true
controller_tacker_hostname: "{{ hostvars['controller-tacker']['ansible_hostname'] }}"
- job:
name: tacker-functional-devstack-enhanced-policy-sol
parent: tacker-functional-devstack-multinode-legacy
description: |
Enhanced policy job for SOL devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-enhanced-policy-sol
devstack_local_conf:
post-config:
$TACKER_CONF:
oslo_policy:
enhanced_tacker_policy: True
vars:
config_enhanced_policy: true
- job:
name: tacker-functional-devstack-enhanced-policy-sol-kubernetes
parent: tacker-functional-devstack-multinode-sol-kubernetes-v2
description: |
Enhanced policy job for SOL Kubernetes devstack-based functional tests
host-vars:
controller-tacker:
tox_envlist: dsvm-functional-enhanced-policy-sol-kubernetes
devstack_local_conf:
post-config:
$TACKER_CONF:
oslo_policy:
enhanced_tacker_policy: True
vars:
config_enhanced_policy: true
- job:
name: tacker-compliance-devstack-multinode-sol
parent: tacker-functional-devstack-multinode-legacy
@ -720,4 +752,6 @@
- tacker-functional-devstack-multinode-sol-kubernetes-multi-tenant
- tacker-functional-devstack-kubernetes-oidc-auth
- tacker-functional-devstack-multinode-sol-v2-az-retry
- tacker-functional-devstack-enhanced-policy-sol
- tacker-functional-devstack-enhanced-policy-sol-kubernetes
- tacker-compliance-devstack-multinode-sol

File diff suppressed because it is too large Load Diff

View File

@ -60,3 +60,4 @@ Use Case Guide
fault_notification_use_case_guide
prometheus_plugin_use_case_guide
db_migration_tool_usage_guide
enhanced_tacker_policy_usage_guide

View File

@ -0,0 +1,251 @@
# Decides what is required for the 'is_admin:True' check to succeed.
"context_is_admin": "role:admin"
# Default rule for most non-Admin APIs.
"admin_or_owner": "is_admin:True or project_id:%(project_id)s"
# Default rule for most Admin APIs.
"admin_only": "is_admin:True"
# Default rule for sharing vims.
"shared": "field:vims:shared=True"
# Default rule for most non-Admin APIs.
"default": "rule:admin_or_owner"
# For manager
"manager_and_owner": "role:manager and project_id:%(project_id)s"
# For user
"owner": "project_id:%(project_id)s"
# VIM resource attributes compare rule.
"vim_attrs_cmp": "area:%(area)s"
# Register a VIM.
# Post /v1.0/vims
"create_vim": "rule:manager_and_owner or role:admin"
# List VIMs or show a VIM.
# GET /v1.0/vims
# GET /v1.0/vims/{vim_id}
"get_vim": "rule:vim_attrs_cmp or role:admin"
# Update a VIM.
# PUT /v1.0/vims/{vim_id}
"update_vim": "rule:vim_attrs_cmp and rule:manager_and_owner or role:admin"
# Delete a VIM.
# DELETE /v1.0/vims/{vim_id}
"delete_vim": "rule:vim_attrs_cmp and rule:manager_and_owner or role:admin"
# vnf_packages resource attributes compare rule.
"vnf_pkg_attrs_cmp": "vendor:%(vendor)s"
# Creates a VNF package.
# POST /vnf_packages
"os_nfv_orchestration_api:vnf_packages:create": "rule:admin_or_owner"
# Show a VNF package.
# GET /vnf_packages/{vnf_package_id}
"os_nfv_orchestration_api:vnf_packages:show": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# List all VNF packages.
# GET /vnf_packages/
"os_nfv_orchestration_api:vnf_packages:index": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# Delete a VNF package.
# DELETE /vnf_packages/{vnf_package_id}
"os_nfv_orchestration_api:vnf_packages:delete": "rule:vnf_pkg_attrs_cmp and rule:manager_and_owner or role:admin"
# Fetch the contents of an on-boarded VNF Package.
# GET /vnf_packages/{vnf_package_id}/package_content
"os_nfv_orchestration_api:vnf_packages:fetch_package_content": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# Upload a VNF package content.
# PUT /vnf_packages/{vnf_package_id}/package_content
"os_nfv_orchestration_api:vnf_packages:upload_package_content": "rule:vnf_pkg_attrs_cmp or role:admin"
# Upload a VNF package content from URI.
# POST /vnf_packages/{vnf_package_id}/package_content/upload_from_uri
"os_nfv_orchestration_api:vnf_packages:upload_from_uri": "rule:admin_or_owner"
# Update information of VNF package.
# PATCH /vnf_packages/{vnf_package_id}
"os_nfv_orchestration_api:vnf_packages:patch": "rule:vnf_pkg_attrs_cmp and rule:manager_and_owner or role:admin"
# Read the content of the VNFD within a VNF package.
# GET /vnf_packages/{vnf_package_id}/vnfd
"os_nfv_orchestration_api:vnf_packages:get_vnf_package_vnfd": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# Read the content of the artifact within a VNF package.
# GET /vnf_packages/{vnfPkgId}/artifacts/{artifactPath}
"os_nfv_orchestration_api:vnf_packages:fetch_artifact": "rule:vnf_pkg_attrs_cmp and rule:owner or role:admin"
# vnflcm create attributes compare rule.
"vnflcm_create_attrs_cmp": "vendor:%(vendor)s"
# vnflcm instantiate attributes compare rule.
"vnflcm_inst_attrs_cmp": "vendor:%(vendor)s"
# vnflcm delete attributes compare rule.
"vnflcm_delete_attrs_cmp": "vendor:%(vendor)s"
# vnflcm resource attributes compare rule.
"vnflcm_attrs_cmp": "area:%(area)s and vendor:%(vendor)s and tenant:%(tenant)s"
# Get API Versions.
# GET /vnflcm/v1/api_versions
"os_nfv_orchestration_api:vnf_instances:api_versions": "@"
# Create VNF instance.
# POST /vnflcm/v1/vnf_instances
"os_nfv_orchestration_api:vnf_instances:create": "rule:vnflcm_create_attrs_cmp and rule:manager_and_owner or role:admin"
# Instantiate VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/instantiate
"os_nfv_orchestration_api:vnf_instances:instantiate": "rule:vnflcm_inst_attrs_cmp and rule:manager_and_owner or role:admin"
# Query an Individual VNF instance.
# GET /vnflcm/v1/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api:vnf_instances:show": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Terminate a VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate
"os_nfv_orchestration_api:vnf_instances:terminate": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Heal a VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/heal
"os_nfv_orchestration_api:vnf_instances:heal": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Scale a VNF instance.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/scale
"os_nfv_orchestration_api:vnf_instances:scale": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Query an Individual VNF LCM operation occurrence.
# GET /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}
"os_nfv_orchestration_api:vnf_instances:show_lcm_op_occs": "rule:admin_or_owner"
# Query VNF LCM operation occurrence.
# GET /vnflcm/v1/vnf_lcm_op_occs
"os_nfv_orchestration_api:vnf_instances:list_lcm_op_occs": "rule:admin_or_owner"
# Query VNF instances.
# GET /vnflcm/v1/vnf_instances
"os_nfv_orchestration_api:vnf_instances:index": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Delete an Individual VNF instance.
# DELETE /vnflcm/v1/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api:vnf_instances:delete": "rule:vnflcm_delete_attrs_cmp and rule:manager_and_owner or role:admin"
# Update an Individual VNF instance.
# PATCH /vnflcm/v1/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api:vnf_instances:update_vnf": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Rollback a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/rollback
"os_nfv_orchestration_api:vnf_instances:rollback": "rule:admin_or_owner"
# Cancel a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/cancel
"os_nfv_orchestration_api:vnf_instances:cancel": "rule:admin_or_owner"
# Fail a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/fail
"os_nfv_orchestration_api:vnf_instances:fail": "rule:admin_or_owner"
# Retry a VNF instance.
# POST /vnflcm/v1/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry
"os_nfv_orchestration_api:vnf_instances:retry": "rule:admin_or_owner"
# Change external VNF connectivity.
# POST /vnflcm/v1/vnf_instances/{vnfInstanceId}/change_ext_conn
"os_nfv_orchestration_api:vnf_instances:change_ext_conn": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Get API Versions.
# GET /vnflcm/v2/api_versions
"os_nfv_orchestration_api_v2:vnf_instances:api_versions": "@"
# Creates VNF instance.
# POST /vnflcm/v2/vnf_instances
"os_nfv_orchestration_api_v2:vnf_instances:create": "rule:vnflcm_create_attrs_cmp and rule:manager_and_owner or role:admin"
# Query VNF instances.
# GET /vnflcm/v2/vnf_instances
"os_nfv_orchestration_api_v2:vnf_instances:index": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Query an Individual VNF instance.
# GET /vnflcm/v2/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api_v2:vnf_instances:show": "rule:vnflcm_attrs_cmp and rule:owner or role:admin"
# Delete an Individual VNF instance.
# DELETE /vnflcm/v2/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api_v2:vnf_instances:delete": "rule:vnflcm_delete_attrs_cmp and rule:manager_and_owner or role:admin"
# Modify VNF instance information.
# PATCH /vnflcm/v2/vnf_instances/{vnfInstanceId}
"os_nfv_orchestration_api_v2:vnf_instances:update": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Instantiate VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/instantiate
"os_nfv_orchestration_api_v2:vnf_instances:instantiate": "rule:vnflcm_inst_attrs_cmp and rule:manager_and_owner or role:admin"
# Terminate VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/terminate
"os_nfv_orchestration_api_v2:vnf_instances:terminate": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Scale VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/scale
"os_nfv_orchestration_api_v2:vnf_instances:scale": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Heal VNF instance.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/heal
"os_nfv_orchestration_api_v2:vnf_instances:heal": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Change external VNF connectivity.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/change_ext_conn
"os_nfv_orchestration_api_v2:vnf_instances:change_ext_conn": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Change VNF package.
# POST /vnflcm/v2/vnf_instances/{vnfInstanceId}/change_vnfpkg
"os_nfv_orchestration_api_v2:vnf_instances:change_vnfpkg": "rule:vnflcm_attrs_cmp and rule:manager_and_owner or role:admin"
# Create subscription.
# POST /vnflcm/v2/subscriptions
"os_nfv_orchestration_api_v2:vnf_instances:subscription_create": "@"
# List subscription.
# GET /vnflcm/v2/subscriptions
"os_nfv_orchestration_api_v2:vnf_instances:subscription_list": "@"
# Show subscription.
# GET /vnflcm/v2/vnf_instances/{subscriptionId}
"os_nfv_orchestration_api_v2:vnf_instances:subscription_show": "@"
# Delete subscription.
# DELETE /vnflcm/v2/vnf_instances/{subscriptionId}
"os_nfv_orchestration_api_v2:vnf_instances:subscription_delete": "@"
# List VnfLcmOpOcc.
# GET /vnflcm/v2/vnf_lcm_op_occs
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_list": "@"
# Show VnfLcmOpOcc.
# GET /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_show": "@"
# Retry VnfLcmOpOcc.
# POST /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}/retry
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_retry": "@"
# Rollback VnfLcmOpOcc.
# POST /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}/rollback
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_rollback": "@"
# Fail VnfLcmOpOcc.
# POST /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}/fail
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_fail": "@"
# Delete VnfLcmOpOcc.
# DELETE /vnflcm/v2/vnf_lcm_op_occs/{vnfLcmOpOccId}
"os_nfv_orchestration_api_v2:vnf_instances:lcm_op_occ_delete": "@"

View File

@ -15,6 +15,8 @@
when: setup_multi_az is defined and setup_multi_az | bool
- role: setup-fake-https-server
when: https_setup is defined and https_setup | bool
- role: config-enhanced-policy
when: config_enhanced_policy is defined and config_enhanced_policy | bool
- role: bindep
bindep_profile: test
bindep_dir: "{{ zuul_work_dir }}"

View File

@ -0,0 +1,6 @@
---
features:
- |
Enhance the existing Tacker policy function so that users can obtain more
fine-grained access control based on user roles and VNF information for
API resources.

View File

@ -0,0 +1,12 @@
- block:
- name: Copy policy.yaml
copy:
src: "{{ devstack_base_dir }}/tacker/etc/tacker/enhanced_tacker_policy.yaml.sample"
dest: "/etc/tacker/policy.yaml"
remote_src: true
mode: 0644
owner: stack
group: stack
become: yes
when:
- inventory_hostname == 'controller-tacker'

View File

@ -288,7 +288,7 @@ class VnfLcmController(wsgi.Controller):
vim_client_obj = vim_client.VimClient()
try:
vim_client_obj.get_vim(
return vim_client_obj.get_vim(
context, vim_id, region_name=region_name)
except nfvo.VimDefaultNotDefined as exp:
raise webob.exc.HTTPBadRequest(explanation=exp.message)
@ -439,7 +439,8 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.create)
def create(self, request, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'create')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'create')
try:
req_body = utils.convert_camelcase_to_snakecase(body)
vnfd_id = req_body.get('vnfd_id')
@ -466,6 +467,10 @@ class VnfLcmController(wsgi.Controller):
vnfd_id)
return self._make_problem_detail(
msg, 500, 'Internal Server Error')
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'create',
target={'vendor': vnfd.vnf_provider})
try:
# get default vim information
vim_client_obj = vim_client.VimClient()
@ -489,6 +494,17 @@ class VnfLcmController(wsgi.Controller):
try:
vnf_instance.create()
vim_conn = objects.VimConnectionInfo(
id=uuidutils.generate_uuid(),
vim_id=default_vim.get('vim_id'),
vim_type=default_vim.get('vim_type'),
access_info={},
interface_info={},
extra=default_vim.get('extra', {})
)
vnf_instance.vim_connection_info = [vim_conn]
vnf_instance.save()
# create entry to 'vnf' table and 'vnf_attribute' table
attributes = {'placement_attr': default_vim.
get('placement_attr', {})}
@ -560,12 +576,52 @@ class VnfLcmController(wsgi.Controller):
return vnf_package_info[0]
def _get_policy_target(self, vnf_instance):
vendor = vnf_instance.vnf_provider
area = None
if vnf_instance.vim_connection_info:
for connection_info in vnf_instance.vim_connection_info:
area = connection_info.extra.get('area')
if area:
break
tenant = None
if (vnf_instance.instantiation_state ==
fields.VnfInstanceState.INSTANTIATED):
if vnf_instance.vnf_metadata:
tenant = vnf_instance.vnf_metadata.get('namespace')
# TODO(kexuesheng): Add steps to get tenant of VNFs deployed
# in OpenStack VIM. This is a temporary workaround until that
# information is available.
if vnf_instance.vim_connection_info:
vim_type = vnf_instance.vim_connection_info[0].vim_type
if vim_type in ["openstack", "ETSINFV.OPENSTACK_KEYSTONE.v_2"]:
tenant = '*'
else:
tenant = '*'
target = {
'vendor': vendor,
'area': area,
'tenant': tenant
}
target = {k: v for k, v in target.items() if v is not None}
return target
@wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def show(self, request, id):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'show')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'show')
vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'show',
target=self._get_policy_target(vnf_instance))
return self._view_builder.show(vnf_instance)
@ -579,8 +635,8 @@ class VnfLcmController(wsgi.Controller):
@api_common.validate_supported_params({'filter', 'nextpage_opaque_marker',
'all_records'})
def index(self, request):
if 'tacker.context' in request.environ:
context = request.environ['tacker.context']
context = request.environ['tacker.context']
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'index')
filters = request.GET.get('filter')
@ -611,6 +667,13 @@ class VnfLcmController(wsgi.Controller):
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
if CONF.oslo_policy.enhanced_tacker_policy:
result = [vnf_instance for vnf_instance in result
if context.can(
vnf_lcm_policies.VNFLCM % 'index',
target=self._get_policy_target(vnf_instance),
fatal=False)]
result = self._view_builder.index(result)
res = webob.Response(content_type='application/json')
@ -650,6 +713,10 @@ class VnfLcmController(wsgi.Controller):
context = request.environ['tacker.context']
vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'delete',
target=self._get_policy_target(vnf_instance))
vnf = self._get_vnf(context, id)
self._delete(context, vnf_instance, vnf)
@ -683,7 +750,12 @@ class VnfLcmController(wsgi.Controller):
req_body, context=context)
# validate the vim connection id passed through request is exist or not
self._validate_vim_connection(context, instantiate_vnf_request)
vim_res = self._validate_vim_connection(
context, instantiate_vnf_request)
if instantiate_vnf_request.vim_connection_info:
instantiate_vnf_request.vim_connection_info[0].extra = vim_res.get(
'extra', {})
vnf_instance.task_state = fields.VnfInstanceTaskState.INSTANTIATING
vnf_instance.save()
@ -715,10 +787,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.instantiate)
def instantiate(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'instantiate')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'instantiate')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'instantiate',
target=self._get_policy_target(vnf_instance))
return self._instantiate(context, vnf_instance, vnf, body)
@ -766,10 +842,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.terminate)
def terminate(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'terminate')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'terminate')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'terminate',
target=self._get_policy_target(vnf_instance))
return self._terminate(context, vnf_instance, vnf, body)
@check_vnf_status_and_error_point(action="heal",
@ -822,10 +902,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.heal)
def heal(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'heal')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'heal')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'heal',
target=self._get_policy_target(vnf_instance))
if vnf_instance.instantiation_state not in \
[fields.VnfInstanceState.INSTANTIATED]:
@ -865,7 +949,12 @@ class VnfLcmController(wsgi.Controller):
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def update(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf')
if CONF.oslo_policy.enhanced_tacker_policy:
vnf_instance = self._get_vnf_instance(context, id)
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf',
target=self._get_policy_target(vnf_instance))
else:
context.can(vnf_lcm_policies.VNFLCM % 'update_vnf')
# get body
body_data = {}
@ -1343,7 +1432,8 @@ class VnfLcmController(wsgi.Controller):
http_client.NOT_FOUND, http_client.CONFLICT))
def scale(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'scale')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'scale')
try:
vnf_info = self._vnfm_plugin.get_vnf(context, id)
@ -1351,6 +1441,9 @@ class VnfLcmController(wsgi.Controller):
return self._make_problem_detail(
'VNF IS NOT ACTIVE', 409, title='VNF IS NOT ACTIVE')
vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'scale',
target=self._get_policy_target(vnf_instance))
if not vnf_instance.instantiated_vnf_info.scale_status:
return self._make_problem_detail(
'NOT SCALE VNF', 409, title='NOT SCALE VNF')
@ -1361,6 +1454,8 @@ class VnfLcmController(wsgi.Controller):
except webob.exc.HTTPNotFound as inst_e:
return self._make_problem_detail(
str(inst_e), 404, title='VNF NOT FOUND')
except exceptions.PolicyNotAuthorized:
raise
except Exception as e:
LOG.error(traceback.format_exc())
return self._make_problem_detail(
@ -1819,10 +1914,14 @@ class VnfLcmController(wsgi.Controller):
@validation.schema(vnf_lcm.change_ext_conn)
def change_ext_conn(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn')
vnf = self._get_vnf(context, id)
vnf_instance = self._get_vnf_instance(context, id)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_lcm_policies.VNFLCM % 'change_ext_conn',
target=self._get_policy_target(vnf_instance))
if (vnf_instance.instantiation_state !=
fields.VnfInstanceState.INSTANTIATED):
return self._make_problem_detail(

View File

@ -104,7 +104,8 @@ class VnfPkgmController(wsgi.Controller):
@wsgi.expected_errors((http_client.FORBIDDEN, http_client.NOT_FOUND))
def show(self, request, id):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'show')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'show')
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
@ -115,18 +116,33 @@ class VnfPkgmController(wsgi.Controller):
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id, expected_attrs=[
"vnf_deployment_flavours", "vnfd", "vnf_artifacts"])
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'show',
target=self._get_policy_target(vnf_package))
except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)
return self._view_builder.show(vnf_package)
def _get_policy_target(self, vnf_package):
if vnf_package.onboarding_state == \
fields.PackageOnboardingStateType.ONBOARDED:
vendor = (vnf_package.vnfd.get('vnf_provider')
if vnf_package.vnfd is not None else None)
return {'vendor': vendor} if vendor else {}
else:
return {'vendor': '*'}
@wsgi.response(http_client.OK)
@wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN))
@validation.query_schema(vnf_packages.query_params_v1)
def index(self, request):
if 'tacker.context' in request.environ:
context = request.environ['tacker.context']
context = request.environ['tacker.context']
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'index')
search_opts = {}
@ -178,6 +194,13 @@ class VnfPkgmController(wsgi.Controller):
return self._make_problem_detail(
str(e), 500, title='Internal Server Error')
if CONF.oslo_policy.enhanced_tacker_policy:
result = [vnf_package for vnf_package in result
if context.can(
vnf_package_policies.VNFPKGM % 'index',
target=self._get_policy_target(vnf_package),
fatal=False)]
results = self._view_builder.index(result,
all_fields=all_fields,
exclude_fields=exclude_fields,
@ -206,9 +229,13 @@ class VnfPkgmController(wsgi.Controller):
http_client.CONFLICT))
def delete(self, request, id):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'delete')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'delete')
vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'delete',
target=self._get_policy_target(vnf_package))
if (vnf_package.operational_state ==
fields.PackageOperationalStateType.ENABLED or
@ -244,9 +271,13 @@ class VnfPkgmController(wsgi.Controller):
http_client.REQUESTED_RANGE_NOT_SATISFIABLE))
def fetch_vnf_package_content(self, request, id):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'fetch_package_content')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'fetch_package_content')
vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'fetch_package_content',
target=self._get_policy_target(vnf_package))
if vnf_package.onboarding_state != \
fields.PackageOnboardingStateType.ONBOARDED:
@ -367,7 +398,9 @@ class VnfPkgmController(wsgi.Controller):
http_client.CONFLICT))
def upload_vnf_package_content(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'upload_package_content')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(
vnf_package_policies.VNFPKGM % 'upload_package_content')
# check if id is of type uuid format
if not uuidutils.is_uuid_like(id):
@ -404,6 +437,13 @@ class VnfPkgmController(wsgi.Controller):
try:
(location, size, checksum, multihash,
loc_meta) = glance_store.store_csar(context, id, body)
if CONF.oslo_policy.enhanced_tacker_policy:
zip_path = glance_store.load_csar(vnf_package.id, location)
vnf_data, flavours, vnf_artifacts = csar_utils.load_csar_data(
context.elevated(), vnf_package.id, zip_path)
context.can(
vnf_package_policies.VNFPKGM % 'upload_package_content',
target={'vendor': vnf_data.get('provider')})
except exceptions.UploadFailedToGlanceStore:
with excutils.save_and_reraise_exception():
vnf_package.onboarding_state = (
@ -413,6 +453,19 @@ class VnfPkgmController(wsgi.Controller):
except Exception as e:
return self._make_problem_detail(
'Internal Server Error', str(e), 500)
except exceptions.Forbidden:
with excutils.save_and_reraise_exception():
vnf_package.onboarding_state = (
fields.PackageOnboardingStateType.CREATED)
try:
# Delete from glance store
glance_store.delete_csar(context, vnf_package.id,
location)
csar_utils.delete_csar_data(vnf_package.id)
vnf_package.save()
except Exception as e:
return self._make_problem_detail(
'Internal Server Error', str(e), 500)
vnf_package.algorithm = CONF.vnf_package.hashing_algorithm
vnf_package.hash = multihash
@ -475,9 +528,13 @@ class VnfPkgmController(wsgi.Controller):
@validation.schema(vnf_packages.patch)
def patch(self, request, id, body):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'patch')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'patch')
old_vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'patch',
target=self._get_policy_target(old_vnf_package))
vnf_package = old_vnf_package.obj_clone()
user_data = body.get('userDefinedData')
@ -529,7 +586,8 @@ class VnfPkgmController(wsgi.Controller):
http_client.INTERNAL_SERVER_ERROR))
def get_vnf_package_vnfd(self, request, id):
context = request.environ['tacker.context']
context.can(vnf_package_policies.VNFPKGM % 'get_vnf_package_vnfd')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'get_vnf_package_vnfd')
valid_accept_headers = ['application/zip', 'text/plain']
accept_headers = request.headers['Accept'].split(',')
@ -543,6 +601,9 @@ class VnfPkgmController(wsgi.Controller):
valid_accept_headers)})
vnf_package = self._get_vnf_package(id, request)
if CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'get_vnf_package_vnfd',
target=self._get_policy_target(vnf_package))
if vnf_package.onboarding_state != \
fields.PackageOnboardingStateType.ONBOARDED:
@ -586,7 +647,8 @@ class VnfPkgmController(wsgi.Controller):
def fetch_vnf_package_artifacts(self, request, id, artifact_path):
context = request.environ['tacker.context']
# get policy
context.can(vnf_package_policies.VNFPKGM % 'fetch_artifact')
if not CONF.oslo_policy.enhanced_tacker_policy:
context.can(vnf_package_policies.VNFPKGM % 'fetch_artifact')
# get vnf_package
if not uuidutils.is_uuid_like(id):
@ -597,6 +659,10 @@ class VnfPkgmController(wsgi.Controller):
vnf_package = vnf_package_obj.VnfPackage.get_by_id(
request.context, id,
expected_attrs=["vnf_artifacts"])
if CONF.oslo_policy.enhanced_tacker_policy:
# get policy
context.can(vnf_package_policies.VNFPKGM % 'fetch_artifact',
target=self._get_policy_target(vnf_package))
except exceptions.VnfPackageNotFound:
msg = _("Can not find requested vnf package: %s") % id
raise webob.exc.HTTPNotFound(explanation=msg)

View File

@ -174,6 +174,18 @@ def is_valid_ipv4(address):
return False
def is_valid_area(area):
"""Verify that the area attribute is valid.
Area attribute is an area-region pair. The value of this attribute should
be a string in the format of "area@region".
"""
try:
return re.match(r'\w+@\w+', area) is not None
except Exception:
return False
def change_memory_unit(mem, to):
"""Change the memory value(mem) based on the unit('to') specified.

View File

@ -18,6 +18,7 @@ from oslo_config import cfg
from tacker.conf import conductor
from tacker.conf import coordination
from tacker.conf import policy
from tacker.conf import vnf_lcm
from tacker.conf import vnf_package
@ -29,3 +30,4 @@ vnf_lcm.register_opts(CONF)
conductor.register_opts(CONF)
coordination.register_opts(CONF)
glance_store.register_opts(CONF)
policy.register_opts(CONF)

33
tacker/conf/policy.py Normal file
View File

@ -0,0 +1,33 @@
# Copyright (C) 2023 Fujitsu
# All Rights Reserved.
#
# 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.
from oslo_config import cfg
CONF = cfg.CONF
policy_opts = [
cfg.BoolOpt('enhanced_tacker_policy',
default=False,
help=_('Enable enhanced tacker policy')),
]
def register_opts(conf):
conf.register_opts(policy_opts, group='oslo_policy')
def list_opts():
return {'oslo_policy': policy_opts}

View File

@ -148,11 +148,14 @@ class ContextBase(oslo_context.RequestContext):
:return: returns a non-False value (not necessarily "True") if
authorized and False if not authorized and fatal is False.
"""
if target is None:
target = {'project_id': self.tenant_id,
'user_id': self.user_id}
tgt = {
'project_id': self.tenant_id,
'user_id': self.user_id
}
if target is not None:
tgt.update(target)
try:
return policy.authorize(self, action, target)
return policy.authorize(self, action, tgt)
except exceptions.Forbidden:
if fatal:
raise

View File

@ -14,6 +14,7 @@
# under the License.
from collections import abc
import copy
import re
import sys
@ -28,6 +29,7 @@ from oslo_utils import importutils
from tacker._i18n import _
from tacker.api.v1 import attributes
from tacker.common import exceptions
from tacker.common.utils import is_valid_area
from tacker import policies
@ -60,10 +62,133 @@ def init(conf=cfg.CONF, policy_file=None):
_ENFORCER.load_rules()
def _pre_enhanced_policy_check(target, credentials):
"""Preprocesses target and credentials for enhanced tacker policy.
This method does the following things:
1) Convert special roles to enhanced policy attributes in credentials.
Note: Special roles are roles that have prefixes 'AREA_', 'VENDOR_',
or 'TENANT_'.
Example::
Before conversion:
credentials = {
'roles': [
'AREA_area_A@region_A',
'VENDOR_company_A',
'TENANT_default'
]
}
After conversion:
credentials = {
'roles': [
'AREA_area_A@region_A',
'VENDOR_company_A',
'TENANT_default'
],
'area': ['*', 'area_A@region_A'],
'vendor': ['*', 'company_A'],
'tenant: ['*', 'default']
}
2) Convert special value `all` to the corresponding attribute value in
target.
:param target: a dictionary of the attributes of the object
being accessed.
:param credentials: The information about the user performing the action.
:return tgt: The preprocessed target is returned.
:return user_attrs: The preprocessed credentials is returned.
"""
if not cfg.CONF.oslo_policy.enhanced_tacker_policy:
return target, credentials
if target is None:
tgt = {}
else:
tgt = copy.copy(target)
LOG.debug(f'target: {target}')
convert_map = {
'area_': 'area',
'vendor_': 'vendor',
'tenant_': 'tenant'
}
user_attrs = {
'area': ['*'],
'vendor': ['*'],
'tenant': ['*']
}
# Convert special roles to enhanced policy attributes in credentials.
for role in credentials.get('roles'):
role = role.lower()
for prefix, key in convert_map.items():
if role.startswith(prefix):
attr = role[len(prefix):]
if attr:
user_attrs[key].append(attr)
common_keys = user_attrs.keys() & tgt.keys()
# Convert special value `all` to the corresponding attribute value in
# target.
for key in common_keys:
tgt[key] = tgt[key].lower()
attrs = user_attrs.get(key)
if tgt.get(key) == '*':
continue
to_remove = []
if 'area' == key:
if not is_valid_area(tgt.get(key)):
continue
for attr in attrs:
if not is_valid_area(attr):
continue
if 'all@all' == attr:
# example:
# target = {'area': 'area_A@region_A'}
# then:
# 'all@all' -> 'area_A@region_A'
to_remove.append('all@all')
attrs.append(tgt.get(key))
elif attr.startswith('all@'):
to_remove.append(attr)
area, region = attr.split('@', 1)
t_area, t_region = tgt.get(key).split('@', 1)
if region == t_region:
# example:
# target = {'area': 'area_A@region_A'}
# then:
# 'all@region_A' -> 'area_A@region_A'
attrs.append(f'{t_area}@{region}')
# else:
# example:
# target = {'area': 'area_A@region_B'}
# then:
# 'all@region_A' -> to be removed.
else:
for attr in attrs:
if 'all' == attr:
# example:
# target = {'vendor': 'company_A'}
# then:
# 'all' -> 'company_A'
to_remove.append('all')
attrs.append(tgt.get(key))
for item in to_remove:
attrs.remove(item)
user_attrs.update(credentials)
return tgt, user_attrs
def authorize(context, action, target, do_raise=True, exc=None):
init()
credentials = context.to_policy_values()
target, credentials = _pre_enhanced_policy_check(target, credentials)
if not exc:
exc = exceptions.PolicyNotAuthorized
try:
@ -387,6 +512,15 @@ def check(context, action, target, plugin=None, might_not_exist=False,
action,
target,
pluralized)
target = copy.copy(target)
if 'area' not in target:
area = target.get('extra', {}).get('area')
if area:
target.update({'area': area})
if 'tenant_id' in target:
target['project_id'] = target['tenant_id']
target, credentials = _pre_enhanced_policy_check(target, credentials)
result = _ENFORCER.enforce(match_rule,
target,
credentials,
@ -397,7 +531,8 @@ def check(context, action, target, plugin=None, might_not_exist=False,
return result
def enforce(context, action, target, plugin=None, pluralized=None):
def enforce(context, action, target, plugin=None, pluralized=None,
exc=exceptions.PolicyNotAuthorized):
"""Verifies that the action is valid on the target in this context.
:param context: tacker context
@ -410,9 +545,12 @@ def enforce(context, action, target, plugin=None, pluralized=None):
Kept for backward compatibility.
:param pluralized: pluralized case of resource
e.g. firewall_policy -> pluralized = "firewall_policies"
:param exc: Class of the exception to raise if the check fails.
If not specified, :class:`PolicyNotAuthorized` will be used.
:raises oslo_policy.policy.PolicyNotAuthorized:
if verification fails.
:raises tacker.common.exceptions.PolicyNotAuthorized or exc specified by
caller:
if verification fails.
"""
# If we already know the context has admin rights do not perform an
# additional check and authorize the operation
@ -422,10 +560,19 @@ def enforce(context, action, target, plugin=None, pluralized=None):
action,
target,
pluralized)
target = copy.copy(target)
if 'area' not in target:
area = target.get('extra', {}).get('area')
if area:
target.update({'area': area})
if 'tenant_id' in target:
target['project_id'] = target['tenant_id']
target, credentials = _pre_enhanced_policy_check(target, credentials)
try:
result = _ENFORCER.enforce(rule, target, credentials, action=action,
do_raise=True)
except policy.PolicyNotAuthorized:
do_raise=True, exc=exc)
except Exception:
with excutils.save_and_reraise_exception():
log_rule_list(rule)
LOG.error("Failed policy check for '%s'", action)

View File

@ -36,6 +36,20 @@ VNF_LCM_OP_OCCS_PATH = V2_PATH + '/vnf_lcm_op_occs'
VNF_LCM_OP_OCCS_ID_PATH = VNF_LCM_OP_OCCS_PATH + '/{vnfLcmOpOccId}'
SERVER_NOTIFICATION_PATH = CONF.server_notification.uri_path_prefix
ENHANCED_POLICY_ACTIONS = (
POLICY_NAME.format('create'),
POLICY_NAME.format('index'),
POLICY_NAME.format('show'),
POLICY_NAME.format('delete'),
POLICY_NAME.format('update'),
POLICY_NAME.format('instantiate'),
POLICY_NAME.format('terminate'),
POLICY_NAME.format('scale'),
POLICY_NAME.format('heal'),
POLICY_NAME.format('change_ext_conn'),
POLICY_NAME.format('change_vnfpkg'),
)
rules = [
policy.DocumentedRuleDefault(
name=POLICY_NAME.format('api_versions'),

View File

@ -24,6 +24,8 @@ from tacker.common import exceptions as common_ex
from tacker import context
from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.api.policies.vnflcm_v2 import (
ENHANCED_POLICY_ACTIONS)
from tacker.sol_refactored.common import config
from tacker.sol_refactored.common import exceptions as sol_ex
@ -159,7 +161,9 @@ class SolResource(object):
return
if action == 'reject':
return
request.context.can(self.policy_name.format(action))
if not (self.policy_name.format(action) in ENHANCED_POLICY_ACTIONS
and config.CONF.oslo_policy.enhanced_tacker_policy):
request.context.can(self.policy_name.format(action))
def _dispatch(self, request, action, action_args):
controller_method = getattr(self.controller, action)

View File

@ -48,6 +48,7 @@ def vim_to_conn_info(vim):
region = vim['placement_attr']['regions'][0]
vim_auth = vim['vim_auth']
extra = vim.get('extra', {})
if vim['vim_type'] == "openstack":
# see. https://nfvwiki.etsi.org/index.php
@ -73,7 +74,8 @@ def vim_to_conn_info(vim):
vimId=vim['vim_id'],
vimType='ETSINFV.OPENSTACK_KEYSTONE.V_3',
interfaceInfo=interface_info,
accessInfo=access_info
accessInfo=access_info,
extra=extra
)
if vim['vim_type'] == "kubernetes":
# When vimType is kubernetes, it will be converted to the vimType name
@ -116,7 +118,8 @@ def vim_to_conn_info(vim):
vimId=vim['vim_id'],
vimType=vim_type,
interfaceInfo=interface_info,
accessInfo=access_info
accessInfo=access_info,
extra=extra
)
raise sol_ex.SolException(sol_detail='not support vim type')

View File

@ -33,8 +33,9 @@ from tacker.sol_refactored.objects.v2 import fields as v2fields
# and the creation of lcmocc and the call to start_lcm_op are
# all executed by the controller, notification driver, etc.
@coordinate.lock_vnf_instance('{vnf_instance_id}')
def heal(context, vnf_instance_id, body):
inst = inst_utils.get_inst(context, vnf_instance_id)
def heal(context, vnf_instance_id, body, inst=None):
if not inst:
inst = inst_utils.get_inst(context, vnf_instance_id)
if inst.instantiationState != 'INSTANTIATED':
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id)
@ -73,8 +74,9 @@ def heal(context, vnf_instance_id, body):
# and the creation of lcmocc and the call to start_lcm_op are
# all executed by the controller, notification driver, etc.
@coordinate.lock_vnf_instance('{vnf_instance_id}')
def scale(context, vnf_instance_id, body):
inst = inst_utils.get_inst(context, vnf_instance_id)
def scale(context, vnf_instance_id, body, inst=None):
if not inst:
inst = inst_utils.get_inst(context, vnf_instance_id)
if inst.instantiationState != 'INSTANTIATED':
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id)

View File

@ -18,6 +18,7 @@ from oslo_log import log as logging
from oslo_utils import uuidutils
from tacker.sol_refactored.api import api_version
from tacker.sol_refactored.api.policies.vnflcm_v2 import POLICY_NAME
from tacker.sol_refactored.api.schemas import vnflcm_v2 as schema
from tacker.sol_refactored.api import validator
from tacker.sol_refactored.api import wsgi as sol_wsgi
@ -62,6 +63,10 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
pkg_info = self.nfvo_client.get_vnf_package_info_vnfd(
context, vnfd_id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('create'),
target={'vendor': pkg_info.vnfProvider})
if pkg_info.operationalState != "ENABLED":
raise sol_ex.VnfdIdNotEnabled(vnfd_id=vnfd_id)
@ -122,6 +127,11 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
insts = inst_utils.get_inst_all(request.context,
marker=pager.marker)
if config.CONF.oslo_policy.enhanced_tacker_policy:
insts = [inst for inst in insts if request.context.can(
POLICY_NAME.format('index'),
target=self._get_policy_target(inst),
fatal=False)]
resp_body = self._inst_view.detail_list(insts, filters,
selector, pager)
@ -130,7 +140,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def show(self, request, id):
inst = inst_utils.get_inst(request.context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
request.context.can(POLICY_NAME.format('show'),
target=self._get_policy_target(inst))
resp_body = self._inst_view.detail(inst)
return sol_wsgi.SolResponse(200, resp_body)
@ -139,7 +151,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def delete(self, request, id):
context = request.context
inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('delete'),
target=self._get_policy_target(inst))
if inst.instantiationState != 'NOT_INSTANTIATED':
raise sol_ex.VnfInstanceIsInstantiated(inst_id=id)
@ -158,7 +172,9 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def update(self, request, id, body):
context = request.context
inst = inst_utils.get_inst(context, id)
if config.CONF.oslo_policy.enhanced_tacker_policy:
context.can(POLICY_NAME.format('update'),
target=self._get_policy_target(inst))
lcmocc_utils.check_lcmocc_in_progress(context, id)
if (inst.instantiationState == 'NOT_INSTANTIATED'
and 'vimConnectionInfo' in body):
@ -200,11 +216,54 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
return sol_wsgi.SolResponse(202, None, location=location)
def _get_policy_target(self, vnf_instance):
vendor = vnf_instance.vnfProvider
if vnf_instance.instantiationState == 'NOT_INSTANTIATED':
area = '*'
tenant = '*'
else:
vim_type = None
area = None
if vnf_instance.obj_attr_is_set('vimConnectionInfo'):
for _, vim_conn_info in vnf_instance.vimConnectionInfo.items():
area = vim_conn_info.get('extra', {}).get('area')
vim_type = vim_conn_info.vimType
if area and vim_type:
break
tenant = None
if (vnf_instance.obj_attr_is_set('instantiatedVnfInfo') and
vnf_instance.instantiatedVnfInfo.obj_attr_is_set(
'metadata')):
tenant = (vnf_instance.instantiatedVnfInfo
.metadata.get('namespace'))