Merge "Convert role_inferences API to flask native dispatching"

This commit is contained in:
Zuul 2018-08-16 17:58:42 +00:00 committed by Gerrit Code Review
commit 210faf3344
8 changed files with 139 additions and 102 deletions

View File

@ -20,6 +20,7 @@ from keystone.api import os_revoke
from keystone.api import os_simple_cert
from keystone.api import regions
from keystone.api import registered_limits
from keystone.api import role_inferences
from keystone.api import roles
from keystone.api import services
from keystone.api import trusts
@ -35,6 +36,7 @@ __all__ = (
'os_simple_cert',
'regions',
'registered_limits',
'role_inferences',
'roles',
'services',
'trusts',
@ -51,6 +53,7 @@ __apis__ = (
os_simple_cert,
regions,
registered_limits,
role_inferences,
roles,
services,
trusts,

View File

@ -0,0 +1,9 @@
# flake8: noqa
# NOTE(morgan): The keystone.api._shared module is explicitly for shared code
# between the APIs that should not be duplicated. This occurs infrequently.
# For the most part adding a new file or code to anything in this module is
# incorrect. If you are unsure of what you are doing, do not add code here.
# WARNING: THIS FILE SHOULD CONTAIN NO CODE, it is explicitly ignored by
# flake8 completely.

View File

@ -0,0 +1,50 @@
# 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.
# NOTE(morgan): This file does not implement any API-specific code. It simply
# supplies shared functions between the "role_inferences" and "roles" (for
# implied roles) APIs. In general, all code for an API should be isolated to
# it's own keystone.api.XXX module and not in the _shared module.
from keystone.common import provider_api
from keystone.server import flask as ks_flask
PROVIDERS = provider_api.ProviderAPIs
def build_prior_role_response_data(prior_role_id, prior_role_name):
return {
'id': prior_role_id,
'links': {
'self': ks_flask.base_url(path='/roles/%s' % prior_role_id)
},
'name': prior_role_name}
def build_implied_role_response_data(implied_role):
return {
'id': implied_role['id'],
'links': {
'self': ks_flask.base_url(
path='/roles/%s' % implied_role['id'])
},
'name': implied_role['name']}
def role_inference_response(prior_role_id):
prior_role = PROVIDERS.role_api.get_role(prior_role_id)
response = {
'role_inference': {
'prior_role': build_prior_role_response_data(
prior_role_id, prior_role['name'])}}
return response

View File

@ -0,0 +1,70 @@
# 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.
# This file handles all flask-restful resources for /v3/role_inferences
import flask_restful
from keystone.api._shared import implied_roles as shared
from keystone.common import provider_api
from keystone.common import rbac_enforcer
from keystone.server import flask as ks_flask
ENFORCER = rbac_enforcer.RBACEnforcer
PROVIDERS = provider_api.ProviderAPIs
class RoleInferencesResource(flask_restful.Resource):
def get(self):
"""List role inference rules.
GET/HEAD /v3/role_inferences
"""
ENFORCER.enforce_call(action='identity:list_role_inference_rules')
refs = PROVIDERS.role_api.list_role_inference_rules()
role_dict = {role_ref['id']: role_ref
for role_ref in PROVIDERS.role_api.list_roles()}
rules = dict()
for ref in refs:
implied_role_id = ref['implied_role_id']
prior_role_id = ref['prior_role_id']
implied = rules.get(prior_role_id, [])
implied.append(
shared.build_implied_role_response_data(
role_dict[implied_role_id]))
rules[prior_role_id] = implied
inferences = []
for prior_id, implied, in rules.items():
prior_response = shared.build_prior_role_response_data(
prior_id, role_dict[prior_id]['name'])
inferences.append({'prior_role': prior_response,
'implies': implied})
results = {'role_inferences': inferences}
return results
class RoleInferencesAPI(ks_flask.APIBase):
_name = 'role_inferences'
_import_name = __name__
resources = []
resource_mapping = [
ks_flask.construct_resource_map(
resource=RoleInferencesResource,
url='/role_inferences',
resource_kwargs={},
rel='role_inferences')
]
APIs = (RoleInferencesAPI,)

View File

@ -16,6 +16,7 @@ import flask
import flask_restful
from six.moves import http_client
from keystone.api._shared import implied_roles as shared
from keystone.assignment import schema
from keystone.common import json_home
from keystone.common import provider_api
@ -191,34 +192,6 @@ def _build_enforcement_target_ref():
return ref
def _build_prior_role_response_data(prior_role_id, prior_role_name):
return {
'id': prior_role_id,
'links': {
'self': ks_flask.base_url(path='/roles/%s' % prior_role_id)
},
'name': prior_role_name}
def _build_implied_role_response_data(implied_role):
return {
'id': implied_role['id'],
'links': {
'self': ks_flask.base_url(
path='/roles/%s' % implied_role['id'])
},
'name': implied_role['name']}
def _role_inference_response(prior_role_id):
prior_role = PROVIDERS.role_api.get_role(prior_role_id)
response = {
'role_inference': {
'prior_role': _build_prior_role_response_data(
prior_role_id, prior_role['name'])}}
return response
class RoleImplicationListResource(flask_restful.Resource):
def get(self, prior_role_id):
"""List Implied Roles.
@ -229,12 +202,12 @@ class RoleImplicationListResource(flask_restful.Resource):
target_attr=_build_enforcement_target_ref())
ref = PROVIDERS.role_api.list_implied_roles(prior_role_id)
implied_ids = [r['implied_role_id'] for r in ref]
response_json = _role_inference_response(prior_role_id)
response_json = shared.role_inference_response(prior_role_id)
response_json['role_inference']['implies'] = []
for implied_id in implied_ids:
implied_role = PROVIDERS.role_api.get_role(implied_id)
response_json['role_inference']['implies'].append(
_build_implied_role_response_data(implied_role))
shared.build_implied_role_response_data(implied_role))
response_json['links'] = {
'self': ks_flask.base_url(
path='/roles/%s/implies' % prior_role_id)}
@ -274,9 +247,9 @@ class RoleImplicationResource(flask_restful.Resource):
PROVIDERS.role_api.get_implied_role(
prior_role_id, implied_role_id)
implied_role_ref = PROVIDERS.role_api.get_role(implied_role_id)
response_json = _role_inference_response(prior_role_id)
response_json = shared.role_inference_response(prior_role_id)
response_json['role_inference'][
'implies'] = _build_implied_role_response_data(
'implies'] = shared.build_implied_role_response_data(
implied_role_ref)
response_json['links'] = {
'self': ks_flask.base_url(

View File

@ -50,66 +50,6 @@ class ProjectAssignmentV3(controller.V3Controller):
hints=hints)
class ImpliedRolesV3(controller.V3Controller):
"""The V3 ImpliedRoles CRD APIs. There is no Update."""
def _check_implies_role(self, request, prep_info,
prior_role_id, implied_role_id=None):
ref = {}
ref['prior_role'] = PROVIDERS.role_api.get_role(prior_role_id)
if implied_role_id:
ref['implied_role'] = PROVIDERS.role_api.get_role(implied_role_id)
self.check_protection(request, prep_info, ref)
def _prior_role_stanza(self, endpoint, prior_role_id, prior_role_name):
return {
"id": prior_role_id,
"links": {
"self": endpoint + "/v3/roles/" + prior_role_id
},
"name": prior_role_name
}
def _implied_role_stanza(self, endpoint, implied_role):
implied_id = implied_role['id']
implied_response = {
"id": implied_id,
"links": {
"self": endpoint + "/v3/roles/" + implied_id
},
"name": implied_role['name']
}
return implied_response
@controller.protected()
def list_role_inference_rules(self, request):
refs = PROVIDERS.role_api.list_role_inference_rules()
role_dict = {role_ref['id']: role_ref
for role_ref in PROVIDERS.role_api.list_roles()}
rules = dict()
endpoint = super(controller.V3Controller, ImpliedRolesV3).base_url(
request.context_dict, 'public')
for ref in refs:
implied_role_id = ref['implied_role_id']
prior_role_id = ref['prior_role_id']
implied = rules.get(prior_role_id, [])
implied.append(self._implied_role_stanza(
endpoint, role_dict[implied_role_id]))
rules[prior_role_id] = implied
inferences = []
for prior_id, implied in rules.items():
prior_response = self._prior_role_stanza(
endpoint, prior_id, role_dict[prior_id]['name'])
inferences.append({'prior_role': prior_response,
'implies': implied})
results = {'role_inferences': inferences}
return results
class GrantAssignmentV3(controller.V3Controller):
"""The V3 Grant Assignment APIs."""

View File

@ -38,7 +38,7 @@ class Public(wsgi.ComposableRouter):
class Routers(wsgi.RoutersBase):
_path_prefixes = ('users', 'role_inferences', 'projects',
_path_prefixes = ('users', 'projects',
'domains', 'system', 'role_assignments', 'OS-INHERIT')
def append_v3_routers(self, mapper, routers):
@ -53,15 +53,6 @@ class Routers(wsgi.RoutersBase):
'user_id': json_home.Parameters.USER_ID,
})
implied_roles_controller = controllers.ImpliedRolesV3()
self._add_resource(
mapper, implied_roles_controller,
path='/role_inferences',
get_head_action='list_role_inference_rules',
rel=json_home.build_v3_resource_relation('role_inferences'),
path_vars={}
)
grant_controller = controllers.GrantAssignmentV3()
self._add_resource(
mapper, grant_controller,

View File

@ -50,6 +50,7 @@ _MOVED_API_PREFIXES = frozenset(
'limits',
'regions',
'registered_limits',
'role_inferences',
'roles',
'services',
]