Blackify the keystone code base

Improve code maintainability by enabling us to use automatic code
formatter. We use black as a relatively fast code formatter (ruff is an
lternative, but it will immediately require also changing all quotes
chaos). In the next change black is going to be added into the
pre-commit allowing contributors to spot formatting issues before
sending code to the CI sending precious run hours. Same steps has been
performed in the openstasksdk/cli and partially nova.

Steps performed:
- `black -l 79 -S keystone`
- add exceptions to the tox.ini for the lines which black decided not to
  split

Following files are polished manually due to noqa headers and similar
being reformatted to wrong strings:

keystone/auth/plugins/totp.py
keystone/common/password_hashing.py
keystone/tests/unit/ksfixtures/__init__.py

Change-Id: I832ec4c152fa58fb0088d9f880add86a20ec95fc
This commit is contained in:
Artem Goncharov 2024-07-19 16:58:05 +02:00
parent 24113bb182
commit a00839ca02
396 changed files with 44964 additions and 30269 deletions

View File

@ -46,10 +46,11 @@ class ResourceBase(ks_flask.ResourceBase):
def _check_timestamp(credentials):
timestamp = (
# AWS Signature v1/v2
credentials.get('params', {}).get('Timestamp') or
credentials.get('params', {}).get('Timestamp')
or
# AWS Signature v4
credentials.get('headers', {}).get('X-Amz-Date') or
credentials.get('params', {}).get('X-Amz-Date')
credentials.get('headers', {}).get('X-Amz-Date')
or credentials.get('params', {}).get('X-Amz-Date')
)
if not timestamp:
# If the signed payload doesn't include a timestamp then the signer
@ -60,12 +61,12 @@ class ResourceBase(ks_flask.ResourceBase):
timestamp = timeutils.normalize_time(timestamp)
except Exception as e:
raise ks_exceptions.Unauthorized(
_('Credential timestamp is invalid: %s') % e)
_('Credential timestamp is invalid: %s') % e
)
auth_ttl = datetime.timedelta(minutes=CONF.credential.auth_ttl)
current_time = timeutils.normalize_time(timeutils.utcnow())
if current_time > timestamp + auth_ttl:
raise ks_exceptions.Unauthorized(
_('Credential is expired'))
raise ks_exceptions.Unauthorized(_('Credential is expired'))
def handle_authenticate(self):
# TODO(morgan): convert this dirty check to JSON Schema validation
@ -86,9 +87,9 @@ class ResourceBase(ks_flask.ResourceBase):
# Try "credentials" then "credential" and THEN ec2Credentials. Final
# default is {}
credentials = (
self.request_body_json.get('credentials') or
self.request_body_json.get('credential') or
self.request_body_json.get('ec2Credentials')
self.request_body_json.get('credentials')
or self.request_body_json.get('credential')
or self.request_body_json.get('ec2Credentials')
)
if not credentials:
credentials = {}
@ -116,21 +117,24 @@ class ResourceBase(ks_flask.ResourceBase):
secret=loaded.get('secret'),
trust_id=loaded.get('trust_id'),
app_cred_id=loaded.get('app_cred_id'),
access_token_id=loaded.get('access_token_id')
access_token_id=loaded.get('access_token_id'),
)
# validate the signature
self._check_signature(cred_data, credentials)
project_ref = PROVIDERS.resource_api.get_project(
cred_data['project_id'])
cred_data['project_id']
)
user_ref = PROVIDERS.identity_api.get_user(cred_data['user_id'])
# validate that the auth info is valid and nothing is disabled
try:
PROVIDERS.identity_api.assert_user_enabled(
user_id=user_ref['id'], user=user_ref)
user_id=user_ref['id'], user=user_ref
)
PROVIDERS.resource_api.assert_project_enabled(
project_id=project_ref['id'], project=project_ref)
project_id=project_ref['id'], project=project_ref
)
except AssertionError as e:
raise ks_exceptions.Unauthorized from e
@ -153,16 +157,19 @@ class ResourceBase(ks_flask.ResourceBase):
elif cred_data['app_cred_id']:
ac_client = PROVIDERS.application_credential_api
app_cred = ac_client.get_application_credential(
cred_data['app_cred_id'])
cred_data['app_cred_id']
)
roles = [r['id'] for r in app_cred['roles']]
elif cred_data['access_token_id']:
access_token = PROVIDERS.oauth_api.get_access_token(
cred_data['access_token_id'])
cred_data['access_token_id']
)
roles = jsonutils.loads(access_token['role_ids'])
auth_context = {'access_token_id': cred_data['access_token_id']}
else:
roles = PROVIDERS.assignment_api.get_roles_for_user_and_project(
user_ref['id'], project_ref['id'])
user_ref['id'], project_ref['id']
)
if not roles:
raise ks_exceptions.Unauthorized(_('User not valid for project.'))
@ -178,9 +185,11 @@ class ResourceBase(ks_flask.ResourceBase):
else:
user_id = user_ref['id']
token = PROVIDERS.token_provider_api.issue_token(
user_id=user_id, method_names=method_names,
user_id=user_id,
method_names=method_names,
project_id=project_ref['id'],
trust_id=cred_data['trust_id'],
app_cred_id=cred_data['app_cred_id'],
auth_context=auth_context)
auth_context=auth_context,
)
return token

View File

@ -33,9 +33,7 @@ PROVIDERS = provider_api.ProviderAPIs
def _check_and_set_default_scoping(auth_info, auth_context):
(domain_id, project_id, trust, unscoped, system) = (
auth_info.get_scope()
)
(domain_id, project_id, trust, unscoped, system) = auth_info.get_scope()
if trust:
project_id = trust['project_id']
if system or domain_id or project_id or trust:
@ -65,37 +63,53 @@ def _check_and_set_default_scoping(auth_info, auth_context):
# make sure user's default project is legit before scoping to it
try:
default_project_ref = PROVIDERS.resource_api.get_project(
default_project_id)
default_project_id
)
default_project_domain_ref = PROVIDERS.resource_api.get_domain(
default_project_ref['domain_id'])
if (default_project_ref.get('enabled', True) and
default_project_domain_ref.get('enabled', True)):
default_project_ref['domain_id']
)
if default_project_ref.get(
'enabled', True
) and default_project_domain_ref.get('enabled', True):
if PROVIDERS.assignment_api.get_roles_for_user_and_project(
user_ref['id'], default_project_id):
user_ref['id'], default_project_id
):
auth_info.set_scope(project_id=default_project_id)
else:
msg = ("User %(user_id)s doesn't have access to"
" default project %(project_id)s. The token"
" will be unscoped rather than scoped to the"
" project.")
LOG.debug(msg,
{'user_id': user_ref['id'],
'project_id': default_project_id})
msg = (
"User %(user_id)s doesn't have access to"
" default project %(project_id)s. The token"
" will be unscoped rather than scoped to the"
" project."
)
LOG.debug(
msg,
{
'user_id': user_ref['id'],
'project_id': default_project_id,
},
)
else:
msg = ("User %(user_id)s's default project %(project_id)s"
" is disabled. The token will be unscoped rather"
" than scoped to the project.")
LOG.debug(msg,
{'user_id': user_ref['id'],
'project_id': default_project_id})
msg = (
"User %(user_id)s's default project %(project_id)s"
" is disabled. The token will be unscoped rather"
" than scoped to the project."
)
LOG.debug(
msg,
{'user_id': user_ref['id'], 'project_id': default_project_id},
)
except (exception.ProjectNotFound, exception.DomainNotFound):
# default project or default project domain doesn't exist,
# will issue unscoped token instead
msg = ("User %(user_id)s's default project %(project_id)s not"
" found. The token will be unscoped rather than"
" scoped to the project.")
LOG.debug(msg, {'user_id': user_ref['id'],
'project_id': default_project_id})
msg = (
"User %(user_id)s's default project %(project_id)s not"
" found. The token will be unscoped rather than"
" scoped to the project."
)
LOG.debug(
msg, {'user_id': user_ref['id'], 'project_id': default_project_id}
)
def authenticate(auth_info, auth_context):
@ -112,9 +126,12 @@ def authenticate(auth_info, auth_context):
'`authenticate` method is not of type '
'`keystone.auth.core.AuthContext`. For security '
'purposes this is required. This is likely a programming '
'error. Received object of type `%s`', type(auth_context))
'error. Received object of type `%s`',
type(auth_context),
)
raise exception.Unauthorized(
_('Cannot Authenticate due to internal error.'))
_('Cannot Authenticate due to internal error.')
)
# The 'external' method allows any 'REMOTE_USER' based authentication
# In some cases the server can set REMOTE_USER as '' instead of
# dropping it, so this must be filtered out
@ -125,8 +142,9 @@ def authenticate(auth_info, auth_context):
if resp and resp.status:
# NOTE(notmorgan): ``external`` plugin cannot be multi-step
# it is either a plain success/fail.
auth_context.setdefault(
'method_names', []).insert(0, 'external')
auth_context.setdefault('method_names', []).insert(
0, 'external'
)
# NOTE(notmorgan): All updates to auth_context is handled
# here in the .authenticate method.
auth_context.update(resp.response_data or {})
@ -152,13 +170,13 @@ def authenticate(auth_info, auth_context):
resp = method.authenticate(auth_info.get_method_data(method_name))
if resp:
if resp.status:
auth_context.setdefault(
'method_names', []).insert(0, method_name)
auth_context.setdefault('method_names', []).insert(
0, method_name
)
# NOTE(notmorgan): All updates to auth_context is handled
# here in the .authenticate method. If the auth attempt was
# not successful do not update the auth_context
resp_method_names = resp.response_data.pop(
'method_names', [])
resp_method_names = resp.response_data.pop('method_names', [])
auth_context['method_names'].extend(resp_method_names)
auth_context.update(resp.response_data or {})
elif resp.response_body:
@ -180,8 +198,7 @@ def authenticate_for_token(auth=None):
"""Authenticate user and issue a token."""
try:
auth_info = core.AuthInfo.create(auth=auth)
auth_context = core.AuthContext(method_names=[],
bind={})
auth_context = core.AuthContext(method_names=[], bind={})
authenticate(auth_info, auth_context)
if auth_context.get('access_token_id'):
auth_info.set_scope(None, auth_context['project_id'], None)
@ -201,7 +218,8 @@ def authenticate_for_token(auth=None):
# the given receipt.
if receipt:
method_names_set = set(
auth_context.get('method_names', []) + receipt.methods)
auth_context.get('method_names', []) + receipt.methods
)
else:
method_names_set = set(auth_context.get('method_names', []))
method_names = list(method_names_set)
@ -213,19 +231,27 @@ def authenticate_for_token(auth=None):
# Do MFA Rule Validation for the user
if not core.UserMFARulesValidator.check_auth_methods_against_rules(
auth_context['user_id'], method_names_set):
auth_context['user_id'], method_names_set
):
raise exception.InsufficientAuthMethods(
user_id=auth_context['user_id'],
methods=method_names)
user_id=auth_context['user_id'], methods=method_names
)
expires_at = auth_context.get('expires_at')
token_audit_id = auth_context.get('audit_id')
token = PROVIDERS.token_provider_api.issue_token(
auth_context['user_id'], method_names, expires_at=expires_at,
system=system, project_id=project_id, domain_id=domain_id,
auth_context=auth_context, trust_id=trust_id,
app_cred_id=app_cred_id, parent_audit_id=token_audit_id)
auth_context['user_id'],
method_names,
expires_at=expires_at,
system=system,
project_id=project_id,
domain_id=domain_id,
auth_context=auth_context,
trust_id=trust_id,
app_cred_id=app_cred_id,
parent_audit_id=token_audit_id,
)
# NOTE(wanghong): We consume a trust use only when we are using
# trusts and have successfully issued a token.
@ -244,8 +270,8 @@ def federated_authenticate_for_token(identity_provider, protocol_id):
'methods': [protocol_id],
protocol_id: {
'identity_provider': identity_provider,
'protocol': protocol_id
}
'protocol': protocol_id,
},
}
}
return authenticate_for_token(auth)

View File

@ -25,20 +25,19 @@ 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}
'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'])
'self': ks_flask.base_url(path='/roles/%s' % implied_role['id'])
},
'name': implied_role['name']}
'name': implied_role['name'],
}
def role_inference_response(prior_role_id):
@ -46,5 +45,8 @@ def role_inference_response(prior_role_id):
response = {
'role_inference': {
'prior_role': build_prior_role_response_data(
prior_role_id, prior_role['name'])}}
prior_role_id, prior_role['name']
)
}
}
return response

View File

@ -22,74 +22,108 @@ from keystone.common import json_home
# OS-EC2 "extension"
os_ec2_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-EC2', extension_version='1.0')
extension_name='OS-EC2',
extension_version='1.0',
)
# s3token "extension"
s3_token_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='s3tokens', extension_version='1.0')
extension_name='s3tokens',
extension_version='1.0',
)
# OS-EP-FILTER "extension"
os_ep_filter_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-EP-FILTER', extension_version='1.0')
extension_name='OS-EP-FILTER',
extension_version='1.0',
)
os_ep_filter_parameter_rel_func = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-EP-FILTER', extension_version='1.0')
extension_name='OS-EP-FILTER',
extension_version='1.0',
)
# OS-OAUTH1 "extension"
os_oauth1_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-OAUTH1', extension_version='1.0')
extension_name='OS-OAUTH1',
extension_version='1.0',
)
os_oauth1_parameter_rel_func = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-OAUTH1', extension_version='1.0')
extension_name='OS-OAUTH1',
extension_version='1.0',
)
# OS-OAUTH2 "extension"
os_oauth2_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-OAUTH2', extension_version='1.0')
extension_name='OS-OAUTH2',
extension_version='1.0',
)
os_oauth2_parameter_rel_func = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-OAUTH2', extension_version='1.0')
extension_name='OS-OAUTH2',
extension_version='1.0',
)
# OS-REVOKE "extension"
os_revoke_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-REVOKE', extension_version='1.0')
extension_name='OS-REVOKE',
extension_version='1.0',
)
# OS-SIMPLE-CERT "extension"
os_simple_cert_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-SIMPLE-CERT', extension_version='1.0')
extension_name='OS-SIMPLE-CERT',
extension_version='1.0',
)
# OS-TRUST "extension"
os_trust_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation, extension_name='OS-TRUST',
extension_version='1.0')
json_home.build_v3_extension_resource_relation,
extension_name='OS-TRUST',
extension_version='1.0',
)
os_trust_parameter_rel_func = functools.partial(
json_home.build_v3_extension_parameter_relation, extension_name='OS-TRUST',
extension_version='1.0')
json_home.build_v3_extension_parameter_relation,
extension_name='OS-TRUST',
extension_version='1.0',
)
# OS-ENDPOINT-POLICY "extension"
os_endpoint_policy_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-ENDPOINT-POLICY', extension_version='1.0')
extension_name='OS-ENDPOINT-POLICY',
extension_version='1.0',
)
# OS-FEDERATION "extension"
os_federation_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-FEDERATION', extension_version='1.0')
extension_name='OS-FEDERATION',
extension_version='1.0',
)
os_federation_parameter_rel_func = functools.partial(
json_home.build_v3_extension_parameter_relation,
extension_name='OS-FEDERATION', extension_version='1.0')
extension_name='OS-FEDERATION',
extension_version='1.0',
)
# OS-INHERIT "extension"
os_inherit_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-INHERIT', extension_version='1.0')
extension_name='OS-INHERIT',
extension_version='1.0',
)
# OS-PKI (revoked) "extension"
os_pki_resource_rel_func = functools.partial(
json_home.build_v3_extension_resource_relation,
extension_name='OS-PKI', extension_version='1.0')
extension_name='OS-PKI',
extension_version='1.0',
)

View File

@ -35,8 +35,10 @@ def create_base_saml_assertion(auth):
token = PROVIDERS.token_provider_api.validate_token(token_id)
if not token.project_scoped:
action = _('Use a project scoped token when attempting to create '
'a SAML assertion')
action = _(
'Use a project scoped token when attempting to create '
'a SAML assertion'
)
raise exception.ForbiddenAction(action=action)
subject = token.user['name']
@ -58,19 +60,27 @@ def create_base_saml_assertion(auth):
'JSON:{"name":"group2","domain":{"name":"Default"}}']
"""
user_groups = []
groups = PROVIDERS.identity_api.list_groups_for_user(
token.user_id)
groups = PROVIDERS.identity_api.list_groups_for_user(token.user_id)
for group in groups:
user_group = {}
group_domain_name = PROVIDERS.resource_api.get_domain(
group['domain_id'])['name']
group['domain_id']
)['name']
user_group["name"] = group['name']
user_group["domain"] = {'name': group_domain_name}
user_groups.append('JSON:' + jsonutils.dumps(user_group))
return user_groups
groups = group_membership()
generator = keystone_idp.SAMLGenerator()
response = generator.samlize_token(
issuer, sp_url, subject, subject_domain_name,
role_names, project, project_domain_name, groups)
issuer,
sp_url,
subject,
subject_domain_name,
role_names,
project,
project_domain_name,
groups,
)
return response, service_provider

View File

@ -58,9 +58,11 @@ def _combine_lists_uniquely(a, b):
def _build_response_headers(service_provider):
# URLs in header are encoded into bytes
return [('Content-Type', 'text/xml'),
('X-sp-url', service_provider['sp_url'].encode('utf-8')),
('X-auth-url', service_provider['auth_url'].encode('utf-8'))]
return [
('Content-Type', 'text/xml'),
('X-sp-url', service_provider['sp_url'].encode('utf-8')),
('X-auth-url', service_provider['auth_url'].encode('utf-8')),
]
def _get_sso_origin_host():
@ -87,13 +89,14 @@ def _get_sso_origin_host():
host = urllib.parse.unquote_plus(origin)
# change trusted_dashboard hostnames to lowercase before comparison
trusted_dashboards = [k_utils.lower_case_hostname(trusted)
for trusted in CONF.federation.trusted_dashboard]
trusted_dashboards = [
k_utils.lower_case_hostname(trusted)
for trusted in CONF.federation.trusted_dashboard
]
if host not in trusted_dashboards:
msg = '%(host)s is not a trusted dashboard host' % {'host': host}
tr_msg = _('%(host)s is not a trusted dashboard host') % {
'host': host}
tr_msg = _('%(host)s is not a trusted dashboard host') % {'host': host}
LOG.error(msg)
raise exception.Unauthorized(tr_msg)
@ -133,14 +136,16 @@ class AuthProjectsResource(ks_flask.ResourceBase):
if user_id:
try:
user_p_refs = PROVIDERS.assignment_api.list_projects_for_user(
user_id)
user_id
)
except exception.UserNotFound: # nosec
# federated users have an id but they don't link to anything
pass
if group_ids:
grp_p_refs = PROVIDERS.assignment_api.list_projects_for_groups(
group_ids)
group_ids
)
refs = _combine_lists_uniquely(user_p_refs, grp_p_refs)
return self.wrap_collection(refs)
@ -165,14 +170,16 @@ class AuthDomainsResource(ks_flask.ResourceBase):
if user_id:
try:
user_d_refs = PROVIDERS.assignment_api.list_domains_for_user(
user_id)
user_id
)
except exception.UserNotFound: # nosec
# federated users have an id but they don't link to anything
pass
if group_ids:
grp_d_refs = PROVIDERS.assignment_api.list_domains_for_groups(
group_ids)
group_ids
)
refs = _combine_lists_uniquely(user_d_refs, grp_d_refs)
return self.wrap_collection(refs)
@ -195,7 +202,8 @@ class AuthSystemResource(_AuthFederationWebSSOBase):
try:
user_assignments = (
PROVIDERS.assignment_api.list_system_grants_for_user(
user_id)
user_id
)
)
except exception.UserNotFound: # nosec
# federated users have an id but they don't link to anything
@ -204,25 +212,23 @@ class AuthSystemResource(_AuthFederationWebSSOBase):
if group_ids:
group_assignments = (
PROVIDERS.assignment_api.list_system_grants_for_groups(
group_ids)
group_ids
)
)
assignments = _combine_lists_uniquely(
user_assignments, group_assignments)
user_assignments, group_assignments
)
if assignments:
response = {
'system': [{'all': True}],
'links': {
'self': ks_flask.base_url(path='auth/system')
}
'links': {'self': ks_flask.base_url(path='auth/system')},
}
else:
response = {
'system': [],
'links': {
'self': ks_flask.base_url(path='auth/system')
}
'links': {'self': ks_flask.base_url(path='auth/system')},
}
return response
@ -239,16 +245,17 @@ class AuthCatalogResource(_AuthFederationWebSSOBase):
if not project_id:
raise exception.Forbidden(
_('A project-scoped token is required to produce a '
'service catalog.'))
_(
'A project-scoped token is required to produce a '
'service catalog.'
)
)
return {
'catalog': PROVIDERS.catalog_api.get_v3_catalog(
user_id, project_id
),
'links': {
'self': ks_flask.base_url(path='auth/catalog')
}
'links': {'self': ks_flask.base_url(path='auth/catalog')},
}
@ -285,18 +292,24 @@ class AuthTokenResource(_AuthFederationWebSSOBase):
ENFORCER.enforce_call(action='identity:validate_token')
token_id = flask.request.headers.get(
authorization.SUBJECT_TOKEN_HEADER)
authorization.SUBJECT_TOKEN_HEADER
)
access_rules_support = flask.request.headers.get(
authorization.ACCESS_RULES_HEADER)
authorization.ACCESS_RULES_HEADER
)
allow_expired = strutils.bool_from_string(
flask.request.args.get('allow_expired'))
flask.request.args.get('allow_expired')
)
window_secs = CONF.token.allow_expired_window if allow_expired else 0
include_catalog = 'nocatalog' not in flask.request.args
token = PROVIDERS.token_provider_api.validate_token(
token_id, window_seconds=window_secs,
access_rules_support=access_rules_support)
token_id,
window_seconds=window_secs,
access_rules_support=access_rules_support,
)
token_resp = render_token.render_token_response_from_model(
token, include_catalog=include_catalog)
token, include_catalog=include_catalog
)
resp_body = jsonutils.dumps(token_resp)
response = flask.make_response(resp_body, http.client.OK)
response.headers['X-Subject-Token'] = token_id
@ -329,7 +342,8 @@ class AuthTokenResource(_AuthFederationWebSSOBase):
"""
ENFORCER.enforce_call(action='identity:revoke_token')
token_id = flask.request.headers.get(
authorization.SUBJECT_TOKEN_HEADER)
authorization.SUBJECT_TOKEN_HEADER
)
PROVIDERS.token_provider_api.revoke_token(token_id)
return None, http.client.NO_CONTENT
@ -342,7 +356,8 @@ class AuthFederationWebSSOResource(_AuthFederationWebSSOBase):
for idp in idps:
try:
remote_id_name = federation_utils.get_remote_id_parameter(
idp, protocol_id)
idp, protocol_id
)
except exception.FederatedProtocolNotFound:
# no protocol for this IdP, so this can't be the IdP we're
# looking for
@ -360,7 +375,8 @@ class AuthFederationWebSSOResource(_AuthFederationWebSSOBase):
ref = PROVIDERS.federation_api.get_idp_from_remote_id(remote_id)
identity_provider = ref['idp_id']
token = authentication.federated_authenticate_for_token(
identity_provider=identity_provider, protocol_id=protocol_id)
identity_provider=identity_provider, protocol_id=protocol_id
)
return cls._render_template_response(host, token.id)
@ks_flask.unenforced_api
@ -378,7 +394,8 @@ class AuthFederationWebSSOIDPsResource(_AuthFederationWebSSOBase):
host = _get_sso_origin_host()
token = authentication.federated_authenticate_for_token(
identity_provider=idp_id, protocol_id=protocol_id)
identity_provider=idp_id, protocol_id=protocol_id
)
return cls._render_template_response(host, token.id)
@ks_flask.unenforced_api
@ -423,15 +440,18 @@ class AuthFederationSaml2ECPResource(_AuthFederationWebSSOBase):
auth = self.request_body_json.get('auth')
validation.lazy_validate(federation_schema.saml_create, auth)
saml_assertion, service_provider = saml.create_base_saml_assertion(
auth)
auth
)
relay_state_prefix = service_provider['relay_state_prefix']
generator = keystone_idp.ECPGenerator()
ecp_assertion = generator.generate_ecp(
saml_assertion, relay_state_prefix)
saml_assertion, relay_state_prefix
)
headers = _build_response_headers(service_provider)
response = flask.make_response(
ecp_assertion.to_string(), http.client.OK)
ecp_assertion.to_string(), http.client.OK
)
for header, value in headers:
response.headers[header] = value
return response
@ -445,29 +465,34 @@ class AuthAPI(ks_flask.APIBase):
ks_flask.construct_resource_map(
resource=AuthProjectsResource,
url='/auth/projects',
alternate_urls=[dict(
url='/OS-FEDERATION/projects',
json_home=ks_flask.construct_json_home_data(
rel='projects',
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func)
alternate_urls=[
dict(
url='/OS-FEDERATION/projects',
json_home=ks_flask.construct_json_home_data(
rel='projects',
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func
),
),
)
)],
],
rel='auth_projects',
resource_kwargs={}
resource_kwargs={},
),
ks_flask.construct_resource_map(
resource=AuthDomainsResource,
url='/auth/domains',
alternate_urls=[dict(
url='/OS-FEDERATION/domains',
json_home=ks_flask.construct_json_home_data(
rel='domains',
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func)
alternate_urls=[
dict(
url='/OS-FEDERATION/domains',
json_home=ks_flask.construct_json_home_data(
rel='domains',
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func
),
),
)
)],
],
rel='auth_domains',
resource_kwargs={},
),
@ -475,27 +500,27 @@ class AuthAPI(ks_flask.APIBase):
resource=AuthSystemResource,
url='/auth/system',
resource_kwargs={},
rel='auth_system'
rel='auth_system',
),
ks_flask.construct_resource_map(
resource=AuthCatalogResource,
url='/auth/catalog',
resource_kwargs={},
rel='auth_catalog'
rel='auth_catalog',
),
ks_flask.construct_resource_map(
resource=AuthTokenOSPKIResource,
url='/auth/tokens/OS-PKI/revoked',
resource_kwargs={},
rel='revocations',
resource_relation_func=json_home_relations.os_pki_resource_rel_func
resource_relation_func=json_home_relations.os_pki_resource_rel_func,
),
ks_flask.construct_resource_map(
resource=AuthTokenResource,
url='/auth/tokens',
resource_kwargs={},
rel='auth_tokens'
)
rel='auth_tokens',
),
]
@ -509,16 +534,18 @@ class AuthFederationAPI(ks_flask.APIBase):
url='/auth/OS-FEDERATION/saml2',
resource_kwargs={},
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func),
rel='saml2'
json_home_relations.os_federation_resource_rel_func
),
rel='saml2',
),
ks_flask.construct_resource_map(
resource=AuthFederationSaml2ECPResource,
url='/auth/OS-FEDERATION/saml2/ecp',
resource_kwargs={},
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func),
rel='ecp'
json_home_relations.os_federation_resource_rel_func
),
rel='ecp',
),
ks_flask.construct_resource_map(
resource=AuthFederationWebSSOResource,
@ -526,28 +553,40 @@ class AuthFederationAPI(ks_flask.APIBase):
resource_kwargs={},
rel='websso',
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func),
json_home_relations.os_federation_resource_rel_func
),
path_vars={
'protocol_id': (
json_home_relations.os_federation_parameter_rel_func(
parameter_name='protocol_id'))}
parameter_name='protocol_id'
)
)
},
),
ks_flask.construct_resource_map(
resource=AuthFederationWebSSOIDPsResource,
url=('/auth/OS-FEDERATION/identity_providers/<string:idp_id>/'
'protocols/<string:protocol_id>/websso'),
url=(
'/auth/OS-FEDERATION/identity_providers/<string:idp_id>/'
'protocols/<string:protocol_id>/websso'
),
resource_kwargs={},
rel='identity_providers_websso',
resource_relation_func=(
json_home_relations.os_federation_resource_rel_func),
json_home_relations.os_federation_resource_rel_func
),
path_vars={
'idp_id': (
json_home_relations.os_federation_parameter_rel_func(
parameter_name='idp_id')),
parameter_name='idp_id'
)
),
'protocol_id': (
json_home_relations.os_federation_parameter_rel_func(
parameter_name='protocol_id'))}
)
parameter_name='protocol_id'
)
),
},
),
]

View File

@ -65,23 +65,28 @@ class CredentialResource(ks_flask.ResourceBase):
blob = jsonutils.loads(ref.get('blob'))
except (ValueError, TabError):
raise exception.ValidationError(
message=_('Invalid blob in credential'))
message=_('Invalid blob in credential')
)
if not blob or not isinstance(blob, dict):
raise exception.ValidationError(attribute='blob',
target='credential')
raise exception.ValidationError(
attribute='blob', target='credential'
)
if blob.get('access') is None:
raise exception.ValidationError(attribute='access',
target='credential')
raise exception.ValidationError(
attribute='access', target='credential'
)
return blob
def _assign_unique_id(
self, ref, trust_id=None, app_cred_id=None, access_token_id=None):
self, ref, trust_id=None, app_cred_id=None, access_token_id=None
):
# Generates an assigns a unique identifier to a credential reference.
if ref.get('type', '').lower() == 'ec2':
blob = self._validate_blob_json(ref)
ref = ref.copy()
ref['id'] = hashlib.sha256(
blob['access'].encode('utf8')).hexdigest()
blob['access'].encode('utf8')
).hexdigest()
# update the blob with the trust_id or app_cred_id, so credentials
# created with a trust- or app cred-scoped token will result in
# trust- or app cred-scoped tokens when authentication via
@ -105,8 +110,11 @@ class CredentialResource(ks_flask.ResourceBase):
target = {'credential': {'user_id': self.oslo_context.user_id}}
else:
target = None
ENFORCER.enforce_call(action='identity:list_credentials',
filters=filters, target_attr=target)
ENFORCER.enforce_call(
action='identity:list_credentials',
filters=filters,
target_attr=target,
)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.credential_api.list_credentials(hints)
# If the request was filtered, make sure to return only the
@ -122,7 +130,7 @@ class CredentialResource(ks_flask.ResourceBase):
cred = PROVIDERS.credential_api.get_credential(ref['id'])
ENFORCER.enforce_call(
action='identity:get_credential',
target_attr={'credential': cred}
target_attr={'credential': cred},
)
filtered_refs.append(ref)
except exception.Forbidden:
@ -134,7 +142,7 @@ class CredentialResource(ks_flask.ResourceBase):
def _get_credential(self, credential_id):
ENFORCER.enforce_call(
action='identity:get_credential',
build_target=_build_target_enforcement
build_target=_build_target_enforcement,
)
credential = PROVIDERS.credential_api.get_credential(credential_id)
return self.wrap_member(self._blob_to_json(credential))
@ -158,15 +166,20 @@ class CredentialResource(ks_flask.ResourceBase):
validation.lazy_validate(schema.credential_create, credential)
trust_id = getattr(self.oslo_context, 'trust_id', None)
app_cred_id = getattr(
self.auth_context['token'], 'application_credential_id', None)
self.auth_context['token'], 'application_credential_id', None
)
access_token_id = getattr(
self.auth_context['token'], 'access_token_id', None)
self.auth_context['token'], 'access_token_id', None
)
ref = self._assign_unique_id(
self._normalize_dict(credential),
trust_id=trust_id, app_cred_id=app_cred_id,
access_token_id=access_token_id)
trust_id=trust_id,
app_cred_id=app_cred_id,
access_token_id=access_token_id,
)
ref = PROVIDERS.credential_api.create_credential(
ref['id'], ref, initiator=self.audit_initiator)
ref['id'], ref, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def _validate_blob_update_keys(self, credential, ref):
@ -176,8 +189,12 @@ class CredentialResource(ks_flask.ResourceBase):
if isinstance(old_blob, str):
old_blob = jsonutils.loads(old_blob)
# if there was a scope set, prevent changing it or unsetting it
for key in ['trust_id', 'app_cred_id', 'access_token_id',
'access_id']:
for key in [
'trust_id',
'app_cred_id',
'access_token_id',
'access_id',
]:
if old_blob.get(key) != new_blob.get(key):
message = _('%s can not be updated for credential') % key
raise exception.ValidationError(message=message)
@ -186,7 +203,7 @@ class CredentialResource(ks_flask.ResourceBase):
# Update Credential
ENFORCER.enforce_call(
action='identity:update_credential',
build_target=_build_target_enforcement
build_target=_build_target_enforcement,
)
current = PROVIDERS.credential_api.get_credential(credential_id)
@ -200,19 +217,23 @@ class CredentialResource(ks_flask.ResourceBase):
action='identity:update_credential', target_attr=target
)
ref = PROVIDERS.credential_api.update_credential(
credential_id, credential)
credential_id, credential
)
return self.wrap_member(ref)
def delete(self, credential_id):
# Delete credentials
ENFORCER.enforce_call(
action='identity:delete_credential',
build_target=_build_target_enforcement
build_target=_build_target_enforcement,
)
return (PROVIDERS.credential_api.delete_credential(
credential_id, initiator=self.audit_initiator),
http.client.NO_CONTENT)
return (
PROVIDERS.credential_api.delete_credential(
credential_id, initiator=self.audit_initiator
),
http.client.NO_CONTENT,
)
class CredentialAPI(ks_flask.APIBase):

View File

@ -31,14 +31,15 @@ def _get_versions_list(identity_url):
'id': 'v3.14',
'status': 'stable',
'updated': '2020-04-07T00:00:00Z',
'links': [{
'rel': 'self',
'href': identity_url,
}],
'media-types': [{
'base': 'application/json',
'type': MEDIA_TYPE_JSON % 'v3'
}]
'links': [
{
'rel': 'self',
'href': identity_url,
}
],
'media-types': [
{'base': 'application/json', 'type': MEDIA_TYPE_JSON % 'v3'}
],
}
return versions
@ -53,7 +54,8 @@ def v3_mime_type_best_match():
return MimeTypes.JSON
return request.accept_mimetypes.best_match(
[MimeTypes.JSON, MimeTypes.JSON_HOME])
[MimeTypes.JSON, MimeTypes.JSON_HOME]
)
@_DISCOVERY_BLUEPRINT.route('/')
@ -63,8 +65,10 @@ def get_versions():
# understand the JSON-Home document.
v3_json_home = json_home.JsonHomeResources.resources()
json_home.translate_urls(v3_json_home, '/v3')
return flask.Response(response=jsonutils.dumps(v3_json_home),
mimetype=MimeTypes.JSON_HOME)
return flask.Response(
response=jsonutils.dumps(v3_json_home),
mimetype=MimeTypes.JSON_HOME,
)
else:
identity_url = '%s/' % ks_flask.base_url()
versions = _get_versions_list(identity_url)
@ -76,10 +80,11 @@ def get_versions():
response = flask.Response(
response=jsonutils.dumps(
{'versions': {
'values': list(versions.values())}}),
{'versions': {'values': list(versions.values())}}
),
mimetype=MimeTypes.JSON,
status=http.client.MULTIPLE_CHOICES)
status=http.client.MULTIPLE_CHOICES,
)
response.headers['Location'] = preferred_location
return response
@ -90,14 +95,16 @@ def get_version_v3():
# RENDER JSON-Home form, we have a clever client who will
# understand the JSON-Home document.
content = json_home.JsonHomeResources.resources()
return flask.Response(response=jsonutils.dumps(content),
mimetype=MimeTypes.JSON_HOME)
return flask.Response(
response=jsonutils.dumps(content), mimetype=MimeTypes.JSON_HOME
)
else:
identity_url = '%s/' % ks_flask.base_url()
versions = _get_versions_list(identity_url)
return flask.Response(
response=jsonutils.dumps({'version': versions['v3']}),
mimetype=MimeTypes.JSON)
mimetype=MimeTypes.JSON,
)
class DiscoveryAPI(object):

View File

@ -59,14 +59,16 @@ def _build_enforcement_target(allow_non_existing=False):
if flask.request.view_args.get('user_id'):
try:
target['user'] = PROVIDERS.identity_api.get_user(
flask.request.view_args['user_id'])
flask.request.view_args['user_id']
)
except exception.UserNotFound:
if not allow_non_existing:
raise
else:
try:
target['group'] = PROVIDERS.identity_api.get_group(
flask.request.view_args.get('group_id'))
flask.request.view_args.get('group_id')
)
except exception.GroupNotFound:
if not allow_non_existing:
raise
@ -77,7 +79,8 @@ class DomainResource(ks_flask.ResourceBase):
collection_key = 'domains'
member_key = 'domain'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='resource_api', method='get_domain')
api='resource_api', method='get_domain'
)
def get(self, domain_id=None):
"""Get domain or list domains.
@ -92,7 +95,7 @@ class DomainResource(ks_flask.ResourceBase):
def _get_domain(self, domain_id):
ENFORCER.enforce_call(
action='identity:get_domain',
build_target=_build_domain_enforcement_target
build_target=_build_domain_enforcement_target,
)
domain = PROVIDERS.resource_api.get_domain(domain_id)
return self.wrap_member(domain)
@ -102,16 +105,14 @@ class DomainResource(ks_flask.ResourceBase):
target = None
if self.oslo_context.domain_id:
target = {'domain': {'id': self.oslo_context.domain_id}}
ENFORCER.enforce_call(action='identity:list_domains',
filters=filters,
target_attr=target)
ENFORCER.enforce_call(
action='identity:list_domains', filters=filters, target_attr=target
)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.resource_api.list_domains(hints=hints)
if self.oslo_context.domain_id:
domain_id = self.oslo_context.domain_id
filtered_refs = [
ref for ref in refs if ref['id'] == domain_id
]
filtered_refs = [ref for ref in refs if ref['id'] == domain_id]
else:
filtered_refs = refs
return self.wrap_collection(filtered_refs, hints=hints)
@ -137,7 +138,8 @@ class DomainResource(ks_flask.ResourceBase):
domain['id'] = domain_id
domain = self._normalize_dict(domain)
ref = PROVIDERS.resource_api.create_domain(
domain['id'], domain, initiator=self.audit_initiator)
domain['id'], domain, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def patch(self, domain_id):
@ -150,7 +152,8 @@ class DomainResource(ks_flask.ResourceBase):
validation.lazy_validate(schema.domain_update, domain)
PROVIDERS.resource_api.get_domain(domain_id)
ref = PROVIDERS.resource_api.update_domain(
domain_id, domain, initiator=self.audit_initiator)
domain_id, domain, initiator=self.audit_initiator
)
return self.wrap_member(ref)
def delete(self, domain_id):
@ -160,7 +163,8 @@ class DomainResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(action='identity:delete_domain')
PROVIDERS.resource_api.delete_domain(
domain_id, initiator=self.audit_initiator)
domain_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -178,14 +182,15 @@ class DomainConfigBase(ks_flask.ResourceBase):
config = {}
try:
PROVIDERS.resource_api.get_domain(domain_id)
except Exception as e: # nosec
except Exception as e: # nosec
# We don't raise out here, we raise out after enforcement, this
# ensures we do not leak domain existance.
err = e
finally:
if group and group == 'security_compliance':
config = self._get_security_compliance_config(
domain_id, group, option)
domain_id, group, option
)
else:
config = self._get_config(domain_id, group, option)
if err is not None:
@ -195,13 +200,16 @@ class DomainConfigBase(ks_flask.ResourceBase):
def _get_config(self, domain_id, group, option):
ENFORCER.enforce_call(action='identity:get_domain_config')
return PROVIDERS.domain_config_api.get_config(
domain_id, group=group, option=option)
domain_id, group=group, option=option
)
def _get_security_compliance_config(self, domain_id, group, option):
ENFORCER.enforce_call(
action='identity:get_security_compliance_domain_config')
action='identity:get_security_compliance_domain_config'
)
return PROVIDERS.domain_config_api.get_security_compliance_config(
domain_id, group, option=option)
domain_id, group, option=option
)
def patch(self, domain_id=None, group=None, option=None):
"""Update domain config option.
@ -214,7 +222,8 @@ class DomainConfigBase(ks_flask.ResourceBase):
PROVIDERS.resource_api.get_domain(domain_id)
config = self.request_body_json.get('config', {})
ref = PROVIDERS.domain_config_api.update_config(
domain_id, config, group, option=option)
domain_id, config, group, option=option
)
return {self.member_key: ref}
def delete(self, domain_id=None, group=None, option=None):
@ -227,7 +236,8 @@ class DomainConfigBase(ks_flask.ResourceBase):
ENFORCER.enforce_call(action='identity:delete_domain_config')
PROVIDERS.resource_api.get_domain(domain_id)
PROVIDERS.domain_config_api.delete_config(
domain_id, group, option=option)
domain_id, group, option=option
)
return None, http.client.NO_CONTENT
@ -313,7 +323,8 @@ class DefaultConfigOptionResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(action='identity:get_domain_config_default')
ref = PROVIDERS.domain_config_api.get_config_default(
group=group, option=option)
group=group, option=option
)
return {'config': ref}
@ -325,12 +336,14 @@ class DomainUserListResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:list_grants',
build_target=_build_enforcement_target)
build_target=_build_enforcement_target,
)
refs = PROVIDERS.assignment_api.list_grants(
domain_id=domain_id, user_id=user_id,
inherited_to_projects=False)
domain_id=domain_id, user_id=user_id, inherited_to_projects=False
)
return ks_flask.ResourceBase.wrap_collection(
refs, collection_name='roles')
refs, collection_name='roles'
)
class DomainUserResource(ks_flask.ResourceBase):
@ -344,10 +357,14 @@ class DomainUserResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=_build_enforcement_target)
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.get_grant(
role_id, domain_id=domain_id, user_id=user_id,
inherited_to_projects=False)
role_id,
domain_id=domain_id,
user_id=user_id,
inherited_to_projects=False,
)
return None, http.client.NO_CONTENT
def put(self, domain_id=None, user_id=None, role_id=None):
@ -357,10 +374,15 @@ class DomainUserResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:create_grant',
build_target=_build_enforcement_target)
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.create_grant(
role_id, domain_id=domain_id, user_id=user_id,
inherited_to_projects=False, initiator=self.audit_initiator)
role_id,
domain_id=domain_id,
user_id=user_id,
inherited_to_projects=False,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
def delete(self, domain_id=None, user_id=None, role_id=None):
@ -370,11 +392,17 @@ class DomainUserResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:revoke_grant',
build_target=functools.partial(_build_enforcement_target,
allow_non_existing=True))
build_target=functools.partial(
_build_enforcement_target, allow_non_existing=True
),
)
PROVIDERS.assignment_api.delete_grant(
role_id, domain_id=domain_id, user_id=user_id,
inherited_to_projects=False, initiator=self.audit_initiator)
role_id,
domain_id=domain_id,
user_id=user_id,
inherited_to_projects=False,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
@ -386,12 +414,14 @@ class DomainGroupListResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:list_grants',
build_target=_build_enforcement_target)
build_target=_build_enforcement_target,
)
refs = PROVIDERS.assignment_api.list_grants(
domain_id=domain_id, group_id=group_id,
inherited_to_projects=False)
domain_id=domain_id, group_id=group_id, inherited_to_projects=False
)
return ks_flask.ResourceBase.wrap_collection(
refs, collection_name='roles')
refs, collection_name='roles'
)
class DomainGroupResource(ks_flask.ResourceBase):
@ -405,10 +435,14 @@ class DomainGroupResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=_build_enforcement_target)
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.get_grant(
role_id, domain_id=domain_id, group_id=group_id,
inherited_to_projects=False)
role_id,
domain_id=domain_id,
group_id=group_id,
inherited_to_projects=False,
)
return None, http.client.NO_CONTENT
def put(self, domain_id=None, group_id=None, role_id=None):
@ -418,10 +452,15 @@ class DomainGroupResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:create_grant',
build_target=_build_enforcement_target)
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.create_grant(
role_id, domain_id=domain_id, group_id=group_id,
inherited_to_projects=False, initiator=self.audit_initiator)
role_id,
domain_id=domain_id,
group_id=group_id,
inherited_to_projects=False,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
def delete(self, domain_id=None, group_id=None, role_id=None):
@ -431,11 +470,17 @@ class DomainGroupResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:revoke_grant',
build_target=functools.partial(_build_enforcement_target,
allow_non_existing=True))
build_target=functools.partial(
_build_enforcement_target, allow_non_existing=True
),
)
PROVIDERS.assignment_api.delete_grant(
role_id, domain_id=domain_id, group_id=group_id,
inherited_to_projects=False, initiator=self.audit_initiator)
role_id,
domain_id=domain_id,
group_id=group_id,
inherited_to_projects=False,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
@ -451,8 +496,8 @@ class DomainAPI(ks_flask.APIBase):
url=('/domains/<string:domain_id>/config'),
resource_kwargs={},
rel='domain_config',
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID}),
path_vars={'domain_id': json_home.Parameters.DOMAIN_ID},
),
ks_flask.construct_resource_map(
resource=DomainConfigGroupResource,
url='/domains/<string:domain_id>/config/<string:group>',
@ -460,81 +505,96 @@ class DomainAPI(ks_flask.APIBase):
rel='domain_config_group',
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'group': CONFIG_GROUP}),
'group': CONFIG_GROUP,
},
),
ks_flask.construct_resource_map(
resource=DomainConfigOptionResource,
url=('/domains/<string:domain_id>/config/<string:group>'
'/<string:option>'),
url=(
'/domains/<string:domain_id>/config/<string:group>'
'/<string:option>'
),
resource_kwargs={},
rel='domain_config_option',
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'group': CONFIG_GROUP,
'option': CONFIG_OPTION}),
'option': CONFIG_OPTION,
},
),
ks_flask.construct_resource_map(
resource=DefaultConfigResource,
url=('/domains/config/default'),
resource_kwargs={},
rel='domain_config_default',
path_vars={}),
path_vars={},
),
ks_flask.construct_resource_map(
resource=DefaultConfigGroupResource,
url='/domains/config/<string:group>/default',
resource_kwargs={},
rel='domain_config_default_group',
path_vars={
'group': CONFIG_GROUP}),
path_vars={'group': CONFIG_GROUP},
),
ks_flask.construct_resource_map(
resource=DefaultConfigOptionResource,
url=('/domains/config/<string:group>'
'/<string:option>/default'),
url=('/domains/config/<string:group>' '/<string:option>/default'),
resource_kwargs={},
rel='domain_config_default_option',
path_vars={
'group': CONFIG_GROUP,
'option': CONFIG_OPTION}),
path_vars={'group': CONFIG_GROUP, 'option': CONFIG_OPTION},
),
ks_flask.construct_resource_map(
resource=DomainUserListResource,
url=('/domains/<string:domain_id>/users'
'/<string:user_id>/roles'),
url=(
'/domains/<string:domain_id>/users' '/<string:user_id>/roles'
),
resource_kwargs={},
rel='domain_user_roles',
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'user_id': json_home.Parameters.USER_ID,
}),
},
),
ks_flask.construct_resource_map(
resource=DomainUserResource,
url=('/domains/<string:domain_id>/users'
'/<string:user_id>/roles/<string:role_id>'),
url=(
'/domains/<string:domain_id>/users'
'/<string:user_id>/roles/<string:role_id>'
),
resource_kwargs={},
rel='domain_user_role',
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'user_id': json_home.Parameters.USER_ID,
'role_id': json_home.Parameters.ROLE_ID
}),
'role_id': json_home.Parameters.ROLE_ID,
},
),
ks_flask.construct_resource_map(
resource=DomainGroupListResource,
url=('/domains/<string:domain_id>/groups'
'/<string:group_id>/roles'),
url=(
'/domains/<string:domain_id>/groups' '/<string:group_id>/roles'
),
resource_kwargs={},
rel='domain_group_roles',
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'group_id': json_home.Parameters.GROUP_ID,
}),
},
),
ks_flask.construct_resource_map(
resource=DomainGroupResource,
url=('/domains/<string:domain_id>/groups'
'/<string:group_id>/roles/<string:role_id>'),
url=(
'/domains/<string:domain_id>/groups'
'/<string:group_id>/roles/<string:role_id>'
),
resource_kwargs={},
rel='domain_group_role',
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'group_id': json_home.Parameters.GROUP_ID,
'role_id': json_home.Parameters.ROLE_ID
})
'role_id': json_home.Parameters.ROLE_ID,
},
),
]

View File

@ -51,14 +51,12 @@ class EC2TokensResource(EC2_S3_Resource.ResourceBase):
# other programming language libraries, JAVA for example.
signer = ec2_utils.Ec2Signer(creds_ref['secret'])
signature = signer.generate(credentials)
if utils.auth_str_equal(
credentials['signature'], signature):
if utils.auth_str_equal(credentials['signature'], signature):
return True
raise exception.Unauthorized(_('Invalid EC2 signature.'))
# Raise the exception when credentials.get('signature') is None
else:
raise exception.Unauthorized(
_('EC2 signature not supplied.'))
raise exception.Unauthorized(_('EC2 signature not supplied.'))
@ks_flask.unenforced_api
def post(self):
@ -86,7 +84,9 @@ class EC2TokensAPI(ks_flask.APIBase):
resource_kwargs={},
rel='ec2tokens',
resource_relation_func=(
json_home_relations.os_ec2_resource_rel_func))
json_home_relations.os_ec2_resource_rel_func
),
)
]

View File

@ -43,7 +43,8 @@ class EndpointResource(ks_flask.ResourceBase):
collection_key = 'endpoints'
member_key = 'endpoint'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='catalog_api', method='get_endpoint')
api='catalog_api', method='get_endpoint'
)
@staticmethod
def _validate_endpoint_region(endpoint):
@ -54,8 +55,10 @@ class EndpointResource(ks_flask.ResourceBase):
then for backward compatibility, we will auto-create the region.
"""
if (endpoint.get('region_id') is None and
endpoint.get('region') is not None):
if (
endpoint.get('region_id') is None
and endpoint.get('region') is not None
):
# To maintain backward compatibility with clients that are
# using the v3 API in the same way as they used the v2 API,
# create the endpoint region, if that region does not exist
@ -66,22 +69,26 @@ class EndpointResource(ks_flask.ResourceBase):
except exception.RegionNotFound:
region = dict(id=endpoint['region_id'])
PROVIDERS.catalog_api.create_region(
region, initiator=notifications.build_audit_initiator())
region, initiator=notifications.build_audit_initiator()
)
return endpoint
def _get_endpoint(self, endpoint_id):
ENFORCER.enforce_call(action='identity:get_endpoint')
return self.wrap_member(_filter_endpoint(
PROVIDERS.catalog_api.get_endpoint(endpoint_id)))
return self.wrap_member(
_filter_endpoint(PROVIDERS.catalog_api.get_endpoint(endpoint_id))
)
def _list_endpoints(self):
filters = ['interface', 'service_id', 'region_id']
ENFORCER.enforce_call(action='identity:list_endpoints',
filters=filters)
ENFORCER.enforce_call(
action='identity:list_endpoints', filters=filters
)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.catalog_api.list_endpoints(hints=hints)
return self.wrap_collection([_filter_endpoint(r) for r in refs],
hints=hints)
return self.wrap_collection(
[_filter_endpoint(r) for r in refs], hints=hints
)
def get(self, endpoint_id=None):
if endpoint_id is not None:
@ -96,7 +103,8 @@ class EndpointResource(ks_flask.ResourceBase):
endpoint = self._assign_unique_id(self._normalize_dict(endpoint))
endpoint = self._validate_endpoint_region(endpoint)
ref = PROVIDERS.catalog_api.create_endpoint(
endpoint['id'], endpoint, initiator=self.audit_initiator)
endpoint['id'], endpoint, initiator=self.audit_initiator
)
return self.wrap_member(_filter_endpoint(ref)), http.client.CREATED
def patch(self, endpoint_id):
@ -106,13 +114,15 @@ class EndpointResource(ks_flask.ResourceBase):
self._require_matching_id(endpoint)
endpoint = self._validate_endpoint_region(endpoint)
ref = PROVIDERS.catalog_api.update_endpoint(
endpoint_id, endpoint, initiator=self.audit_initiator)
endpoint_id, endpoint, initiator=self.audit_initiator
)
return self.wrap_member(_filter_endpoint(ref))
def delete(self, endpoint_id):
ENFORCER.enforce_call(action='identity:delete_endpoint')
PROVIDERS.catalog_api.delete_endpoint(endpoint_id,
initiator=self.audit_initiator)
PROVIDERS.catalog_api.delete_endpoint(
endpoint_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -121,9 +131,11 @@ class EndpointPolicyEndpointResource(flask_restful.Resource):
ENFORCER.enforce_call(action='identity:get_policy_for_endpoint')
PROVIDERS.catalog_api.get_endpoint(endpoint_id)
ref = PROVIDERS.endpoint_policy_api.get_policy_for_endpoint(
endpoint_id)
endpoint_id
)
return ks_flask.ResourceBase.wrap_member(
ref, collection_name='endpoints', member_name='policy')
ref, collection_name='endpoints', member_name='policy'
)
class EndpointAPI(ks_flask.APIBase):
@ -137,7 +149,8 @@ class EndpointAPI(ks_flask.APIBase):
resource_kwargs={},
rel='endpoint_policy',
resource_relation_func=_resource_rel_func,
path_vars={'endpoint_id': json_home.Parameters.ENDPOINT_ID})
path_vars={'endpoint_id': json_home.Parameters.ENDPOINT_ID},
)
]

View File

@ -51,7 +51,8 @@ class GroupsResource(ks_flask.ResourceBase):
collection_key = 'groups'
member_key = 'group'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='identity_api', method='get_group')
api='identity_api', method='get_group'
)
def get(self, group_id=None):
if group_id is not None:
@ -65,7 +66,7 @@ class GroupsResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:get_group',
build_target=_build_group_target_enforcement
build_target=_build_group_target_enforcement,
)
return self.wrap_member(PROVIDERS.identity_api.get_group(group_id))
@ -78,12 +79,14 @@ class GroupsResource(ks_flask.ResourceBase):
target = None
if self.oslo_context.domain_id:
target = {'group': {'domain_id': self.oslo_context.domain_id}}
ENFORCER.enforce_call(action='identity:list_groups', filters=filters,
target_attr=target)
ENFORCER.enforce_call(
action='identity:list_groups', filters=filters, target_attr=target
)
hints = self.build_driver_hints(filters)
domain = self._get_domain_id_for_list_request()
refs = PROVIDERS.identity_api.list_groups(domain_scope=domain,
hints=hints)
refs = PROVIDERS.identity_api.list_groups(
domain_scope=domain, hints=hints
)
if self.oslo_context.domain_id:
filtered_refs = []
for ref in refs:
@ -106,7 +109,8 @@ class GroupsResource(ks_flask.ResourceBase):
group = self._normalize_dict(group)
group = self._normalize_domain_id(group)
ref = PROVIDERS.identity_api.create_group(
group, initiator=self.audit_initiator)
group, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def patch(self, group_id):
@ -116,13 +120,14 @@ class GroupsResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:update_group',
build_target=_build_group_target_enforcement
build_target=_build_group_target_enforcement,
)
group = self.request_body_json.get('group', {})
validation.lazy_validate(schema.group_update, group)
self._require_matching_id(group)
ref = PROVIDERS.identity_api.update_group(
group_id, group, initiator=self.audit_initiator)
group_id, group, initiator=self.audit_initiator
)
return self.wrap_member(ref)
def delete(self, group_id):
@ -132,7 +137,8 @@ class GroupsResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(action='identity:delete_group')
PROVIDERS.identity_api.delete_group(
group_id, initiator=self.audit_initiator)
group_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -151,19 +157,24 @@ class GroupUsersResource(ks_flask.ResourceBase):
# data, leage target empty. This is the safest route and does not
# leak data before enforcement happens.
pass
ENFORCER.enforce_call(action='identity:list_users_in_group',
target_attr=target, filters=filters)
ENFORCER.enforce_call(
action='identity:list_users_in_group',
target_attr=target,
filters=filters,
)
hints = ks_flask.ResourceBase.build_driver_hints(filters)
refs = PROVIDERS.identity_api.list_users_in_group(
group_id, hints=hints)
if (self.oslo_context.domain_id):
group_id, hints=hints
)
if self.oslo_context.domain_id:
filtered_refs = []
for ref in refs:
if ref['domain_id'] == self.oslo_context.domain_id:
filtered_refs.append(ref)
refs = filtered_refs
return ks_flask.ResourceBase.wrap_collection(
refs, hints=hints, collection_name='users')
refs, hints=hints, collection_name='users'
)
class UserGroupCRUDResource(flask_restful.Resource):
@ -191,8 +202,10 @@ class UserGroupCRUDResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:check_user_in_group',
build_target=functools.partial(self._build_enforcement_target_attr,
user_id, group_id))
build_target=functools.partial(
self._build_enforcement_target_attr, user_id, group_id
),
)
PROVIDERS.identity_api.check_user_in_group(user_id, group_id)
return None, http.client.NO_CONTENT
@ -203,10 +216,13 @@ class UserGroupCRUDResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:add_user_to_group',
build_target=functools.partial(self._build_enforcement_target_attr,
user_id, group_id))
build_target=functools.partial(
self._build_enforcement_target_attr, user_id, group_id
),
)
PROVIDERS.identity_api.add_user_to_group(
user_id, group_id, initiator=notifications.build_audit_initiator())
user_id, group_id, initiator=notifications.build_audit_initiator()
)
return None, http.client.NO_CONTENT
def delete(self, group_id, user_id):
@ -216,10 +232,13 @@ class UserGroupCRUDResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:remove_user_from_group',
build_target=functools.partial(self._build_enforcement_target_attr,
user_id, group_id))
build_target=functools.partial(
self._build_enforcement_target_attr, user_id, group_id
),
)
PROVIDERS.identity_api.remove_user_from_group(
user_id, group_id, initiator=notifications.build_audit_initiator())
user_id, group_id, initiator=notifications.build_audit_initiator()
)
return None, http.client.NO_CONTENT
@ -233,7 +252,8 @@ class GroupAPI(ks_flask.APIBase):
url='/groups/<string:group_id>/users',
resource_kwargs={},
rel='group_users',
path_vars={'group_id': json_home.Parameters.GROUP_ID}),
path_vars={'group_id': json_home.Parameters.GROUP_ID},
),
ks_flask.construct_resource_map(
resource=UserGroupCRUDResource,
url='/groups/<string:group_id>/users/<string:user_id>',
@ -241,7 +261,9 @@ class GroupAPI(ks_flask.APIBase):
rel='group_user',
path_vars={
'group_id': json_home.Parameters.GROUP_ID,
'user_id': json_home.Parameters.USER_ID})
'user_id': json_home.Parameters.USER_ID,
},
),
]

View File

@ -55,11 +55,17 @@ class LimitsResource(ks_flask.ResourceBase):
member_key = 'limit'
json_home_resource_status = json_home.Status.EXPERIMENTAL
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='unified_limit_api', method='get_limit')
api='unified_limit_api', method='get_limit'
)
def _list_limits(self):
filters = ['service_id', 'region_id', 'resource_name', 'project_id',
'domain_id']
filters = [
'service_id',
'region_id',
'resource_name',
'project_id',
'domain_id',
]
ENFORCER.enforce_call(action='identity:list_limits', filters=filters)
@ -90,8 +96,10 @@ class LimitsResource(ks_flask.ResourceBase):
return self.wrap_collection(filtered_refs, hints=hints)
def _get_limit(self, limit_id):
ENFORCER.enforce_call(action='identity:get_limit',
build_target=_build_limit_enforcement_target)
ENFORCER.enforce_call(
action='identity:get_limit',
build_target=_build_limit_enforcement_target,
)
ref = PROVIDERS.unified_limit_api.get_limit(limit_id)
return self.wrap_member(ref)
@ -103,10 +111,13 @@ class LimitsResource(ks_flask.ResourceBase):
def post(self):
ENFORCER.enforce_call(action='identity:create_limits')
limits_b = (flask.request.get_json(silent=True, force=True) or {}).get(
'limits', {})
'limits', {}
)
validation.lazy_validate(schema.limit_create, limits_b)
limits = [self._assign_unique_id(self._normalize_dict(limit))
for limit in limits_b]
limits = [
self._assign_unique_id(self._normalize_dict(limit))
for limit in limits_b
]
refs = PROVIDERS.unified_limit_api.create_limits(limits)
refs = self.wrap_collection(refs)
refs.pop('links')
@ -115,7 +126,8 @@ class LimitsResource(ks_flask.ResourceBase):
def patch(self, limit_id):
ENFORCER.enforce_call(action='identity:update_limit')
limit = (flask.request.get_json(silent=True, force=True) or {}).get(
'limit', {})
'limit', {}
)
validation.lazy_validate(schema.limit_update, limit)
self._require_matching_id(limit)
ref = PROVIDERS.unified_limit_api.update_limit(limit_id, limit)
@ -123,8 +135,10 @@ class LimitsResource(ks_flask.ResourceBase):
def delete(self, limit_id):
ENFORCER.enforce_call(action='identity:delete_limit')
return (PROVIDERS.unified_limit_api.delete_limit(limit_id),
http.client.NO_CONTENT)
return (
PROVIDERS.unified_limit_api.delete_limit(limit_id),
http.client.NO_CONTENT,
)
class LimitModelResource(flask_restful.Resource):
@ -144,7 +158,7 @@ class LimitsAPI(ks_flask.APIBase):
resource_kwargs={},
url='/limits/model',
rel='limit_model',
status=json_home.Status.EXPERIMENTAL
status=json_home.Status.EXPERIMENTAL,
)
]

View File

@ -34,7 +34,8 @@ _build_resource_relation = json_home_relations.os_ep_filter_resource_rel_func
_build_parameter_relation = json_home_relations.os_ep_filter_parameter_rel_func
_ENDPOINT_GROUP_PARAMETER_RELATION = _build_parameter_relation(
parameter_name='endpoint_group_id')
parameter_name='endpoint_group_id'
)
# NOTE(morgan): This is shared from keystone.api.endpoint, this is a special
@ -58,17 +59,20 @@ class EndpointGroupsResource(ks_flask.ResourceBase):
if key not in valid_filter_keys:
raise exception.ValidationError(
attribute=' or '.join(valid_filter_keys),
target='endpoint_group')
target='endpoint_group',
)
def _get_endpoint_group(self, endpoint_group_id):
ENFORCER.enforce_call(action='identity:get_endpoint_group')
return self.wrap_member(
PROVIDERS.catalog_api.get_endpoint_group(endpoint_group_id))
PROVIDERS.catalog_api.get_endpoint_group(endpoint_group_id)
)
def _list_endpoint_groups(self):
filters = ('name')
ENFORCER.enforce_call(action='identity:list_endpoint_groups',
filters=filters)
filters = 'name'
ENFORCER.enforce_call(
action='identity:list_endpoint_groups', filters=filters
)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.catalog_api.list_endpoint_groups(hints)
return self.wrap_collection(refs, hints=hints)
@ -89,8 +93,14 @@ class EndpointGroupsResource(ks_flask.ResourceBase):
raise exception.ValidationError(message=msg)
self._require_valid_filter(ep_group)
ep_group = self._assign_unique_id(ep_group)
return self.wrap_member(PROVIDERS.catalog_api.create_endpoint_group(
ep_group['id'], ep_group)), http.client.CREATED
return (
self.wrap_member(
PROVIDERS.catalog_api.create_endpoint_group(
ep_group['id'], ep_group
)
),
http.client.CREATED,
)
def patch(self, endpoint_group_id):
ENFORCER.enforce_call(action='identity:update_endpoint_group')
@ -99,13 +109,18 @@ class EndpointGroupsResource(ks_flask.ResourceBase):
if 'filters' in ep_group:
self._require_valid_filter(ep_group)
self._require_matching_id(ep_group)
return self.wrap_member(PROVIDERS.catalog_api.update_endpoint_group(
endpoint_group_id, ep_group))
return self.wrap_member(
PROVIDERS.catalog_api.update_endpoint_group(
endpoint_group_id, ep_group
)
)
def delete(self, endpoint_group_id):
ENFORCER.enforce_call(action='identity:delete_endpoint_group')
return (PROVIDERS.catalog_api.delete_endpoint_group(endpoint_group_id),
http.client.NO_CONTENT)
return (
PROVIDERS.catalog_api.delete_endpoint_group(endpoint_group_id),
http.client.NO_CONTENT,
)
class EPFilterEndpointProjectsResource(flask_restful.Resource):
@ -114,10 +129,13 @@ class EPFilterEndpointProjectsResource(flask_restful.Resource):
ENFORCER.enforce_call(action='identity:list_projects_for_endpoint')
PROVIDERS.catalog_api.get_endpoint(endpoint_id)
refs = PROVIDERS.catalog_api.list_projects_for_endpoint(endpoint_id)
projects = [PROVIDERS.resource_api.get_project(ref['project_id'])
for ref in refs]
projects = [
PROVIDERS.resource_api.get_project(ref['project_id'])
for ref in refs
]
return ks_flask.ResourceBase.wrap_collection(
projects, collection_name='projects')
projects, collection_name='projects'
)
class EPFilterProjectsEndpointsResource(flask_restful.Resource):
@ -126,7 +144,8 @@ class EPFilterProjectsEndpointsResource(flask_restful.Resource):
PROVIDERS.catalog_api.get_endpoint(endpoint_id)
PROVIDERS.resource_api.get_project(project_id)
PROVIDERS.catalog_api.check_endpoint_in_project(
endpoint_id, project_id)
endpoint_id, project_id
)
return None, http.client.NO_CONTENT
def put(self, project_id, endpoint_id):
@ -138,8 +157,12 @@ class EPFilterProjectsEndpointsResource(flask_restful.Resource):
def delete(self, project_id, endpoint_id):
ENFORCER.enforce_call(action='identity:remove_endpoint_from_project')
return (PROVIDERS.catalog_api.remove_endpoint_from_project(
endpoint_id, project_id), http.client.NO_CONTENT)
return (
PROVIDERS.catalog_api.remove_endpoint_from_project(
endpoint_id, project_id
),
http.client.NO_CONTENT,
)
class EPFilterProjectEndpointsListResource(flask_restful.Resource):
@ -147,49 +170,62 @@ class EPFilterProjectEndpointsListResource(flask_restful.Resource):
ENFORCER.enforce_call(action='identity:list_endpoints_for_project')
PROVIDERS.resource_api.get_project(project_id)
filtered_endpoints = PROVIDERS.catalog_api.list_endpoints_for_project(
project_id)
project_id
)
return ks_flask.ResourceBase.wrap_collection(
[_filter_endpoint(v) for v in filtered_endpoints.values()],
collection_name='endpoints')
collection_name='endpoints',
)
class EndpointFilterProjectEndpointGroupsListResource(flask_restful.Resource):
def get(self, project_id):
ENFORCER.enforce_call(
action='identity:list_endpoint_groups_for_project')
action='identity:list_endpoint_groups_for_project'
)
return EndpointGroupsResource.wrap_collection(
PROVIDERS.catalog_api.get_endpoint_groups_for_project(project_id))
PROVIDERS.catalog_api.get_endpoint_groups_for_project(project_id)
)
class EndpointFilterEPGroupsProjects(flask_restful.Resource):
def get(self, endpoint_group_id):
ENFORCER.enforce_call(
action='identity:list_projects_associated_with_endpoint_group')
endpoint_group_refs = (PROVIDERS.catalog_api.
list_projects_associated_with_endpoint_group(
endpoint_group_id))
action='identity:list_projects_associated_with_endpoint_group'
)
endpoint_group_refs = (
PROVIDERS.catalog_api.list_projects_associated_with_endpoint_group(
endpoint_group_id
)
)
projects = []
for endpoint_group_ref in endpoint_group_refs:
project = PROVIDERS.resource_api.get_project(
endpoint_group_ref['project_id'])
endpoint_group_ref['project_id']
)
if project:
projects.append(project)
return ks_flask.ResourceBase.wrap_collection(
projects, collection_name='projects')
projects, collection_name='projects'
)
class EndpointFilterEPGroupsEndpoints(flask_restful.Resource):
def get(self, endpoint_group_id):
ENFORCER.enforce_call(
action='identity:list_endpoints_associated_with_endpoint_group')
filtered_endpoints = (PROVIDERS.catalog_api.
get_endpoints_filtered_by_endpoint_group(
endpoint_group_id))
action='identity:list_endpoints_associated_with_endpoint_group'
)
filtered_endpoints = (
PROVIDERS.catalog_api.get_endpoints_filtered_by_endpoint_group(
endpoint_group_id
)
)
return ks_flask.ResourceBase.wrap_collection(
[_filter_endpoint(e) for e in filtered_endpoints],
collection_name='endpoints')
collection_name='endpoints',
)
class EPFilterGroupsProjectsResource(ks_flask.ResourceBase):
@ -198,10 +234,14 @@ class EPFilterGroupsProjectsResource(ks_flask.ResourceBase):
@classmethod
def _add_self_referential_link(cls, ref, collection_name=None):
url = ('/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s'
'/projects/%(project_id)s' % {
'endpoint_group_id': ref['endpoint_group_id'],
'project_id': ref['project_id']})
url = (
'/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s'
'/projects/%(project_id)s'
% {
'endpoint_group_id': ref['endpoint_group_id'],
'project_id': ref['project_id'],
}
)
ref.setdefault('links', {})
ref['links']['self'] = url
@ -210,7 +250,8 @@ class EPFilterGroupsProjectsResource(ks_flask.ResourceBase):
PROVIDERS.resource_api.get_project(project_id)
PROVIDERS.catalog_api.get_endpoint_group(endpoint_group_id)
ref = PROVIDERS.catalog_api.get_endpoint_group_in_project(
endpoint_group_id, project_id)
endpoint_group_id, project_id
)
return self.wrap_member(ref)
def put(self, endpoint_group_id, project_id):
@ -218,16 +259,19 @@ class EPFilterGroupsProjectsResource(ks_flask.ResourceBase):
PROVIDERS.resource_api.get_project(project_id)
PROVIDERS.catalog_api.get_endpoint_group(endpoint_group_id)
PROVIDERS.catalog_api.add_endpoint_group_to_project(
endpoint_group_id, project_id)
endpoint_group_id, project_id
)
return None, http.client.NO_CONTENT
def delete(self, endpoint_group_id, project_id):
ENFORCER.enforce_call(
action='identity:remove_endpoint_group_from_project')
action='identity:remove_endpoint_group_from_project'
)
PROVIDERS.resource_api.get_project(project_id)
PROVIDERS.catalog_api.get_endpoint_group(endpoint_group_id)
PROVIDERS.catalog_api.remove_endpoint_group_from_project(
endpoint_group_id, project_id)
endpoint_group_id, project_id
)
return None, http.client.NO_CONTENT
@ -243,9 +287,8 @@ class EPFilterAPI(ks_flask.APIBase):
resource_kwargs={},
rel='endpoint_projects',
resource_relation_func=_build_resource_relation,
path_vars={
'endpoint_id': json_home.Parameters.ENDPOINT_ID
}),
path_vars={'endpoint_id': json_home.Parameters.ENDPOINT_ID},
),
ks_flask.construct_resource_map(
resource=EPFilterProjectsEndpointsResource,
url='/projects/<string:project_id>/endpoints/<string:endpoint_id>',
@ -254,21 +297,25 @@ class EPFilterAPI(ks_flask.APIBase):
resource_relation_func=_build_resource_relation,
path_vars={
'endpoint_id': json_home.Parameters.ENDPOINT_ID,
'project_id': json_home.Parameters.PROJECT_ID}),
'project_id': json_home.Parameters.PROJECT_ID,
},
),
ks_flask.construct_resource_map(
resource=EPFilterProjectEndpointsListResource,
url='/projects/<string:project_id>/endpoints',
resource_kwargs={},
rel='project_endpoints',
resource_relation_func=_build_resource_relation,
path_vars={'project_id': json_home.Parameters.PROJECT_ID}),
path_vars={'project_id': json_home.Parameters.PROJECT_ID},
),
ks_flask.construct_resource_map(
resource=EndpointFilterProjectEndpointGroupsListResource,
url='/projects/<string:project_id>/endpoint_groups',
resource_kwargs={},
rel='project_endpoint_groups',
resource_relation_func=_build_resource_relation,
path_vars={'project_id': json_home.Parameters.PROJECT_ID}),
path_vars={'project_id': json_home.Parameters.PROJECT_ID},
),
ks_flask.construct_resource_map(
resource=EndpointFilterEPGroupsEndpoints,
url='/endpoint_groups/<string:endpoint_group_id>/endpoints',
@ -276,7 +323,9 @@ class EPFilterAPI(ks_flask.APIBase):
rel='endpoints_in_endpoint_group',
resource_relation_func=_build_resource_relation,
path_vars={
'endpoint_group_id': _ENDPOINT_GROUP_PARAMETER_RELATION}),
'endpoint_group_id': _ENDPOINT_GROUP_PARAMETER_RELATION
},
),
ks_flask.construct_resource_map(
resource=EndpointFilterEPGroupsProjects,
url='/endpoint_groups/<string:endpoint_group_id>/projects',
@ -284,17 +333,23 @@ class EPFilterAPI(ks_flask.APIBase):
rel='projects_associated_with_endpoint_group',
resource_relation_func=_build_resource_relation,
path_vars={
'endpoint_group_id': _ENDPOINT_GROUP_PARAMETER_RELATION}),
'endpoint_group_id': _ENDPOINT_GROUP_PARAMETER_RELATION
},
),
ks_flask.construct_resource_map(
resource=EPFilterGroupsProjectsResource,
url=('/endpoint_groups/<string:endpoint_group_id>/projects/'
'<string:project_id>'),
url=(
'/endpoint_groups/<string:endpoint_group_id>/projects/'
'<string:project_id>'
),
resource_kwargs={},
rel='endpoint_group_to_project_association',
resource_relation_func=_build_resource_relation,
path_vars={'project_id': json_home.Parameters.PROJECT_ID,
'endpoint_group_id': _ENDPOINT_GROUP_PARAMETER_RELATION
}),
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'endpoint_group_id': _ENDPOINT_GROUP_PARAMETER_RELATION,
},
),
]

View File

@ -43,7 +43,8 @@ _build_resource_relation = json_home_relations.os_federation_resource_rel_func
IDP_ID_PARAMETER_RELATION = _build_param_relation(parameter_name='idp_id')
PROTOCOL_ID_PARAMETER_RELATION = _build_param_relation(
parameter_name='protocol_id')
parameter_name='protocol_id'
)
SP_ID_PARAMETER_RELATION = _build_param_relation(parameter_name='sp_id')
@ -76,10 +77,17 @@ class IdentityProvidersResource(_ResourceBase):
collection_key = 'identity_providers'
member_key = 'identity_provider'
api_prefix = '/OS-FEDERATION'
_public_parameters = frozenset(['id', 'enabled', 'description',
'remote_ids', 'links', 'domain_id',
'authorization_ttl'
])
_public_parameters = frozenset(
[
'id',
'enabled',
'description',
'remote_ids',
'links',
'domain_id',
'authorization_ttl',
]
)
_id_path_param_name_override = 'idp_id'
@staticmethod
@ -117,8 +125,9 @@ class IdentityProvidersResource(_ResourceBase):
GET/HEAD /OS-FEDERATION/identity_providers
"""
filters = ['id', 'enabled']
ENFORCER.enforce_call(action='identity:list_identity_providers',
filters=filters)
ENFORCER.enforce_call(
action='identity:list_identity_providers', filters=filters
)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.federation_api.list_idps(hints=hints)
refs = [self.filter_params(r) for r in refs]
@ -135,12 +144,10 @@ class IdentityProvidersResource(_ResourceBase):
"""
ENFORCER.enforce_call(action='identity:create_identity_provider')
idp = self.request_body_json.get('identity_provider', {})
validation.lazy_validate(schema.identity_provider_create,
idp)
validation.lazy_validate(schema.identity_provider_create, idp)
idp = self._normalize_dict(idp)
idp.setdefault('enabled', False)
idp_ref = PROVIDERS.federation_api.create_idp(
idp_id, idp)
idp_ref = PROVIDERS.federation_api.create_idp(idp_id, idp)
return self.wrap_member(idp_ref), http.client.CREATED
def patch(self, idp_id):
@ -148,8 +155,7 @@ class IdentityProvidersResource(_ResourceBase):
idp = self.request_body_json.get('identity_provider', {})
validation.lazy_validate(schema.identity_provider_update, idp)
idp = self._normalize_dict(idp)
idp_ref = PROVIDERS.federation_api.update_idp(
idp_id, idp)
idp_ref = PROVIDERS.federation_api.update_idp(idp_id, idp)
return self.wrap_member(idp_ref)
def delete(self, idp_id):
@ -162,8 +168,7 @@ class _IdentityProvidersProtocolsResourceBase(_ResourceBase):
collection_key = 'protocols'
member_key = 'protocol'
_public_parameters = frozenset(['id', 'mapping_id', 'links'])
json_home_additional_parameters = {
'idp_id': IDP_ID_PARAMETER_RELATION}
json_home_additional_parameters = {'idp_id': IDP_ID_PARAMETER_RELATION}
json_home_collection_resource_name_override = 'identity_provider_protocols'
json_home_member_resource_name_override = 'identity_provider_protocol'
@ -179,7 +184,8 @@ class _IdentityProvidersProtocolsResourceBase(_ResourceBase):
"""
ref.setdefault('links', {})
ref['links']['identity_provider'] = ks_flask.base_url(
path=ref['idp_id'])
path=ref['idp_id']
)
class IDPProtocolsListResource(_IdentityProvidersProtocolsResourceBase):
@ -220,8 +226,9 @@ class IDPProtocolsCRUDResource(_IdentityProvidersProtocolsResourceBase):
protocol = self.request_body_json.get('protocol', {})
validation.lazy_validate(schema.protocol_create, protocol)
protocol = self._normalize_dict(protocol)
ref = PROVIDERS.federation_api.create_protocol(idp_id, protocol_id,
protocol)
ref = PROVIDERS.federation_api.create_protocol(
idp_id, protocol_id, protocol
)
return self.wrap_member(ref), http.client.CREATED
def patch(self, idp_id, protocol_id):
@ -233,8 +240,9 @@ class IDPProtocolsCRUDResource(_IdentityProvidersProtocolsResourceBase):
ENFORCER.enforce_call(action='identity:update_protocol')
protocol = self.request_body_json.get('protocol', {})
validation.lazy_validate(schema.protocol_update, protocol)
ref = PROVIDERS.federation_api.update_protocol(idp_id, protocol_id,
protocol)
ref = PROVIDERS.federation_api.update_protocol(
idp_id, protocol_id, protocol
)
return self.wrap_member(ref)
def delete(self, idp_id, protocol_id):
@ -264,8 +272,9 @@ class MappingResource(_ResourceBase):
HEAD/GET /OS-FEDERATION/mappings/{mapping_id}
"""
ENFORCER.enforce_call(action='identity:get_mapping')
return self.wrap_member(PROVIDERS.federation_api.get_mapping(
mapping_id))
return self.wrap_member(
PROVIDERS.federation_api.get_mapping(mapping_id)
)
def _list_mappings(self):
"""List mappings.
@ -276,22 +285,28 @@ class MappingResource(_ResourceBase):
return self.wrap_collection(PROVIDERS.federation_api.list_mappings())
def _internal_normalize_and_validate_attribute_mapping(
self, action_executed_message="created"):
self, action_executed_message="created"
):
mapping = self.request_body_json.get('mapping', {})
mapping = self._normalize_dict(mapping)
if not mapping.get('schema_version'):
default_schema_version =\
default_schema_version = (
utils.get_default_attribute_mapping_schema_version()
LOG.debug("A mapping [%s] was %s without providing a "
"'schema_version'; therefore, we need to set one. The "
"current default is [%s]. We will use this value for "
"the attribute mapping being registered. It is "
"recommended that one does not rely on this default "
"value, as it can change, and the already persisted "
"attribute mappings will remain with the previous "
"default values.", mapping, action_executed_message,
default_schema_version)
)
LOG.debug(
"A mapping [%s] was %s without providing a "
"'schema_version'; therefore, we need to set one. The "
"current default is [%s]. We will use this value for "
"the attribute mapping being registered. It is "
"recommended that one does not rely on this default "
"value, as it can change, and the already persisted "
"attribute mappings will remain with the previous "
"default values.",
mapping,
action_executed_message,
default_schema_version,
)
mapping['schema_version'] = default_schema_version
utils.validate_mapping_structure(mapping)
return mapping
@ -304,7 +319,8 @@ class MappingResource(_ResourceBase):
ENFORCER.enforce_call(action='identity:create_mapping')
am = self._internal_normalize_and_validate_attribute_mapping(
"registered")
"registered"
)
mapping_ref = PROVIDERS.federation_api.create_mapping(mapping_id, am)
return self.wrap_member(mapping_ref), http.client.CREATED
@ -334,8 +350,17 @@ class MappingResource(_ResourceBase):
class ServiceProvidersResource(_ResourceBase):
collection_key = 'service_providers'
member_key = 'service_provider'
_public_parameters = frozenset(['auth_url', 'id', 'enabled', 'description',
'links', 'relay_state_prefix', 'sp_url'])
_public_parameters = frozenset(
[
'auth_url',
'id',
'enabled',
'description',
'links',
'relay_state_prefix',
'sp_url',
]
)
_id_path_param_name_override = 'sp_id'
api_prefix = '/OS-FEDERATION'
@ -358,12 +383,14 @@ class ServiceProvidersResource(_ResourceBase):
GET/HEAD /OS-FEDERATION/service_providers
"""
filters = ['id', 'enabled']
ENFORCER.enforce_call(action='identity:list_service_providers',
filters=filters)
ENFORCER.enforce_call(
action='identity:list_service_providers', filters=filters
)
hints = self.build_driver_hints(filters)
refs = [self.filter_params(r)
for r in
PROVIDERS.federation_api.list_sps(hints=hints)]
refs = [
self.filter_params(r)
for r in PROVIDERS.federation_api.list_sps(hints=hints)
]
return self.wrap_collection(refs, hints=hints)
def put(self, sp_id):
@ -452,7 +479,7 @@ class OSFederationAuthResource(flask_restful.Resource):
'methods': [protocol_id],
protocol_id: {
'identity_provider': idp_id,
'protocol': protocol_id
'protocol': protocol_id,
},
}
}
@ -476,17 +503,22 @@ class OSFederationAPI(ks_flask.APIBase):
url='/saml2/metadata',
resource_kwargs={},
rel='metadata',
resource_relation_func=_build_resource_relation),
resource_relation_func=_build_resource_relation,
),
ks_flask.construct_resource_map(
resource=OSFederationAuthResource,
url=('/identity_providers/<string:idp_id>/protocols/'
'<string:protocol_id>/auth'),
url=(
'/identity_providers/<string:idp_id>/protocols/'
'<string:protocol_id>/auth'
),
resource_kwargs={},
rel='identity_provider_protocol_auth',
resource_relation_func=_build_resource_relation,
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION}),
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
},
),
]
@ -505,15 +537,17 @@ class OSFederationIdentityProvidersProtocolsAPI(ks_flask.APIBase):
resource_mapping = [
ks_flask.construct_resource_map(
resource=IDPProtocolsCRUDResource,
url=('/OS-FEDERATION/identity_providers/<string:idp_id>/protocols/'
'<string:protocol_id>'),
url=(
'/OS-FEDERATION/identity_providers/<string:idp_id>/protocols/'
'<string:protocol_id>'
),
resource_kwargs={},
rel='identity_provider_protocol',
resource_relation_func=_build_resource_relation,
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION,
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION
}
'protocol_id': PROTOCOL_ID_PARAMETER_RELATION,
},
),
ks_flask.construct_resource_map(
resource=IDPProtocolsListResource,
@ -521,9 +555,7 @@ class OSFederationIdentityProvidersProtocolsAPI(ks_flask.APIBase):
resource_kwargs={},
rel='identity_provider_protocols',
resource_relation_func=_build_resource_relation,
path_vars={
'idp_id': IDP_ID_PARAMETER_RELATION
}
path_vars={'idp_id': IDP_ID_PARAMETER_RELATION},
),
]
@ -549,5 +581,5 @@ APIs = (
OSFederationIdentityProvidersAPI,
OSFederationIdentityProvidersProtocolsAPI,
OSFederationMappingsAPI,
OSFederationServiceProvidersAPI
OSFederationServiceProvidersAPI,
)

View File

@ -32,9 +32,14 @@ LOG = log.getLogger(__name__)
_build_resource_relation = json_home_relations.os_inherit_resource_rel_func
def _build_enforcement_target_attr(role_id=None, user_id=None, group_id=None,
project_id=None, domain_id=None,
allow_non_existing=False):
def _build_enforcement_target_attr(
role_id=None,
user_id=None,
group_id=None,
project_id=None,
domain_id=None,
allow_non_existing=False,
):
"""Check protection for role grant APIs.
The policy rule might want to inspect attributes of any of the entities
@ -59,25 +64,33 @@ def _build_enforcement_target_attr(role_id=None, user_id=None, group_id=None,
try:
target['role'] = PROVIDERS.role_api.get_role(role_id)
except exception.RoleNotFound:
LOG.info('Role (%(role_id)s) not found, Enforcement target of '
'`role` remaind empty', {'role_id': role_id})
LOG.info(
'Role (%(role_id)s) not found, Enforcement target of '
'`role` remaind empty',
{'role_id': role_id},
)
target['role'] = {}
if user_id:
try:
target['user'] = PROVIDERS.identity_api.get_user(user_id)
except exception.UserNotFound:
if not allow_non_existing:
LOG.info('User (%(user_id)s) was not found. Enforcement target'
' of `user` remains empty.', {'user_id': user_id})
LOG.info(
'User (%(user_id)s) was not found. Enforcement target'
' of `user` remains empty.',
{'user_id': user_id},
)
target['user'] = {}
else:
try:
target['group'] = PROVIDERS.identity_api.get_group(group_id)
except exception.GroupNotFound:
if not allow_non_existing:
LOG.info('Group (%(group_id)s) was not found. Enforcement '
'target of `group` remains empty.',
{'group_id': group_id})
LOG.info(
'Group (%(group_id)s) was not found. Enforcement '
'target of `group` remains empty.',
{'group_id': group_id},
)
target['group'] = {}
# NOTE(lbragstad): This if/else check will need to be expanded in the
@ -86,17 +99,21 @@ def _build_enforcement_target_attr(role_id=None, user_id=None, group_id=None,
try:
target['domain'] = PROVIDERS.resource_api.get_domain(domain_id)
except exception.DomainNotFound:
LOG.info('Domain (%(domain_id)s) was not found. Enforcement '
'target of `domain` remains empty.',
{'domain_id': domain_id})
LOG.info(
'Domain (%(domain_id)s) was not found. Enforcement '
'target of `domain` remains empty.',
{'domain_id': domain_id},
)
target['domain'] = {}
elif project_id:
try:
target['project'] = PROVIDERS.resource_api.get_project(project_id)
except exception.ProjectNotFound:
LOG.info('Project (%(project_id)s) was not found. Enforcement '
'target of `project` remains empty.',
{'project_id': project_id})
LOG.info(
'Project (%(project_id)s) was not found. Enforcement '
'target of `project` remains empty.',
{'project_id': project_id},
)
target['project'] = {}
return target
@ -111,13 +128,19 @@ class OSInheritDomainGroupRolesResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.get_grant(
domain_id=domain_id, group_id=group_id, role_id=role_id,
inherited_to_projects=True)
domain_id=domain_id,
group_id=group_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def put(self, domain_id, group_id, role_id):
@ -128,13 +151,19 @@ class OSInheritDomainGroupRolesResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:create_grant',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.create_grant(
domain_id=domain_id, group_id=group_id, role_id=role_id,
inherited_to_projects=True)
domain_id=domain_id,
group_id=group_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def delete(self, domain_id, group_id, role_id):
@ -145,13 +174,19 @@ class OSInheritDomainGroupRolesResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:revoke_grant',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.delete_grant(
domain_id=domain_id, group_id=group_id, role_id=role_id,
inherited_to_projects=True)
domain_id=domain_id,
group_id=group_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
@ -164,13 +199,18 @@ class OSInheritDomainGroupRolesListResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:list_grants',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
group_id=group_id,
),
)
refs = PROVIDERS.assignment_api.list_grants(
domain_id=domain_id, group_id=group_id, inherited_to_projects=True)
domain_id=domain_id, group_id=group_id, inherited_to_projects=True
)
return ks_flask.ResourceBase.wrap_collection(
refs, collection_name='roles')
refs, collection_name='roles'
)
class OSInheritDomainUserRolesResource(flask_restful.Resource):
@ -182,13 +222,19 @@ class OSInheritDomainUserRolesResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.get_grant(
domain_id=domain_id, user_id=user_id, role_id=role_id,
inherited_to_projects=True)
domain_id=domain_id,
user_id=user_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def put(self, domain_id, user_id, role_id):
@ -199,13 +245,19 @@ class OSInheritDomainUserRolesResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:create_grant',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.create_grant(
domain_id=domain_id, user_id=user_id, role_id=role_id,
inherited_to_projects=True)
domain_id=domain_id,
user_id=user_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def delete(self, domain_id, user_id, role_id):
@ -216,13 +268,19 @@ class OSInheritDomainUserRolesResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:revoke_grant',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.delete_grant(
domain_id=domain_id, user_id=user_id, role_id=role_id,
inherited_to_projects=True)
domain_id=domain_id,
user_id=user_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
@ -235,13 +293,18 @@ class OSInheritDomainUserRolesListResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:list_grants',
build_target=functools.partial(_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id))
build_target=functools.partial(
_build_enforcement_target_attr,
domain_id=domain_id,
user_id=user_id,
),
)
refs = PROVIDERS.assignment_api.list_grants(
domain_id=domain_id, user_id=user_id, inherited_to_projects=True)
domain_id=domain_id, user_id=user_id, inherited_to_projects=True
)
return ks_flask.ResourceBase.wrap_collection(
refs, collection_name='roles')
refs, collection_name='roles'
)
class OSInheritProjectUserResource(flask_restful.Resource):
@ -253,13 +316,19 @@ class OSInheritProjectUserResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=functools.partial(_build_enforcement_target_attr,
project_id=project_id,
user_id=user_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
project_id=project_id,
user_id=user_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.get_grant(
project_id=project_id, user_id=user_id, role_id=role_id,
inherited_to_projects=True)
project_id=project_id,
user_id=user_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def put(self, project_id, user_id, role_id):
@ -270,13 +339,19 @@ class OSInheritProjectUserResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:create_grant',
build_target=functools.partial(_build_enforcement_target_attr,
project_id=project_id,
user_id=user_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
project_id=project_id,
user_id=user_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.create_grant(
project_id=project_id, user_id=user_id, role_id=role_id,
inherited_to_projects=True)
project_id=project_id,
user_id=user_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def delete(self, project_id, user_id, role_id):
@ -287,13 +362,19 @@ class OSInheritProjectUserResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:revoke_grant',
build_target=functools.partial(_build_enforcement_target_attr,
project_id=project_id,
user_id=user_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
project_id=project_id,
user_id=user_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.delete_grant(
project_id=project_id, user_id=user_id, role_id=role_id,
inherited_to_projects=True)
project_id=project_id,
user_id=user_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
@ -306,13 +387,19 @@ class OSInheritProjectGroupResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=functools.partial(_build_enforcement_target_attr,
project_id=project_id,
group_id=group_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
project_id=project_id,
group_id=group_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.get_grant(
project_id=project_id, group_id=group_id, role_id=role_id,
inherited_to_projects=True)
project_id=project_id,
group_id=group_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def put(self, project_id, group_id, role_id):
@ -323,13 +410,19 @@ class OSInheritProjectGroupResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:create_grant',
build_target=functools.partial(_build_enforcement_target_attr,
project_id=project_id,
group_id=group_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
project_id=project_id,
group_id=group_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.create_grant(
project_id=project_id, group_id=group_id, role_id=role_id,
inherited_to_projects=True)
project_id=project_id,
group_id=group_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
def delete(self, project_id, group_id, role_id):
@ -340,13 +433,19 @@ class OSInheritProjectGroupResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:revoke_grant',
build_target=functools.partial(_build_enforcement_target_attr,
project_id=project_id,
group_id=group_id,
role_id=role_id))
build_target=functools.partial(
_build_enforcement_target_attr,
project_id=project_id,
group_id=group_id,
role_id=role_id,
),
)
PROVIDERS.assignment_api.delete_grant(
project_id=project_id, group_id=group_id, role_id=role_id,
inherited_to_projects=True)
project_id=project_id,
group_id=group_id,
role_id=role_id,
inherited_to_projects=True,
)
return None, http.client.NO_CONTENT
@ -358,68 +457,92 @@ class OSInheritAPI(ks_flask.APIBase):
resource_mapping = [
ks_flask.construct_resource_map(
resource=OSInheritDomainGroupRolesResource,
url=('/domains/<string:domain_id>/groups/<string:group_id>/roles'
'/<string:role_id>/inherited_to_projects'),
url=(
'/domains/<string:domain_id>/groups/<string:group_id>/roles'
'/<string:role_id>/inherited_to_projects'
),
resource_kwargs={},
rel='domain_group_role_inherited_to_projects',
resource_relation_func=_build_resource_relation,
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'group_id': json_home.Parameters.GROUP_ID,
'role_id': json_home.Parameters.ROLE_ID}),
'role_id': json_home.Parameters.ROLE_ID,
},
),
ks_flask.construct_resource_map(
resource=OSInheritDomainGroupRolesListResource,
url=('/domains/<string:domain_id>/groups/<string:group_id>/roles'
'/inherited_to_projects'),
url=(
'/domains/<string:domain_id>/groups/<string:group_id>/roles'
'/inherited_to_projects'
),
resource_kwargs={},
rel='domain_group_roles_inherited_to_projects',
resource_relation_func=_build_resource_relation,
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'group_id': json_home.Parameters.GROUP_ID}),
'group_id': json_home.Parameters.GROUP_ID,
},
),
ks_flask.construct_resource_map(
resource=OSInheritDomainUserRolesResource,
url=('/domains/<string:domain_id>/users/<string:user_id>/roles'
'/<string:role_id>/inherited_to_projects'),
url=(
'/domains/<string:domain_id>/users/<string:user_id>/roles'
'/<string:role_id>/inherited_to_projects'
),
resource_kwargs={},
rel='domain_user_role_inherited_to_projects',
resource_relation_func=_build_resource_relation,
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'user_id': json_home.Parameters.USER_ID,
'role_id': json_home.Parameters.ROLE_ID}),
'role_id': json_home.Parameters.ROLE_ID,
},
),
ks_flask.construct_resource_map(
resource=OSInheritDomainUserRolesListResource,
url=('/domains/<string:domain_id>/users/<string:user_id>/roles'
'/inherited_to_projects'),
url=(
'/domains/<string:domain_id>/users/<string:user_id>/roles'
'/inherited_to_projects'
),
resource_kwargs={},
rel='domain_user_roles_inherited_to_projects',
resource_relation_func=_build_resource_relation,
path_vars={
'domain_id': json_home.Parameters.DOMAIN_ID,
'user_id': json_home.Parameters.USER_ID}),
'user_id': json_home.Parameters.USER_ID,
},
),
ks_flask.construct_resource_map(
resource=OSInheritProjectUserResource,
url=('projects/<string:project_id>/users/<string:user_id>/roles'
'/<string:role_id>/inherited_to_projects'),
url=(
'projects/<string:project_id>/users/<string:user_id>/roles'
'/<string:role_id>/inherited_to_projects'
),
resource_kwargs={},
rel='project_user_role_inherited_to_projects',
resource_relation_func=_build_resource_relation,
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'user_id': json_home.Parameters.USER_ID,
'role_id': json_home.Parameters.ROLE_ID}),
'role_id': json_home.Parameters.ROLE_ID,
},
),
ks_flask.construct_resource_map(
resource=OSInheritProjectGroupResource,
url=('projects/<string:project_id>/groups/<string:group_id>/roles'
'/<string:role_id>/inherited_to_projects'),
url=(
'projects/<string:project_id>/groups/<string:group_id>/roles'
'/<string:role_id>/inherited_to_projects'
),
resource_kwargs={},
rel='project_group_role_inherited_to_projects',
resource_relation_func=_build_resource_relation,
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'group_id': json_home.Parameters.GROUP_ID,
'role_id': json_home.Parameters.ROLE_ID})
'role_id': json_home.Parameters.ROLE_ID,
},
),
]

View File

@ -46,7 +46,8 @@ _build_resource_relation = json_home_relations.os_oauth1_resource_rel_func
_build_parameter_relation = json_home_relations.os_oauth1_parameter_rel_func
_ACCESS_TOKEN_ID_PARAMETER_RELATION = _build_parameter_relation(
parameter_name='access_token_id')
parameter_name='access_token_id'
)
def _normalize_role_list(authorize_roles):
@ -55,8 +56,9 @@ def _normalize_role_list(authorize_roles):
if role.get('id'):
roles.add(role['id'])
else:
roles.add(PROVIDERS.role_api.get_unique_role_by_name(
role['name'])['id'])
roles.add(
PROVIDERS.role_api.get_unique_role_by_name(role['name'])['id']
)
return roles
@ -102,12 +104,14 @@ class ConsumerResource(ks_flask.ResourceBase):
def post(self):
ENFORCER.enforce_call(action='identity:create_consumer')
consumer = (flask.request.get_json(force=True, silent=True) or {}).get(
'consumer', {})
'consumer', {}
)
consumer = self._normalize_dict(consumer)
validation.lazy_validate(schema.consumer_create, consumer)
consumer = self._assign_unique_id(consumer)
ref = PROVIDERS.oauth_api.create_consumer(
consumer, initiator=self.audit_initiator)
consumer, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def delete(self, consumer_id):
@ -116,23 +120,25 @@ class ConsumerResource(ks_flask.ResourceBase):
'Invalidating token cache because consumer %(consumer_id)s has '
'been deleted. Authorization for users with OAuth tokens will be '
'recalculated and enforced accordingly the next time they '
'authenticate or validate a token.' %
{'consumer_id': consumer_id}
'authenticate or validate a token.' % {'consumer_id': consumer_id}
)
notifications.invalidate_token_cache_notification(reason)
PROVIDERS.oauth_api.delete_consumer(
consumer_id, initiator=self.audit_initiator)
consumer_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
def patch(self, consumer_id):
ENFORCER.enforce_call(action='identity:update_consumer')
consumer = (flask.request.get_json(force=True, silent=True) or {}).get(
'consumer', {})
'consumer', {}
)
validation.lazy_validate(schema.consumer_update, consumer)
consumer = self._normalize_dict(consumer)
self._require_matching_id(consumer)
ref = PROVIDERS.oauth_api.update_consumer(
consumer_id, consumer, initiator=self.audit_initiator)
consumer_id, consumer, initiator=self.audit_initiator
)
return self.wrap_member(ref)
@ -142,14 +148,17 @@ class RequestTokenResource(_OAuth1ResourceBase):
oauth_headers = oauth1.get_oauth_headers(flask.request.headers)
consumer_id = oauth_headers.get('oauth_consumer_key')
requested_project_id = flask.request.headers.get(
'Requested-Project-Id')
'Requested-Project-Id'
)
if not consumer_id:
raise exception.ValidationError(
attribute='oauth_consumer_key', target='request')
attribute='oauth_consumer_key', target='request'
)
if not requested_project_id:
raise exception.ValidationError(
attribute='Requested-Project-Id', target='request')
attribute='Requested-Project-Id', target='request'
)
# NOTE(stevemar): Ensure consumer and requested project exist
PROVIDERS.resource_api.get_project(requested_project_id)
@ -160,10 +169,14 @@ class RequestTokenResource(_OAuth1ResourceBase):
req_headers.update(flask.request.headers)
request_verifier = oauth1.RequestTokenEndpoint(
request_validator=validator.OAuthValidator(),
token_generator=oauth1.token_generator)
token_generator=oauth1.token_generator,
)
h, b, s = request_verifier.create_request_token_response(
url, http_method='POST', body=flask.request.args,
headers=req_headers)
url,
http_method='POST',
body=flask.request.args,
headers=req_headers,
)
if not b:
msg = _('Invalid signature')
raise exception.Unauthorized(message=msg)
@ -174,11 +187,13 @@ class RequestTokenResource(_OAuth1ResourceBase):
consumer_id,
requested_project_id,
request_token_duration,
initiator=notifications.build_audit_initiator())
initiator=notifications.build_audit_initiator(),
)
result = ('oauth_token=%(key)s&oauth_token_secret=%(secret)s'
% {'key': token_ref['id'],
'secret': token_ref['request_secret']})
result = 'oauth_token=%(key)s&oauth_token_secret=%(secret)s' % {
'key': token_ref['id'],
'secret': token_ref['request_secret'],
}
if CONF.oauth1.request_token_duration > 0:
expiry_bit = '&oauth_expires_at=%s' % token_ref['expires_at']
@ -199,35 +214,40 @@ class AccessTokenResource(_OAuth1ResourceBase):
if not consumer_id:
raise exception.ValidationError(
attribute='oauth_consumer_key', target='request')
attribute='oauth_consumer_key', target='request'
)
if not request_token_id:
raise exception.ValidationError(
attribute='oauth_token', target='request')
attribute='oauth_token', target='request'
)
if not oauth_verifier:
raise exception.ValidationError(
attribute='oauth_verifier', target='request')
attribute='oauth_verifier', target='request'
)
req_token = PROVIDERS.oauth_api.get_request_token(
request_token_id)
req_token = PROVIDERS.oauth_api.get_request_token(request_token_id)
expires_at = req_token['expires_at']
if expires_at:
now = timeutils.utcnow()
expires = timeutils.normalize_time(
timeutils.parse_isotime(expires_at))
timeutils.parse_isotime(expires_at)
)
if now > expires:
raise exception.Unauthorized(_('Request token is expired'))
url = _update_url_scheme()
access_verifier = oauth1.AccessTokenEndpoint(
request_validator=validator.OAuthValidator(),
token_generator=oauth1.token_generator)
token_generator=oauth1.token_generator,
)
try:
h, b, s = access_verifier.create_access_token_response(
url,
http_method='POST',
body=flask.request.args,
headers=dict(flask.request.headers))
headers=dict(flask.request.headers),
)
except NotImplementedError:
# Client key or request token validation failed, since keystone
# does not yet support dummy client or dummy request token,
@ -239,10 +259,14 @@ class AccessTokenResource(_OAuth1ResourceBase):
LOG.warning('Provided consumer does not exist.')
raise exception.Unauthorized(message=msg)
if req_token['consumer_id'] != consumer_id:
msg = ('Provided consumer key does not match stored consumer '
'key.')
tr_msg = _('Provided consumer key does not match stored '
'consumer key.')
msg = (
'Provided consumer key does not match stored consumer '
'key.'
)
tr_msg = _(
'Provided consumer key does not match stored '
'consumer key.'
)
LOG.warning(msg)
raise exception.Unauthorized(message=tr_msg)
# The response body is empty since either one of the following reasons
@ -266,11 +290,13 @@ class AccessTokenResource(_OAuth1ResourceBase):
token_ref = PROVIDERS.oauth_api.create_access_token(
request_token_id,
access_token_duration,
initiator=notifications.build_audit_initiator())
initiator=notifications.build_audit_initiator(),
)
result = ('oauth_token=%(key)s&oauth_token_secret=%(secret)s'
% {'key': token_ref['id'],
'secret': token_ref['access_secret']})
result = 'oauth_token=%(key)s&oauth_token_secret=%(secret)s' % {
'key': token_ref['id'],
'secret': token_ref['access_secret'],
}
if CONF.oauth1.access_token_duration > 0:
expiry_bit = '&oauth_expires_at=%s' % (token_ref['expires_at'])
@ -285,13 +311,17 @@ class AuthorizeResource(_OAuth1ResourceBase):
def put(self, request_token_id):
ENFORCER.enforce_call(action='identity:authorize_request_token')
roles = (flask.request.get_json(force=True, silent=True) or {}).get(
'roles', [])
'roles', []
)
validation.lazy_validate(schema.request_token_authorize, roles)
ctx = flask.request.environ[context.REQUEST_CONTEXT_ENV]
if ctx.is_delegated_auth:
raise exception.Forbidden(
_('Cannot authorize a request token with a token issued via '
'delegation.'))
_(
'Cannot authorize a request token with a token issued via '
'delegation.'
)
)
req_token = PROVIDERS.oauth_api.get_request_token(request_token_id)
@ -299,7 +329,8 @@ class AuthorizeResource(_OAuth1ResourceBase):
if expires_at:
now = timeutils.utcnow()
expires = timeutils.normalize_time(
timeutils.parse_isotime(expires_at))
timeutils.parse_isotime(expires_at)
)
if now > expires:
raise exception.Unauthorized(_('Request token is expired'))
@ -308,7 +339,8 @@ class AuthorizeResource(_OAuth1ResourceBase):
# verify the authorizing user has the roles
try:
auth_context = flask.request.environ[
authorization.AUTH_CONTEXT_ENV]
authorization.AUTH_CONTEXT_ENV
]
user_token_ref = auth_context['token']
except KeyError:
LOG.warning("Couldn't find the auth context.")
@ -317,7 +349,8 @@ class AuthorizeResource(_OAuth1ResourceBase):
user_id = user_token_ref.user_id
project_id = req_token['requested_project_id']
user_roles = PROVIDERS.assignment_api.get_roles_for_user_and_project(
user_id, project_id)
user_id, project_id
)
cred_set = set(user_roles)
if not cred_set.issuperset(authed_roles):
@ -329,7 +362,8 @@ class AuthorizeResource(_OAuth1ResourceBase):
# finally authorize the token
authed_token = PROVIDERS.oauth_api.authorize_request_token(
request_token_id, user_id, role_ids)
request_token_id, user_id, role_ids
)
to_return = {'token': {'oauth_verifier': authed_token['verifier']}}
return to_return
@ -346,14 +380,14 @@ class OSAuth1API(ks_flask.APIBase):
url='/request_token',
resource_kwargs={},
rel='request_tokens',
resource_relation_func=_build_resource_relation
resource_relation_func=_build_resource_relation,
),
ks_flask.construct_resource_map(
resource=AccessTokenResource,
url='/access_token',
rel='access_tokens',
resource_kwargs={},
resource_relation_func=_build_resource_relation
resource_relation_func=_build_resource_relation,
),
ks_flask.construct_resource_map(
resource=AuthorizeResource,
@ -363,8 +397,11 @@ class OSAuth1API(ks_flask.APIBase):
resource_relation_func=_build_resource_relation,
path_vars={
'request_token_id': _build_parameter_relation(
parameter_name='request_token_id')
})]
parameter_name='request_token_id'
)
},
),
]
APIs = (OSAuth1API,)

View File

@ -42,7 +42,8 @@ class AccessTokenResource(ks_flask.ResourceBase):
raise exception.OAuth2OtherError(
int(http.client.METHOD_NOT_ALLOWED),
http.client.responses[http.client.METHOD_NOT_ALLOWED],
_('The method is not allowed for the requested URL.'))
_('The method is not allowed for the requested URL.'),
)
@ks_flask.unenforced_api
def get(self):
@ -80,18 +81,22 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidRequest(
int(http.client.BAD_REQUEST),
http.client.responses[http.client.BAD_REQUEST],
_('The parameter grant_type is required.'))
LOG.info('Get OAuth2.0 Access Token API: '
f'{error.message_format}')
_('The parameter grant_type is required.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: ' f'{error.message_format}'
)
raise error
if grant_type != 'client_credentials':
error = exception.OAuth2UnsupportedGrantType(
int(http.client.BAD_REQUEST),
http.client.responses[http.client.BAD_REQUEST],
_('The parameter grant_type %s is not supported.'
) % grant_type)
LOG.info('Get OAuth2.0 Access Token API: '
f'{error.message_format}')
_('The parameter grant_type %s is not supported.')
% grant_type,
)
LOG.info(
'Get OAuth2.0 Access Token API: ' f'{error.message_format}'
)
raise error
auth_method = ''
@ -107,9 +112,12 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'failed to get a client_id from the request.')
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'failed to get a client_id from the request.'
)
raise error
if client_cert:
auth_method = 'tls_client_auth'
@ -125,9 +133,12 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'failed to get client credentials from the request.')
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'failed to get client credentials from the request.'
)
raise error
def _client_secret_basic(self, client_id, client_secret):
@ -137,8 +148,8 @@ class AccessTokenResource(ks_flask.ResourceBase):
'methods': ['application_credential'],
'application_credential': {
'id': client_id,
'secret': client_secret
}
'secret': client_secret,
},
}
}
try:
@ -146,32 +157,37 @@ class AccessTokenResource(ks_flask.ResourceBase):
except exception.Error as error:
if error.code == 401:
error = exception.OAuth2InvalidClient(
error.code, error.title,
str(error))
error.code, error.title, str(error)
)
elif error.code == 400:
error = exception.OAuth2InvalidRequest(
error.code, error.title,
str(error))
error.code, error.title, str(error)
)
else:
error = exception.OAuth2OtherError(
error.code, error.title,
error.code,
error.title,
'An unknown error occurred and failed to get an OAuth2.0 '
'access token.')
'access token.',
)
LOG.exception(error)
raise error
except Exception as error:
error = exception.OAuth2OtherError(
int(http.client.INTERNAL_SERVER_ERROR),
http.client.responses[http.client.INTERNAL_SERVER_ERROR],
str(error))
str(error),
)
LOG.exception(error)
raise error
resp = make_response({
'access_token': token.id,
'token_type': 'Bearer',
'expires_in': CONF.token.expiration
})
resp = make_response(
{
'access_token': token.id,
'token_type': 'Bearer',
'expires_in': CONF.token.expiration,
}
)
resp.status = '200 OK'
return resp
@ -183,14 +199,18 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'mapping id %s is not found. ',
mapping_id)
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'mapping id %s is not found. ',
mapping_id,
)
raise error
rule_processor = federation_utils.RuleProcessor(
mapping.get('id'), mapping.get('rules'))
mapping.get('id'), mapping.get('rules')
)
try:
mapped_properties = rule_processor.process(cert_dn)
except exception.Error as error:
@ -198,24 +218,32 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'mapping rule process failed. '
'mapping_id: %s, rules: %s, data: %s.',
mapping_id, mapping.get('rules'),
jsonutils.dumps(cert_dn))
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'mapping rule process failed. '
'mapping_id: %s, rules: %s, data: %s.',
mapping_id,
mapping.get('rules'),
jsonutils.dumps(cert_dn),
)
raise error
except Exception as error:
LOG.exception(error)
error = exception.OAuth2OtherError(
int(http.client.INTERNAL_SERVER_ERROR),
http.client.responses[http.client.INTERNAL_SERVER_ERROR],
str(error))
LOG.info('Get OAuth2.0 Access Token API: '
'mapping rule process failed. '
'mapping_id: %s, rules: %s, data: %s.',
mapping_id, mapping.get('rules'),
jsonutils.dumps(cert_dn))
str(error),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'mapping rule process failed. '
'mapping_id: %s, rules: %s, data: %s.',
mapping_id,
mapping.get('rules'),
jsonutils.dumps(cert_dn),
)
raise error
mapping_user = mapped_properties.get('user', {})
@ -229,50 +257,77 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user name', mapping_user_name, user.get('name'))
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user name',
mapping_user_name,
user.get('name'),
)
raise error
if mapping_user_id and mapping_user_id != user.get('id'):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user id', mapping_user_id, user.get('id'))
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user id',
mapping_user_id,
user.get('id'),
)
raise error
if mapping_user_email and mapping_user_email != user.get('email'):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user email', mapping_user_email, user.get('email'))
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user email',
mapping_user_email,
user.get('email'),
)
raise error
if (mapping_user_domain_id and
mapping_user_domain_id != user_domain.get('id')):
if (
mapping_user_domain_id
and mapping_user_domain_id != user_domain.get('id')
):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user domain id', mapping_user_domain_id,
user_domain.get('id'))
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user domain id',
mapping_user_domain_id,
user_domain.get('id'),
)
raise error
if (mapping_user_domain_name and
mapping_user_domain_name != user_domain.get('name')):
if (
mapping_user_domain_name
and mapping_user_domain_name != user_domain.get('name')
):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user domain name', mapping_user_domain_name,
user_domain.get('name'))
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: %s check failed. '
'DN value: %s, DB value: %s.',
'user domain name',
mapping_user_domain_name,
user_domain.get('name'),
)
raise error
def _tls_client_auth(self, client_id, client_cert):
@ -283,9 +338,12 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'failed to get the subject DN from the certificate.')
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'failed to get the subject DN from the certificate.'
)
raise error
try:
cert_issuer_dn = utils.get_certificate_issuer_dn(client_cert)
@ -293,17 +351,22 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'failed to get the issuer DN from the certificate.')
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'failed to get the issuer DN from the certificate.'
)
raise error
client_cert_dn = {}
for key in cert_subject_dn:
client_cert_dn['SSL_CLIENT_SUBJECT_DN_%s' %
key.upper()] = cert_subject_dn.get(key)
client_cert_dn['SSL_CLIENT_SUBJECT_DN_%s' % key.upper()] = (
cert_subject_dn.get(key)
)
for key in cert_issuer_dn:
client_cert_dn['SSL_CLIENT_ISSUER_DN_%s' %
key.upper()] = cert_issuer_dn.get(key)
client_cert_dn['SSL_CLIENT_ISSUER_DN_%s' % key.upper()] = (
cert_issuer_dn.get(key)
)
try:
user = PROVIDERS.identity_api.get_user(client_id)
@ -311,24 +374,29 @@ class AccessTokenResource(ks_flask.ResourceBase):
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'the user does not exist. user id: %s.',
client_id)
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'the user does not exist. user id: %s.',
client_id,
)
raise error
project_id = user.get('default_project_id')
if not project_id:
error = exception.OAuth2InvalidClient(
int(http.client.UNAUTHORIZED),
http.client.responses[http.client.UNAUTHORIZED],
_('Client authentication failed.'))
LOG.info('Get OAuth2.0 Access Token API: '
'the user does not have default project. user id: %s.',
client_id)
_('Client authentication failed.'),
)
LOG.info(
'Get OAuth2.0 Access Token API: '
'the user does not have default project. user id: %s.',
client_id,
)
raise error
user_domain = PROVIDERS.resource_api.get_domain(
user.get('domain_id'))
user_domain = PROVIDERS.resource_api.get_domain(user.get('domain_id'))
self._check_mapped_properties(client_cert_dn, user, user_domain)
thumbprint = utils.get_certificate_thumbprint(client_cert)
LOG.debug(f'The mTLS certificate thumbprint: {thumbprint}')
@ -337,37 +405,42 @@ class AccessTokenResource(ks_flask.ResourceBase):
user_id=client_id,
method_names=['oauth2_credential'],
project_id=project_id,
thumbprint=thumbprint
thumbprint=thumbprint,
)
except exception.Error as error:
if error.code == 401:
error = exception.OAuth2InvalidClient(
error.code, error.title,
str(error))
error.code, error.title, str(error)
)
elif error.code == 400:
error = exception.OAuth2InvalidRequest(
error.code, error.title,
str(error))
error.code, error.title, str(error)
)
else:
error = exception.OAuth2OtherError(
error.code, error.title,
error.code,
error.title,
'An unknown error occurred and failed to get an OAuth2.0 '
'access token.')
'access token.',
)
LOG.exception(error)
raise error
except Exception as error:
error = exception.OAuth2OtherError(
int(http.client.INTERNAL_SERVER_ERROR),
http.client.responses[http.client.INTERNAL_SERVER_ERROR],
str(error))
str(error),
)
LOG.exception(error)
raise error
resp = make_response({
'access_token': token.id,
'token_type': 'Bearer',
'expires_in': CONF.token.expiration
})
resp = make_response(
{
'access_token': token.id,
'token_type': 'Bearer',
'expires_in': CONF.token.expiration,
}
)
resp.status = '200 OK'
return resp
@ -383,8 +456,9 @@ class OSAuth2API(ks_flask.APIBase):
url='/token',
rel='token',
resource_kwargs={},
resource_relation_func=_build_resource_relation
)]
resource_relation_func=_build_resource_relation,
)
]
APIs = (OSAuth2API,)

View File

@ -39,10 +39,12 @@ class OSRevokeResource(flask_restful.Resource):
if since:
try:
last_fetch = timeutils.normalize_time(
timeutils.parse_isotime(since))
timeutils.parse_isotime(since)
)
except ValueError:
raise exception.ValidationError(
message=_('invalidate date format %s') % since)
message=_('invalidate date format %s') % since
)
# FIXME(notmorgan): The revocation events cannot have resource options
# added to them or lazy-loaded relationships as long as to_dict
# is called outside of an active session context. This API is unused
@ -51,12 +53,14 @@ class OSRevokeResource(flask_restful.Resource):
# events themselves.
events = PROVIDERS.revoke_api.list_events(last_fetch=last_fetch)
# Build the links by hand as the standard controller calls require ids
response = {'events': [event.to_dict() for event in events],
'links': {
'next': None,
'self': ks_flask.base_url(path='/OS-REVOKE/events'),
'previous': None}
}
response = {
'events': [event.to_dict() for event in events],
'links': {
'next': None,
'self': ks_flask.base_url(path='/OS-REVOKE/events'),
'previous': None,
},
}
return response
@ -71,7 +75,7 @@ class OSRevokeAPI(ks_flask.APIBase):
url='/events',
resource_kwargs={},
rel='events',
resource_relation_func=_build_resource_relation
resource_relation_func=_build_resource_relation,
)
]

View File

@ -31,16 +31,22 @@ class SimpleCertCAResource(flask_restful.Resource):
@ks_flask.unenforced_api
def get(self):
raise exception.Gone(
message=_('This API is no longer available due to the removal '
'of support for PKI tokens.'))
message=_(
'This API is no longer available due to the removal '
'of support for PKI tokens.'
)
)
class SimpleCertListResource(flask_restful.Resource):
@ks_flask.unenforced_api
def get(self):
raise exception.Gone(
message=_('This API is no longer available due to the removal '
'of support for PKI tokens.'))
message=_(
'This API is no longer available due to the removal '
'of support for PKI tokens.'
)
)
class SimpleCertAPI(ks_flask.APIBase):
@ -53,13 +59,15 @@ class SimpleCertAPI(ks_flask.APIBase):
url='/OS-SIMPLE-CERT/ca',
resource_kwargs={},
rel='ca_certificate',
resource_relation_func=_build_resource_relation),
resource_relation_func=_build_resource_relation,
),
ks_flask.construct_resource_map(
resource=SimpleCertListResource,
url='/OS-SIMPLE-CERT/certificates',
resource_kwargs={},
rel='certificates',
resource_relation_func=_build_resource_relation),
resource_relation_func=_build_resource_relation,
),
]

View File

@ -41,7 +41,7 @@ class PolicyResource(ks_flask.ResourceBase):
@versionutils.deprecated(
as_of=versionutils.deprecated.QUEENS,
what='identity:get_policy of the v3 Policy APIs'
what='identity:get_policy of the v3 Policy APIs',
)
def _get_policy(self, policy_id):
ENFORCER.enforce_call(action='identity:get_policy')
@ -50,7 +50,7 @@ class PolicyResource(ks_flask.ResourceBase):
@versionutils.deprecated(
as_of=versionutils.deprecated.QUEENS,
what='identity:list_policies of the v3 Policy APIs'
what='identity:list_policies of the v3 Policy APIs',
)
def _list_policies(self):
ENFORCER.enforce_call(action='identity:list_policies')
@ -61,7 +61,7 @@ class PolicyResource(ks_flask.ResourceBase):
@versionutils.deprecated(
as_of=versionutils.deprecated.QUEENS,
what='identity:create_policy of the v3 Policy APIs'
what='identity:create_policy of the v3 Policy APIs',
)
def post(self):
ENFORCER.enforce_call(action='identity:create_policy')
@ -76,7 +76,7 @@ class PolicyResource(ks_flask.ResourceBase):
@versionutils.deprecated(
as_of=versionutils.deprecated.QUEENS,
what='identity:update_policy of the v3 Policy APIs'
what='identity:update_policy of the v3 Policy APIs',
)
def patch(self, policy_id):
ENFORCER.enforce_call(action='identity:update_policy')
@ -90,7 +90,7 @@ class PolicyResource(ks_flask.ResourceBase):
@versionutils.deprecated(
as_of=versionutils.deprecated.QUEENS,
what='identity:delete_policy of the v3 Policy APIs'
what='identity:delete_policy of the v3 Policy APIs',
)
def delete(self, policy_id):
ENFORCER.enforce_call(action='identity:delete_policy')
@ -231,45 +231,51 @@ class PolicyAPI(ks_flask.APIBase):
resource_kwargs={},
rel='policy_endpoints',
path_vars={'policy_id': json_home.Parameters.POLICY_ID},
resource_relation_func=_resource_rel_func
resource_relation_func=_resource_rel_func,
),
ks_flask.construct_resource_map(
resource=EndpointPolicyAssociations,
url=('/policies/<string:policy_id>/OS-ENDPOINT-POLICY/'
'endpoints/<string:endpoint_id>'),
url=(
'/policies/<string:policy_id>/OS-ENDPOINT-POLICY/'
'endpoints/<string:endpoint_id>'
),
resource_kwargs={},
rel='endpoint_policy_association',
path_vars={
'policy_id': json_home.Parameters.POLICY_ID,
'endpoint_id': json_home.Parameters.ENDPOINT_ID
'endpoint_id': json_home.Parameters.ENDPOINT_ID,
},
resource_relation_func=_resource_rel_func
resource_relation_func=_resource_rel_func,
),
ks_flask.construct_resource_map(
resource=ServicePolicyAssociations,
url=('/policies/<string:policy_id>/OS-ENDPOINT-POLICY/'
'services/<string:service_id>'),
url=(
'/policies/<string:policy_id>/OS-ENDPOINT-POLICY/'
'services/<string:service_id>'
),
resource_kwargs={},
rel='service_policy_association',
path_vars={
'policy_id': json_home.Parameters.POLICY_ID,
'service_id': json_home.Parameters.SERVICE_ID
'service_id': json_home.Parameters.SERVICE_ID,
},
resource_relation_func=_resource_rel_func
resource_relation_func=_resource_rel_func,
),
ks_flask.construct_resource_map(
resource=ServiceRegionPolicyAssociations,
url=('/policies/<string:policy_id>/OS-ENDPOINT-POLICY/'
'services/<string:service_id>/regions/<string:region_id>'),
url=(
'/policies/<string:policy_id>/OS-ENDPOINT-POLICY/'
'services/<string:service_id>/regions/<string:region_id>'
),
resource_kwargs={},
rel='region_and_service_policy_association',
path_vars={
'policy_id': json_home.Parameters.POLICY_ID,
'service_id': json_home.Parameters.SERVICE_ID,
'region_id': json_home.Parameters.REGION_ID
'region_id': json_home.Parameters.REGION_ID,
},
resource_relation_func=_resource_rel_func
)
resource_relation_func=_resource_rel_func,
),
]

View File

@ -50,7 +50,8 @@ class ProjectResource(ks_flask.ResourceBase):
collection_key = 'projects'
member_key = 'project'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='resource_api', method='get_project')
api='resource_api', method='get_project'
)
def _expand_project_ref(self, ref):
parents_as_list = self.query_filter_is_true('parents_as_list')
@ -63,21 +64,25 @@ class ProjectResource(ks_flask.ResourceBase):
# parents_as_list and parents_as_ids are mutually exclusive
if parents_as_list and parents_as_ids:
msg = _('Cannot use parents_as_list and parents_as_ids query '
'params at the same time.')
msg = _(
'Cannot use parents_as_list and parents_as_ids query '
'params at the same time.'
)
raise exception.ValidationError(msg)
# subtree_as_list and subtree_as_ids are mutually exclusive
if subtree_as_list and subtree_as_ids:
msg = _('Cannot use subtree_as_list and subtree_as_ids query '
'params at the same time.')
msg = _(
'Cannot use subtree_as_list and subtree_as_ids query '
'params at the same time.'
)
raise exception.ValidationError(msg)
if parents_as_list:
parents = PROVIDERS.resource_api.list_project_parents(
ref['id'], self.oslo_context.user_id, include_limits)
ref['parents'] = [self.wrap_member(p)
for p in parents]
ref['id'], self.oslo_context.user_id, include_limits
)
ref['parents'] = [self.wrap_member(p) for p in parents]
elif parents_as_ids:
ref['parents'] = PROVIDERS.resource_api.get_project_parents_as_ids(
ref
@ -85,9 +90,9 @@ class ProjectResource(ks_flask.ResourceBase):
if subtree_as_list:
subtree = PROVIDERS.resource_api.list_projects_in_subtree(
ref['id'], self.oslo_context.user_id, include_limits)
ref['subtree'] = [self.wrap_member(p)
for p in subtree]
ref['id'], self.oslo_context.user_id, include_limits
)
ref['subtree'] = [self.wrap_member(p) for p in subtree]
elif subtree_as_ids:
ref['subtree'] = (
PROVIDERS.resource_api.get_projects_in_subtree_as_ids(
@ -102,7 +107,7 @@ class ProjectResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:get_project',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
project = PROVIDERS.resource_api.get_project(project_id)
self._expand_project_ref(project)
@ -117,9 +122,11 @@ class ProjectResource(ks_flask.ResourceBase):
target = None
if self.oslo_context.domain_id:
target = {'domain_id': self.oslo_context.domain_id}
ENFORCER.enforce_call(action='identity:list_projects',
filters=filters,
target_attr=target)
ENFORCER.enforce_call(
action='identity:list_projects',
filters=filters,
target_attr=target,
)
hints = self.build_driver_hints(filters)
# If 'is_domain' has not been included as a query, we default it to
@ -174,9 +181,8 @@ class ProjectResource(ks_flask.ResourceBase):
project = self._normalize_dict(project)
try:
ref = PROVIDERS.resource_api.create_project(
project['id'],
project,
initiator=self.audit_initiator)
project['id'], project, initiator=self.audit_initiator
)
except (exception.DomainNotFound, exception.ProjectNotFound) as e:
raise exception.ValidationError(e)
return self.wrap_member(ref), http.client.CREATED
@ -188,15 +194,14 @@ class ProjectResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:update_project',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
project = self.request_body_json.get('project', {})
validation.lazy_validate(schema.project_update, project)
self._require_matching_id(project)
ref = PROVIDERS.resource_api.update_project(
project_id,
project,
initiator=self.audit_initiator)
project_id, project, initiator=self.audit_initiator
)
return self.wrap_member(ref)
def delete(self, project_id):
@ -206,11 +211,11 @@ class ProjectResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:delete_project',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
PROVIDERS.resource_api.delete_project(
project_id,
initiator=self.audit_initiator)
project_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -218,7 +223,8 @@ class _ProjectTagResourceBase(ks_flask.ResourceBase):
collection_key = 'projects'
member_key = 'tags'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='resource_api', method='get_project_tag')
api='resource_api', method='get_project_tag'
)
@classmethod
def wrap_member(cls, ref, collection_name=None, member_name=None):
@ -226,7 +232,7 @@ class _ProjectTagResourceBase(ks_flask.ResourceBase):
# NOTE(gagehugo): Overriding this due to how the common controller
# expects the ref to have an id, which for tags it does not.
new_ref = {'links': {'self': ks_flask.full_url()}}
new_ref[member_name] = (ref or [])
new_ref[member_name] = ref or []
return new_ref
@ -238,7 +244,7 @@ class ProjectTagsResource(_ProjectTagResourceBase):
"""
ENFORCER.enforce_call(
action='identity:list_project_tags',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
ref = PROVIDERS.resource_api.list_project_tags(project_id)
return self.wrap_member(ref)
@ -250,12 +256,13 @@ class ProjectTagsResource(_ProjectTagResourceBase):
"""
ENFORCER.enforce_call(
action='identity:update_project_tags',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
tags = self.request_body_json.get('tags', {})
validation.lazy_validate(schema.project_tags_update, tags)
ref = PROVIDERS.resource_api.update_project_tags(
project_id, tags, initiator=self.audit_initiator)
project_id, tags, initiator=self.audit_initiator
)
return self.wrap_member(ref)
def delete(self, project_id):
@ -265,7 +272,7 @@ class ProjectTagsResource(_ProjectTagResourceBase):
"""
ENFORCER.enforce_call(
action='identity:delete_project_tags',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
PROVIDERS.resource_api.update_project_tags(project_id, [])
return None, http.client.NO_CONTENT
@ -291,7 +298,7 @@ class ProjectTagResource(_ProjectTagResourceBase):
"""
ENFORCER.enforce_call(
action='identity:create_project_tag',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
validation.lazy_validate(schema.project_tag_create, value)
# Check if we will exceed the max number of tags on this project
@ -299,9 +306,7 @@ class ProjectTagResource(_ProjectTagResourceBase):
tags.append(value)
validation.lazy_validate(schema.project_tags_update, tags)
PROVIDERS.resource_api.create_project_tag(
project_id,
value,
initiator=self.audit_initiator
project_id, value, initiator=self.audit_initiator
)
url = '/'.join((ks_flask.base_url(), project_id, 'tags', value))
response = flask.make_response('', http.client.CREATED)
@ -315,7 +320,7 @@ class ProjectTagResource(_ProjectTagResourceBase):
"""
ENFORCER.enforce_call(
action='identity:delete_project_tag',
build_target=_build_project_target_enforcement
build_target=_build_project_target_enforcement,
)
PROVIDERS.resource_api.delete_project_tag(project_id, value)
return None, http.client.NO_CONTENT
@ -325,17 +330,22 @@ class _ProjectGrantResourceBase(ks_flask.ResourceBase):
collection_key = 'roles'
member_key = 'role'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='role_api', method='get_role')
api='role_api', method='get_role'
)
@staticmethod
def _check_if_inherited():
return flask.request.path.endswith('/inherited_to_projects')
@staticmethod
def _build_enforcement_target_attr(role_id=None, user_id=None,
group_id=None, domain_id=None,
project_id=None,
allow_non_existing=False):
def _build_enforcement_target_attr(
role_id=None,
user_id=None,
group_id=None,
domain_id=None,
project_id=None,
allow_non_existing=False,
):
ref = {}
if role_id:
ref['role'] = PROVIDERS.role_api.get_role(role_id)
@ -368,13 +378,19 @@ class ProjectUserGrantResource(_ProjectGrantResourceBase):
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=functools.partial(
self._build_enforcement_target_attr, role_id=role_id,
project_id=project_id, user_id=user_id)
self._build_enforcement_target_attr,
role_id=role_id,
project_id=project_id,
user_id=user_id,
),
)
inherited = self._check_if_inherited()
PROVIDERS.assignment_api.get_grant(
role_id=role_id, user_id=user_id, project_id=project_id,
inherited_to_projects=inherited)
role_id=role_id,
user_id=user_id,
project_id=project_id,
inherited_to_projects=inherited,
)
return None, http.client.NO_CONTENT
def put(self, project_id, user_id, role_id):
@ -386,12 +402,19 @@ class ProjectUserGrantResource(_ProjectGrantResourceBase):
action='identity:create_grant',
build_target=functools.partial(
self._build_enforcement_target_attr,
role_id=role_id, project_id=project_id, user_id=user_id)
role_id=role_id,
project_id=project_id,
user_id=user_id,
),
)
inherited = self._check_if_inherited()
PROVIDERS.assignment_api.create_grant(
role_id=role_id, user_id=user_id, project_id=project_id,
inherited_to_projects=inherited, initiator=self.audit_initiator)
role_id=role_id,
user_id=user_id,
project_id=project_id,
inherited_to_projects=inherited,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
def delete(self, project_id, user_id, role_id):
@ -403,13 +426,20 @@ class ProjectUserGrantResource(_ProjectGrantResourceBase):
action='identity:revoke_grant',
build_target=functools.partial(
self._build_enforcement_target_attr,
role_id=role_id, user_id=user_id, project_id=project_id,
allow_non_existing=True)
role_id=role_id,
user_id=user_id,
project_id=project_id,
allow_non_existing=True,
),
)
inherited = self._check_if_inherited()
PROVIDERS.assignment_api.delete_grant(
role_id=role_id, user_id=user_id, project_id=project_id,
inherited_to_projects=inherited, initiator=self.audit_initiator)
role_id=role_id,
user_id=user_id,
project_id=project_id,
inherited_to_projects=inherited,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
@ -423,12 +453,16 @@ class ProjectUserListGrantResource(_ProjectGrantResourceBase):
action='identity:list_grants',
build_target=functools.partial(
self._build_enforcement_target_attr,
project_id=project_id, user_id=user_id)
project_id=project_id,
user_id=user_id,
),
)
inherited = self._check_if_inherited()
refs = PROVIDERS.assignment_api.list_grants(
user_id=user_id, project_id=project_id,
inherited_to_projects=inherited)
user_id=user_id,
project_id=project_id,
inherited_to_projects=inherited,
)
return self.wrap_collection(refs)
@ -441,13 +475,19 @@ class ProjectGroupGrantResource(_ProjectGrantResourceBase):
ENFORCER.enforce_call(
action='identity:check_grant',
build_target=functools.partial(
self._build_enforcement_target_attr, role_id=role_id,
project_id=project_id, group_id=group_id)
self._build_enforcement_target_attr,
role_id=role_id,
project_id=project_id,
group_id=group_id,
),
)
inherited = self._check_if_inherited()
PROVIDERS.assignment_api.get_grant(
role_id=role_id, group_id=group_id, project_id=project_id,
inherited_to_projects=inherited)
role_id=role_id,
group_id=group_id,
project_id=project_id,
inherited_to_projects=inherited,
)
return None, http.client.NO_CONTENT
def put(self, project_id, group_id, role_id):
@ -459,12 +499,19 @@ class ProjectGroupGrantResource(_ProjectGrantResourceBase):
action='identity:create_grant',
build_target=functools.partial(
self._build_enforcement_target_attr,
role_id=role_id, project_id=project_id, group_id=group_id)
role_id=role_id,
project_id=project_id,
group_id=group_id,
),
)
inherited = self._check_if_inherited()
PROVIDERS.assignment_api.create_grant(
role_id=role_id, group_id=group_id, project_id=project_id,
inherited_to_projects=inherited, initiator=self.audit_initiator)
role_id=role_id,
group_id=group_id,
project_id=project_id,
inherited_to_projects=inherited,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
def delete(self, project_id, group_id, role_id):
@ -476,13 +523,20 @@ class ProjectGroupGrantResource(_ProjectGrantResourceBase):
action='identity:revoke_grant',
build_target=functools.partial(
self._build_enforcement_target_attr,
role_id=role_id, group_id=group_id, project_id=project_id,
allow_non_existing=True)
role_id=role_id,
group_id=group_id,
project_id=project_id,
allow_non_existing=True,
),
)
inherited = self._check_if_inherited()
PROVIDERS.assignment_api.delete_grant(
role_id=role_id, group_id=group_id, project_id=project_id,
inherited_to_projects=inherited, initiator=self.audit_initiator)
role_id=role_id,
group_id=group_id,
project_id=project_id,
inherited_to_projects=inherited,
initiator=self.audit_initiator,
)
return None, http.client.NO_CONTENT
@ -496,12 +550,16 @@ class ProjectGroupListGrantResource(_ProjectGrantResourceBase):
action='identity:list_grants',
build_target=functools.partial(
self._build_enforcement_target_attr,
project_id=project_id, group_id=group_id)
project_id=project_id,
group_id=group_id,
),
)
inherited = self._check_if_inherited()
refs = PROVIDERS.assignment_api.list_grants(
group_id=group_id, project_id=project_id,
inherited_to_projects=inherited)
group_id=group_id,
project_id=project_id,
inherited_to_projects=inherited,
)
return self.wrap_collection(refs)
@ -515,8 +573,7 @@ class ProjectAPI(ks_flask.APIBase):
url='/projects/<string:project_id>/tags',
resource_kwargs={},
rel='project_tags',
path_vars={
'project_id': json_home.Parameters.PROJECT_ID}
path_vars={'project_id': json_home.Parameters.PROJECT_ID},
),
ks_flask.construct_resource_map(
resource=ProjectTagResource,
@ -525,18 +582,21 @@ class ProjectAPI(ks_flask.APIBase):
rel='project_tags',
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'value': json_home.Parameters.TAG_VALUE}
'value': json_home.Parameters.TAG_VALUE,
},
),
ks_flask.construct_resource_map(
resource=ProjectUserGrantResource,
url=('/projects/<string:project_id>/users/<string:user_id>/'
'roles/<string:role_id>'),
url=(
'/projects/<string:project_id>/users/<string:user_id>/'
'roles/<string:role_id>'
),
resource_kwargs={},
rel='project_user_role',
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'user_id': json_home.Parameters.USER_ID,
'role_id': json_home.Parameters.ROLE_ID
'role_id': json_home.Parameters.ROLE_ID,
},
),
ks_flask.construct_resource_map(
@ -546,19 +606,21 @@ class ProjectAPI(ks_flask.APIBase):
rel='project_user_roles',
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'user_id': json_home.Parameters.USER_ID
}
'user_id': json_home.Parameters.USER_ID,
},
),
ks_flask.construct_resource_map(
resource=ProjectGroupGrantResource,
url=('/projects/<string:project_id>/groups/<string:group_id>/'
'roles/<string:role_id>'),
url=(
'/projects/<string:project_id>/groups/<string:group_id>/'
'roles/<string:role_id>'
),
resource_kwargs={},
rel='project_group_role',
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'group_id': json_home.Parameters.GROUP_ID,
'role_id': json_home.Parameters.ROLE_ID
'role_id': json_home.Parameters.ROLE_ID,
},
),
ks_flask.construct_resource_map(
@ -568,7 +630,7 @@ class ProjectAPI(ks_flask.APIBase):
rel='project_group_roles',
path_vars={
'project_id': json_home.Parameters.PROJECT_ID,
'group_id': json_home.Parameters.GROUP_ID
'group_id': json_home.Parameters.GROUP_ID,
},
),
]

View File

@ -58,7 +58,8 @@ class RegionResource(ks_flask.ResourceBase):
# both ways.
region = self._assign_unique_id(region)
ref = PROVIDERS.catalog_api.create_region(
region, initiator=self.audit_initiator)
region, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def put(self, region_id):
@ -70,13 +71,16 @@ class RegionResource(ks_flask.ResourceBase):
region['id'] = region_id
elif region_id != region.get('id'):
raise exception.ValidationError(
_('Conflicting region IDs specified: '
'"%(url_id)s" != "%(ref_id)s"') % {
'url_id': region_id,
'ref_id': region['id']})
_(
'Conflicting region IDs specified: '
'"%(url_id)s" != "%(ref_id)s"'
)
% {'url_id': region_id, 'ref_id': region['id']}
)
ref = PROVIDERS.catalog_api.create_region(
region, initiator=self.audit_initiator)
region, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def patch(self, region_id):
@ -84,13 +88,20 @@ class RegionResource(ks_flask.ResourceBase):
region = self.request_body_json.get('region')
validation.lazy_validate(schema.region_update, region)
self._require_matching_id(region)
return self.wrap_member(PROVIDERS.catalog_api.update_region(
region_id, region, initiator=self.audit_initiator))
return self.wrap_member(
PROVIDERS.catalog_api.update_region(
region_id, region, initiator=self.audit_initiator
)
)
def delete(self, region_id):
ENFORCER.enforce_call(action='identity:delete_region')
return PROVIDERS.catalog_api.delete_region(
region_id, initiator=self.audit_initiator), http.client.NO_CONTENT
return (
PROVIDERS.catalog_api.delete_region(
region_id, initiator=self.audit_initiator
),
http.client.NO_CONTENT,
)
class RegionAPI(ks_flask.APIBase):

View File

@ -35,13 +35,15 @@ class RegisteredLimitResource(ks_flask.ResourceBase):
def _get_registered_limit(self, registered_limit_id):
ENFORCER.enforce_call(action='identity:get_registered_limit')
ref = PROVIDERS.unified_limit_api.get_registered_limit(
registered_limit_id)
registered_limit_id
)
return self.wrap_member(ref)
def _list_registered_limits(self):
filters = ['service_id', 'region_id', 'resource_name']
ENFORCER.enforce_call(action='identity:list_registered_limits',
filters=filters)
ENFORCER.enforce_call(
action='identity:list_registered_limits', filters=filters
)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.unified_limit_api.list_registered_limits(hints)
return self.wrap_collection(refs, hints=hints)
@ -53,32 +55,42 @@ class RegisteredLimitResource(ks_flask.ResourceBase):
def post(self):
ENFORCER.enforce_call(action='identity:create_registered_limits')
reg_limits = (flask.request.get_json(
silent=True, force=True) or {}).get('registered_limits', {})
reg_limits = (
flask.request.get_json(silent=True, force=True) or {}
).get('registered_limits', {})
validation.lazy_validate(schema.registered_limit_create, reg_limits)
registered_limits = [self._assign_unique_id(self._normalize_dict(r))
for r in reg_limits]
registered_limits = [
self._assign_unique_id(self._normalize_dict(r)) for r in reg_limits
]
refs = PROVIDERS.unified_limit_api.create_registered_limits(
registered_limits)
registered_limits
)
refs = self.wrap_collection(refs)
refs.pop('links')
return refs, http.client.CREATED
def patch(self, registered_limit_id):
ENFORCER.enforce_call(action='identity:update_registered_limit')
registered_limit = (flask.request.get_json(
silent=True, force=True) or {}).get('registered_limit', {})
validation.lazy_validate(schema.registered_limit_update,
registered_limit)
registered_limit = (
flask.request.get_json(silent=True, force=True) or {}
).get('registered_limit', {})
validation.lazy_validate(
schema.registered_limit_update, registered_limit
)
self._require_matching_id(registered_limit)
ref = PROVIDERS.unified_limit_api.update_registered_limit(
registered_limit_id, registered_limit)
registered_limit_id, registered_limit
)
return self.wrap_member(ref)
def delete(self, registered_limit_id):
ENFORCER.enforce_call(action='identity:delete_registered_limit')
return (PROVIDERS.unified_limit_api.delete_registered_limit(
registered_limit_id), http.client.NO_CONTENT)
return (
PROVIDERS.unified_limit_api.delete_registered_limit(
registered_limit_id
),
http.client.NO_CONTENT,
)
class RegisteredLimitsAPI(ks_flask.APIBase):

View File

@ -46,8 +46,13 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
def _list_role_assignments(self):
filters = [
'group.id', 'role.id', 'scope.domain.id', 'scope.project.id',
'scope.OS-INHERIT:inherited_to', 'user.id', 'scope.system'
'group.id',
'role.id',
'scope.domain.id',
'scope.project.id',
'scope.OS-INHERIT:inherited_to',
'user.id',
'scope.system',
]
target = None
if self.oslo_context.domain_id:
@ -58,9 +63,11 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
# so we reflect the domain_id from the context into the target
# to validate domain-scoped tokens.
target = {'domain_id': self.oslo_context.domain_id}
ENFORCER.enforce_call(action='identity:list_role_assignments',
filters=filters,
target_attr=target)
ENFORCER.enforce_call(
action='identity:list_role_assignments',
filters=filters,
target_attr=target,
)
assignments = self._build_role_assignments_list()
@ -83,8 +90,12 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
def _list_role_assignments_for_tree(self):
filters = [
'group.id', 'role.id', 'scope.domain.id', 'scope.project.id',
'scope.OS-INHERIT:inherited_to', 'user.id'
'group.id',
'role.id',
'scope.domain.id',
'scope.project.id',
'scope.OS-INHERIT:inherited_to',
'user.id',
]
project_id = flask.request.args.get('scope.project.id')
target = None
@ -95,11 +106,16 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
# Add target.domain_id to validate domain-scoped tokens
target['domain_id'] = target['project']['domain_id']
ENFORCER.enforce_call(action='identity:list_role_assignments_for_tree',
filters=filters, target_attr=target)
ENFORCER.enforce_call(
action='identity:list_role_assignments_for_tree',
filters=filters,
target_attr=target,
)
if not project_id:
msg = _('scope.project.id must be specified if include_subtree '
'is also specified')
msg = _(
'scope.project.id must be specified if include_subtree '
'is also specified'
)
raise exception.ValidationError(message=msg)
return self._build_role_assignments_list(include_subtree=True)
@ -144,31 +160,36 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
include_subtree=include_subtree,
inherited=self._inherited,
effective=self._effective,
include_names=include_names)
include_names=include_names,
)
formatted_refs = [self._format_entity(ref) for ref in refs]
return self.wrap_collection(formatted_refs)
def _assert_domain_nand_project(self):
if (flask.request.args.get('scope.domain.id') and
flask.request.args.get('scope.project.id')):
if flask.request.args.get(
'scope.domain.id'
) and flask.request.args.get('scope.project.id'):
msg = _('Specify a domain or project, not both')
raise exception.ValidationError(msg)
def _assert_system_nand_domain(self):
if (flask.request.args.get('scope.domain.id') and
flask.request.args.get('scope.system')):
if flask.request.args.get(
'scope.domain.id'
) and flask.request.args.get('scope.system'):
msg = _('Specify system or domain, not both')
raise exception.ValidationError(msg)
def _assert_system_nand_project(self):
if (flask.request.args.get('scope.project.id') and
flask.request.args.get('scope.system')):
if flask.request.args.get(
'scope.project.id'
) and flask.request.args.get('scope.system'):
msg = _('Specify system or project, not both')
raise exception.ValidationError(msg)
def _assert_user_nand_group(self):
if (flask.request.args.get('user.id') and
flask.request.args.get('group.id')):
if flask.request.args.get('user.id') and flask.request.args.get(
'group.id'
):
msg = _('Specify a user or group, not both')
raise exception.ValidationError(msg)
@ -184,14 +205,17 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
"""
if self._effective:
if flask.request.args.get('group.id'):
msg = _('Combining effective and group filter will always '
'result in an empty list.')
msg = _(
'Combining effective and group filter will always '
'result in an empty list.'
)
raise exception.ValidationError(msg)
if self._inherited and flask.request.args.get('scope.domain.id'):
msg = _(
'Combining effective, domain and inherited filters will '
'always result in an empty list.')
'always result in an empty list.'
)
raise exception.ValidationError(msg)
@property
@ -275,33 +299,45 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
if 'project_id' in entity:
if 'project_name' in entity:
formatted_entity['scope'] = {'project': {
'id': entity['project_id'],
'name': entity['project_name'],
'domain': {'id': entity['project_domain_id'],
'name': entity['project_domain_name']}}}
formatted_entity['scope'] = {
'project': {
'id': entity['project_id'],
'name': entity['project_name'],
'domain': {
'id': entity['project_domain_id'],
'name': entity['project_domain_name'],
},
}
}
else:
formatted_entity['scope'] = {
'project': {'id': entity['project_id']}}
'project': {'id': entity['project_id']}
}
if 'domain_id' in entity.get('indirect', {}):
inherited_assignment = True
formatted_link = ('/domains/%s' %
entity['indirect']['domain_id'])
formatted_link = (
'/domains/%s' % entity['indirect']['domain_id']
)
elif 'project_id' in entity.get('indirect', {}):
inherited_assignment = True
formatted_link = ('/projects/%s' %
entity['indirect']['project_id'])
formatted_link = (
'/projects/%s' % entity['indirect']['project_id']
)
else:
formatted_link = '/projects/%s' % entity['project_id']
elif 'domain_id' in entity:
if 'domain_name' in entity:
formatted_entity['scope'] = {
'domain': {'id': entity['domain_id'],
'name': entity['domain_name']}}
'domain': {
'id': entity['domain_id'],
'name': entity['domain_name'],
}
}
else:
formatted_entity['scope'] = {
'domain': {'id': entity['domain_id']}}
'domain': {'id': entity['domain_id']}
}
formatted_link = '/domains/%s' % entity['domain_id']
elif 'system' in entity:
formatted_link = '/system'
@ -312,14 +348,18 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
formatted_entity['user'] = {
'id': entity['user_id'],
'name': entity['user_name'],
'domain': {'id': entity['user_domain_id'],
'name': entity['user_domain_name']}}
'domain': {
'id': entity['user_domain_id'],
'name': entity['user_domain_name'],
},
}
else:
formatted_entity['user'] = {'id': entity['user_id']}
if 'group_id' in entity.get('indirect', {}):
membership_url = (
ks_flask.base_url(path='/groups/%s/users/%s' % (
entity['indirect']['group_id'], entity['user_id'])))
membership_url = ks_flask.base_url(
path='/groups/%s/users/%s'
% (entity['indirect']['group_id'], entity['user_id'])
)
formatted_entity['links']['membership'] = membership_url
formatted_link += '/groups/%s' % entity['indirect']['group_id']
else:
@ -329,43 +369,54 @@ class RoleAssignmentsResource(ks_flask.ResourceBase):
formatted_entity['group'] = {
'id': entity['group_id'],
'name': entity['group_name'],
'domain': {'id': entity['group_domain_id'],
'name': entity['group_domain_name']}}
'domain': {
'id': entity['group_domain_id'],
'name': entity['group_domain_name'],
},
}
else:
formatted_entity['group'] = {'id': entity['group_id']}
formatted_link += '/groups/%s' % entity['group_id']
if 'role_name' in entity:
formatted_entity['role'] = {'id': entity['role_id'],
'name': entity['role_name']}
formatted_entity['role'] = {
'id': entity['role_id'],
'name': entity['role_name'],
}
if 'role_domain_id' in entity and 'role_domain_name' in entity:
formatted_entity['role'].update(
{'domain': {'id': entity['role_domain_id'],
'name': entity['role_domain_name']}})
{
'domain': {
'id': entity['role_domain_id'],
'name': entity['role_domain_name'],
}
}
)
else:
formatted_entity['role'] = {'id': entity['role_id']}
prior_role_link = ''
if 'role_id' in entity.get('indirect', {}):
formatted_link += '/roles/%s' % entity['indirect']['role_id']
prior_role_link = (
'/prior_role/%(prior)s/implies/%(implied)s' % {
'prior': entity['role_id'],
'implied': entity['indirect']['role_id']
})
prior_role_link = '/prior_role/%(prior)s/implies/%(implied)s' % {
'prior': entity['role_id'],
'implied': entity['indirect']['role_id'],
}
else:
formatted_link += '/roles/%s' % entity['role_id']
if inherited_assignment:
formatted_entity['scope']['OS-INHERIT:inherited_to'] = (
'projects')
formatted_link = ('/OS-INHERIT%s/inherited_to_projects' %
formatted_link)
formatted_entity['scope']['OS-INHERIT:inherited_to'] = 'projects'
formatted_link = (
'/OS-INHERIT%s/inherited_to_projects' % formatted_link
)
formatted_entity['links']['assignment'] = ks_flask.base_url(
path=formatted_link)
path=formatted_link
)
if prior_role_link:
formatted_entity['links']['prior_role'] = (
ks_flask.base_url(path=prior_role_link))
formatted_entity['links']['prior_role'] = ks_flask.base_url(
path=prior_role_link
)
return formatted_entity
@ -379,7 +430,8 @@ class RoleAssignmentsAPI(ks_flask.APIBase):
resource=RoleAssignmentsResource,
url='/role_assignments',
resource_kwargs={},
rel='role_assignments')
rel='role_assignments',
)
]

View File

@ -32,8 +32,10 @@ class RoleInferencesResource(flask_restful.Resource):
"""
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()}
role_dict = {
role_ref['id']: role_ref
for role_ref in PROVIDERS.role_api.list_roles()
}
rules = dict()
for ref in refs:
@ -42,15 +44,22 @@ class RoleInferencesResource(flask_restful.Resource):
implied = rules.get(prior_role_id, [])
implied.append(
shared.build_implied_role_response_data(
role_dict[implied_role_id]))
role_dict[implied_role_id]
)
)
rules[prior_role_id] = implied
inferences = []
for prior_id, implied, in rules.items():
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})
prior_id, role_dict[prior_id]['name']
)
inferences.append(
{'prior_role': prior_response, 'implies': implied}
)
results = {'role_inferences': inferences}
return results
@ -64,7 +73,8 @@ class RoleInferencesAPI(ks_flask.APIBase):
resource=RoleInferencesResource,
url='/role_inferences',
resource_kwargs={},
rel='role_inferences')
rel='role_inferences',
)
]

View File

@ -35,7 +35,8 @@ class RoleResource(ks_flask.ResourceBase):
collection_key = 'roles'
member_key = 'role'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='role_api', method='get_role')
api='role_api', method='get_role'
)
def _is_domain_role(self, role):
return bool(role.get('domain_id'))
@ -72,20 +73,24 @@ class RoleResource(ks_flask.ResourceBase):
# reraise the error after enforcement if needed.
raise err
else:
ENFORCER.enforce_call(action='identity:get_domain_role',
member_target_type='role',
member_target=role)
ENFORCER.enforce_call(
action='identity:get_domain_role',
member_target_type='role',
member_target=role,
)
return self.wrap_member(role)
def _list_roles(self):
filters = ['name', 'domain_id']
domain_filter = flask.request.args.get('domain_id')
if domain_filter:
ENFORCER.enforce_call(action='identity:list_domain_roles',
filters=filters)
ENFORCER.enforce_call(
action='identity:list_domain_roles', filters=filters
)
else:
ENFORCER.enforce_call(action='identity:list_roles',
filters=filters)
ENFORCER.enforce_call(
action='identity:list_roles', filters=filters
)
hints = self.build_driver_hints(filters)
if not domain_filter:
@ -113,7 +118,8 @@ class RoleResource(ks_flask.ResourceBase):
role = self._assign_unique_id(role)
role = self._normalize_dict(role)
ref = PROVIDERS.role_api.create_role(
role['id'], role, initiator=self.audit_initiator)
role['id'], role, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def patch(self, role_id):
@ -136,14 +142,17 @@ class RoleResource(ks_flask.ResourceBase):
if err:
raise err
else:
ENFORCER.enforce_call(action='identity:update_domain_role',
member_target_type='role',
member_target=role)
ENFORCER.enforce_call(
action='identity:update_domain_role',
member_target_type='role',
member_target=role,
)
request_body_role = self.request_body_json.get('role', {})
validation.lazy_validate(schema.role_update, request_body_role)
self._require_matching_id(request_body_role)
ref = PROVIDERS.role_api.update_role(
role_id, request_body_role, initiator=self.audit_initiator)
role_id, request_body_role, initiator=self.audit_initiator
)
return self.wrap_member(ref)
def delete(self, role_id):
@ -166,9 +175,11 @@ class RoleResource(ks_flask.ResourceBase):
if err:
raise err
else:
ENFORCER.enforce_call(action='identity:delete_domain_role',
member_target_type='role',
member_target=role)
ENFORCER.enforce_call(
action='identity:delete_domain_role',
member_target_type='role',
member_target=role,
)
PROVIDERS.role_api.delete_role(role_id, initiator=self.audit_initiator)
return None, http.client.NO_CONTENT
@ -177,10 +188,12 @@ def _build_enforcement_target_ref():
ref = {}
if flask.request.view_args:
ref['prior_role'] = PROVIDERS.role_api.get_role(
flask.request.view_args.get('prior_role_id'))
flask.request.view_args.get('prior_role_id')
)
if flask.request.view_args.get('implied_role_id'):
ref['implied_role'] = PROVIDERS.role_api.get_role(
flask.request.view_args['implied_role_id'])
flask.request.view_args['implied_role_id']
)
return ref
@ -190,8 +203,10 @@ class RoleImplicationListResource(flask_restful.Resource):
GET/HEAD /v3/roles/{prior_role_id}/implies
"""
ENFORCER.enforce_call(action='identity:list_implied_roles',
build_target=_build_enforcement_target_ref)
ENFORCER.enforce_call(
action='identity:list_implied_roles',
build_target=_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 = shared.role_inference_response(prior_role_id)
@ -199,10 +214,11 @@ class RoleImplicationListResource(flask_restful.Resource):
for implied_id in implied_ids:
implied_role = PROVIDERS.role_api.get_role(implied_id)
response_json['role_inference']['implies'].append(
shared.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)}
'self': ks_flask.base_url(path='/roles/%s/implies' % prior_role_id)
}
return response_json
@ -215,8 +231,10 @@ class RoleImplicationResource(flask_restful.Resource):
# consistent policy enforcement behavior even if it is superfluous.
# Alternatively we can keep check_implied_role and reference
# ._get_implied_role instead.
ENFORCER.enforce_call(action='identity:check_implied_role',
build_target=_build_enforcement_target_ref)
ENFORCER.enforce_call(
action='identity:check_implied_role',
build_target=_build_enforcement_target_ref,
)
self.get(prior_role_id, implied_role_id)
# NOTE(morgan): Our API here breaks HTTP Spec. This should be evaluated
# for a future fix. This should just return the above "get" however,
@ -231,22 +249,24 @@ class RoleImplicationResource(flask_restful.Resource):
"""
ENFORCER.enforce_call(
action='identity:get_implied_role',
build_target=_build_enforcement_target_ref)
build_target=_build_enforcement_target_ref,
)
return self._get_implied_role(prior_role_id, implied_role_id)
def _get_implied_role(self, prior_role_id, implied_role_id):
# Isolate this logic so it can be re-used without added enforcement
PROVIDERS.role_api.get_implied_role(
prior_role_id, implied_role_id)
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 = shared.role_inference_response(prior_role_id)
response_json['role_inference'][
'implies'] = shared.build_implied_role_response_data(
implied_role_ref)
response_json['role_inference']['implies'] = (
shared.build_implied_role_response_data(implied_role_ref)
)
response_json['links'] = {
'self': ks_flask.base_url(
path='/roles/%(prior)s/implies/%(implies)s' % {
'prior': prior_role_id, 'implies': implied_role_id})}
path='/roles/%(prior)s/implies/%(implies)s'
% {'prior': prior_role_id, 'implies': implied_role_id}
)
}
return response_json
def put(self, prior_role_id, implied_role_id):
@ -254,8 +274,10 @@ class RoleImplicationResource(flask_restful.Resource):
PUT /v3/roles/{prior_role_id}/implies/{implied_role_id}
"""
ENFORCER.enforce_call(action='identity:create_implied_role',
build_target=_build_enforcement_target_ref)
ENFORCER.enforce_call(
action='identity:create_implied_role',
build_target=_build_enforcement_target_ref,
)
PROVIDERS.role_api.create_implied_role(prior_role_id, implied_role_id)
response_json = self._get_implied_role(prior_role_id, implied_role_id)
return response_json, http.client.CREATED
@ -265,8 +287,10 @@ class RoleImplicationResource(flask_restful.Resource):
DELETE /v3/roles/{prior_role_id}/implies/{implied_role_id}
"""
ENFORCER.enforce_call(action='identity:delete_implied_role',
build_target=_build_enforcement_target_ref)
ENFORCER.enforce_call(
action='identity:delete_implied_role',
build_target=_build_enforcement_target_ref,
)
PROVIDERS.role_api.delete_implied_role(prior_role_id, implied_role_id)
return None, http.client.NO_CONTENT
@ -281,16 +305,21 @@ class RoleAPI(ks_flask.APIBase):
url='/roles/<string:prior_role_id>/implies',
resource_kwargs={},
rel='implied_roles',
path_vars={'prior_role_id': json_home.Parameters.ROLE_ID}),
path_vars={'prior_role_id': json_home.Parameters.ROLE_ID},
),
ks_flask.construct_resource_map(
resource=RoleImplicationResource,
resource_kwargs={},
url=('/roles/<string:prior_role_id>/'
'implies/<string:implied_role_id>'),
url=(
'/roles/<string:prior_role_id>/'
'implies/<string:implied_role_id>'
),
rel='implied_role',
path_vars={
'prior_role_id': json_home.Parameters.ROLE_ID,
'implied_role_id': json_home.Parameters.ROLE_ID})
'implied_role_id': json_home.Parameters.ROLE_ID,
},
),
]

View File

@ -39,8 +39,11 @@ def _calculate_signature_v1(string_to_sign, secret_key):
"""
key = str(secret_key).encode('utf-8')
b64_encode = base64.encodebytes
signed = b64_encode(hmac.new(key, string_to_sign, hashlib.sha1)
.digest()).decode('utf-8').strip()
signed = (
b64_encode(hmac.new(key, string_to_sign, hashlib.sha1).digest())
.decode('utf-8')
.strip()
)
return signed
@ -80,15 +83,18 @@ class S3Resource(EC2_S3_Resource.ResourceBase):
string_to_sign = base64.urlsafe_b64decode(str(credentials['token']))
if string_to_sign[0:4] != b'AWS4':
signature = _calculate_signature_v1(string_to_sign,
creds_ref['secret'])
signature = _calculate_signature_v1(
string_to_sign, creds_ref['secret']
)
else:
signature = _calculate_signature_v4(string_to_sign,
creds_ref['secret'])
signature = _calculate_signature_v4(
string_to_sign, creds_ref['secret']
)
if not utils.auth_str_equal(credentials['signature'], signature):
raise exception.Unauthorized(
message=_('Credential signature mismatch'))
message=_('Credential signature mismatch')
)
@ks_flask.unenforced_api
def post(self):
@ -115,7 +121,9 @@ class S3Api(ks_flask.APIBase):
resource_kwargs={},
rel='s3tokens',
resource_relation_func=(
json_home_relations.s3_token_resource_rel_func))
json_home_relations.s3_token_resource_rel_func
),
)
]

View File

@ -51,7 +51,8 @@ class ServicesResource(ks_flask.ResourceBase):
validation.lazy_validate(schema.service_create, service)
service = self._assign_unique_id(self._normalize_dict(service))
ref = PROVIDERS.catalog_api.create_service(
service['id'], service, initiator=self.audit_initiator)
service['id'], service, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def patch(self, service_id):
@ -60,13 +61,18 @@ class ServicesResource(ks_flask.ResourceBase):
validation.lazy_validate(schema.service_update, service)
self._require_matching_id(service)
ref = PROVIDERS.catalog_api.update_service(
service_id, service, initiator=self.audit_initiator)
service_id, service, initiator=self.audit_initiator
)
return self.wrap_member(ref)
def delete(self, service_id):
ENFORCER.enforce_call(action='identity:delete_service')
return PROVIDERS.catalog_api.delete_service(
service_id, initiator=self.audit_initiator), http.client.NO_CONTENT
return (
PROVIDERS.catalog_api.delete_service(
service_id, initiator=self.audit_initiator
),
http.client.NO_CONTENT,
)
class ServiceAPI(ks_flask.APIBase):

View File

@ -33,18 +33,21 @@ def _build_enforcement_target(allow_non_existing=False):
if flask.request.view_args:
if flask.request.view_args.get('role_id'):
target['role'] = PROVIDERS.role_api.get_role(
flask.request.view_args['role_id'])
flask.request.view_args['role_id']
)
if flask.request.view_args.get('user_id'):
try:
target['user'] = PROVIDERS.identity_api.get_user(
flask.request.view_args['user_id'])
flask.request.view_args['user_id']
)
except exception.UserNotFound:
if not allow_non_existing:
raise
else:
try:
target['group'] = PROVIDERS.identity_api.get_group(
flask.request.view_args.get('group_id'))
flask.request.view_args.get('group_id')
)
except exception.GroupNotFound:
if not allow_non_existing:
raise
@ -57,11 +60,14 @@ class SystemUsersListResource(flask_restful.Resource):
GET/HEAD /system/users/{user_id}/roles
"""
ENFORCER.enforce_call(action='identity:list_system_grants_for_user',
build_target=_build_enforcement_target)
ENFORCER.enforce_call(
action='identity:list_system_grants_for_user',
build_target=_build_enforcement_target,
)
refs = PROVIDERS.assignment_api.list_system_grants_for_user(user_id)
return ks_flask.ResourceBase.wrap_collection(
refs, collection_name='roles')
refs, collection_name='roles'
)
class SystemUsersResource(flask_restful.Resource):
@ -70,8 +76,10 @@ class SystemUsersResource(flask_restful.Resource):
GET/HEAD /system/users/{user_id}/roles/{role_id}
"""
ENFORCER.enforce_call(action='identity:check_system_grant_for_user',
build_target=_build_enforcement_target)
ENFORCER.enforce_call(
action='identity:check_system_grant_for_user',
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.check_system_grant_for_user(user_id, role_id)
return None, http.client.NO_CONTENT
@ -80,8 +88,10 @@ class SystemUsersResource(flask_restful.Resource):
PUT /system/users/{user_id}/roles/{role_id}
"""
ENFORCER.enforce_call(action='identity:create_system_grant_for_user',
build_target=_build_enforcement_target)
ENFORCER.enforce_call(
action='identity:create_system_grant_for_user',
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.create_system_grant_for_user(user_id, role_id)
return None, http.client.NO_CONTENT
@ -93,8 +103,9 @@ class SystemUsersResource(flask_restful.Resource):
ENFORCER.enforce_call(
action='identity:revoke_system_grant_for_user',
build_target=functools.partial(
_build_enforcement_target,
allow_non_existing=True))
_build_enforcement_target, allow_non_existing=True
),
)
PROVIDERS.assignment_api.delete_system_grant_for_user(user_id, role_id)
return None, http.client.NO_CONTENT
@ -105,11 +116,14 @@ class SystemGroupsRolesListResource(flask_restful.Resource):
GET/HEAD /system/groups/{group_id}/roles
"""
ENFORCER.enforce_call(action='identity:list_system_grants_for_group',
build_target=_build_enforcement_target)
ENFORCER.enforce_call(
action='identity:list_system_grants_for_group',
build_target=_build_enforcement_target,
)
refs = PROVIDERS.assignment_api.list_system_grants_for_group(group_id)
return ks_flask.ResourceBase.wrap_collection(
refs, collection_name='roles')
refs, collection_name='roles'
)
class SystemGroupsRolestResource(flask_restful.Resource):
@ -118,10 +132,13 @@ class SystemGroupsRolestResource(flask_restful.Resource):
GET/HEAD /system/groups/{group_id}/roles/{role_id}
"""
ENFORCER.enforce_call(action='identity:check_system_grant_for_group',
build_target=_build_enforcement_target)
ENFORCER.enforce_call(
action='identity:check_system_grant_for_group',
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.check_system_grant_for_group(
group_id, role_id)
group_id, role_id
)
return None, http.client.NO_CONTENT
def put(self, group_id, role_id):
@ -129,10 +146,13 @@ class SystemGroupsRolestResource(flask_restful.Resource):
PUT /system/groups/{group_id}/roles/{role_id}
"""
ENFORCER.enforce_call(action='identity:create_system_grant_for_group',
build_target=_build_enforcement_target)
ENFORCER.enforce_call(
action='identity:create_system_grant_for_group',
build_target=_build_enforcement_target,
)
PROVIDERS.assignment_api.create_system_grant_for_group(
group_id, role_id)
group_id, role_id
)
return None, http.client.NO_CONTENT
def delete(self, group_id, role_id):
@ -143,10 +163,12 @@ class SystemGroupsRolestResource(flask_restful.Resource):
ENFORCER.enforce_call(
action='identity:revoke_system_grant_for_group',
build_target=functools.partial(
_build_enforcement_target,
allow_non_existing=True))
_build_enforcement_target, allow_non_existing=True
),
)
PROVIDERS.assignment_api.delete_system_grant_for_group(
group_id, role_id)
group_id, role_id
)
return None, http.client.NO_CONTENT
@ -160,7 +182,8 @@ class SystemAPI(ks_flask.APIBase):
url='/system/users/<string:user_id>/roles',
resource_kwargs={},
rel='system_user_roles',
path_vars={'user_id': json_home.Parameters.USER_ID}),
path_vars={'user_id': json_home.Parameters.USER_ID},
),
ks_flask.construct_resource_map(
resource=SystemUsersResource,
url='/system/users/<string:user_id>/roles/<string:role_id>',
@ -168,13 +191,16 @@ class SystemAPI(ks_flask.APIBase):
rel='system_user_role',
path_vars={
'role_id': json_home.Parameters.ROLE_ID,
'user_id': json_home.Parameters.USER_ID}),
'user_id': json_home.Parameters.USER_ID,
},
),
ks_flask.construct_resource_map(
resource=SystemGroupsRolesListResource,
url='/system/groups/<string:group_id>/roles',
resource_kwargs={},
rel='system_group_roles',
path_vars={'group_id': json_home.Parameters.GROUP_ID}),
path_vars={'group_id': json_home.Parameters.GROUP_ID},
),
ks_flask.construct_resource_map(
resource=SystemGroupsRolestResource,
url='/system/groups/<string:group_id>/roles/<string:role_id>',
@ -182,7 +208,9 @@ class SystemAPI(ks_flask.APIBase):
rel='system_group_role',
path_vars={
'role_id': json_home.Parameters.ROLE_ID,
'group_id': json_home.Parameters.GROUP_ID})
'group_id': json_home.Parameters.GROUP_ID,
},
),
]

View File

@ -43,7 +43,8 @@ _build_resource_relation = json_home_relations.os_trust_resource_rel_func
_build_parameter_relation = json_home_relations.os_trust_parameter_rel_func
TRUST_ID_PARAMETER_RELATION = _build_parameter_relation(
parameter_name='trust_id')
parameter_name='trust_id'
)
def _build_trust_target_enforcement():
@ -60,17 +61,21 @@ def _build_trust_target_enforcement():
def _trustor_trustee_only(trust):
user_id = flask.request.environ.get(context.REQUEST_CONTEXT_ENV).user_id
if user_id not in [trust.get('trustee_user_id'),
trust.get('trustor_user_id')]:
if user_id not in [
trust.get('trustee_user_id'),
trust.get('trustor_user_id'),
]:
raise exception.ForbiddenAction(
action=_('Requested user has no relation to this trust'))
action=_('Requested user has no relation to this trust')
)
def _normalize_trust_expires_at(trust):
# correct isotime
if trust.get('expires_at') is not None:
trust['expires_at'] = utils.isotime(trust['expires_at'],
subsecond=True)
trust['expires_at'] = utils.isotime(
trust['expires_at'], subsecond=True
)
def _normalize_trust_roles(trust):
@ -81,7 +86,8 @@ def _normalize_trust_roles(trust):
try:
matching_role = PROVIDERS.role_api.get_role(trust_role)
full_role = ks_flask.ResourceBase.wrap_member(
matching_role, collection_name='roles', member_name='role')
matching_role, collection_name='roles', member_name='role'
)
trust_full_roles.append(full_role['role'])
except exception.RoleNotFound:
pass
@ -90,7 +96,8 @@ def _normalize_trust_roles(trust):
trust['roles_links'] = {
'self': ks_flask.base_url(path='/%s/roles' % trust['id']),
'next': None,
'previous': None}
'previous': None,
}
class TrustResource(ks_flask.ResourceBase):
@ -106,8 +113,10 @@ class TrustResource(ks_flask.ResourceBase):
token = self.auth_context['token']
if 'application_credential' in token.methods:
if not token.application_credential['unrestricted']:
action = _("Using method 'application_credential' is not "
"allowed for managing trusts.")
action = _(
"Using method 'application_credential' is not "
"allowed for managing trusts."
)
raise exception.ForbiddenAction(action=action)
def _find_redelegated_trust(self):
@ -130,8 +139,9 @@ class TrustResource(ks_flask.ResourceBase):
def _require_trustor_has_role_in_project(self, trust):
trustor_roles = self._get_trustor_roles(trust)
for trust_role in trust['roles']:
matching_roles = [x for x in trustor_roles
if x == trust_role['id']]
matching_roles = [
x for x in trustor_roles if x == trust_role['id']
]
if not matching_roles:
raise exception.RoleNotFound(role_id=trust_role['id'])
@ -139,7 +149,8 @@ class TrustResource(ks_flask.ResourceBase):
original_trust = trust.copy()
while original_trust.get('redelegated_trust_id'):
original_trust = PROVIDERS.trust_api.get_trust(
original_trust['redelegated_trust_id'])
original_trust['redelegated_trust_id']
)
if not ((trust.get('project_id')) in [None, '']):
# Check project exists.
@ -148,7 +159,9 @@ class TrustResource(ks_flask.ResourceBase):
assignment_list = PROVIDERS.assignment_api.list_role_assignments(
user_id=original_trust['trustor_user_id'],
project_id=original_trust['project_id'],
effective=True, strip_domain_roles=False)
effective=True,
strip_domain_roles=False,
)
return list({x['role_id'] for x in assignment_list})
else:
return []
@ -160,12 +173,15 @@ class TrustResource(ks_flask.ResourceBase):
roles.append({'id': role['id']})
else:
roles.append(
PROVIDERS.role_api.get_unique_role_by_name(role['name']))
PROVIDERS.role_api.get_unique_role_by_name(role['name'])
)
return roles
def _get_trust(self, trust_id):
ENFORCER.enforce_call(action='identity:get_trust',
build_target=_build_trust_target_enforcement)
ENFORCER.enforce_call(
action='identity:get_trust',
build_target=_build_trust_target_enforcement,
)
# NOTE(cmurphy) look up trust before doing is_admin authorization - to
# maintain the API contract, we expect a missing trust to raise a 404
@ -176,7 +192,8 @@ class TrustResource(ks_flask.ResourceBase):
# policies are not loaded for the is_admin context, so need to
# block access here
raise exception.ForbiddenAction(
action=_('Requested user has no relation to this trust'))
action=_('Requested user has no relation to this trust')
)
# NOTE(cmurphy) As of Train, the default policies enforce the
# identity:get_trust rule. However, in case the
@ -189,7 +206,8 @@ class TrustResource(ks_flask.ResourceBase):
LOG.warning(
"The policy check string for rule \"identity:get_trust\" "
"has been overridden to \"always true\". In the next release, "
"this will cause the" "\"identity:get_trust\" action to "
"this will cause the"
"\"identity:get_trust\" action to "
"be fully permissive as hardcoded enforcement will be "
"removed. To correct this issue, either stop overriding the "
"\"identity:get_trust\" rule in config to accept the "
@ -206,12 +224,14 @@ class TrustResource(ks_flask.ResourceBase):
trustee_user_id = flask.request.args.get('trustee_user_id')
if trustor_user_id:
target = {'trust': {'trustor_user_id': trustor_user_id}}
ENFORCER.enforce_call(action='identity:list_trusts_for_trustor',
target_attr=target)
ENFORCER.enforce_call(
action='identity:list_trusts_for_trustor', target_attr=target
)
elif trustee_user_id:
target = {'trust': {'trustee_user_id': trustee_user_id}}
ENFORCER.enforce_call(action='identity:list_trusts_for_trustee',
target_attr=target)
ENFORCER.enforce_call(
action='identity:list_trusts_for_trustee', target_attr=target
)
else:
ENFORCER.enforce_call(action='identity:list_trusts')
@ -244,10 +264,12 @@ class TrustResource(ks_flask.ResourceBase):
trusts += PROVIDERS.trust_api.list_trusts()
elif trustor_user_id:
trusts += PROVIDERS.trust_api.list_trusts_for_trustor(
trustor_user_id)
trustor_user_id
)
elif trustee_user_id:
trusts += PROVIDERS.trust_api.list_trusts_for_trustee(
trustee_user_id)
trustee_user_id
)
for trust in trusts:
# get_trust returns roles, list_trusts does not
@ -257,8 +279,9 @@ class TrustResource(ks_flask.ResourceBase):
del trust['roles']
if trust.get('expires_at') is not None:
trust['expires_at'] = utils.isotime(trust['expires_at'],
subsecond=True)
trust['expires_at'] = utils.isotime(
trust['expires_at'], subsecond=True
)
return self.wrap_collection(trusts)
@ -294,7 +317,8 @@ class TrustResource(ks_flask.ResourceBase):
trust['roles'] = self._normalize_role_list(trust.get('roles', []))
self._require_trustor_has_role_in_project(trust)
trust['expires_at'] = self._parse_expiration_date(
trust.get('expires_at'))
trust.get('expires_at')
)
trust = self._assign_unique_id(trust)
redelegated_trust = self._find_redelegated_trust()
return_trust = PROVIDERS.trust_api.create_trust(
@ -302,14 +326,17 @@ class TrustResource(ks_flask.ResourceBase):
trust=trust,
roles=trust['roles'],
redelegated_trust=redelegated_trust,
initiator=self.audit_initiator)
initiator=self.audit_initiator,
)
_normalize_trust_expires_at(return_trust)
_normalize_trust_roles(return_trust)
return self.wrap_member(return_trust), http.client.CREATED
def delete(self, trust_id):
ENFORCER.enforce_call(action='identity:delete_trust',
build_target=_build_trust_target_enforcement)
ENFORCER.enforce_call(
action='identity:delete_trust',
build_target=_build_trust_target_enforcement,
)
self._check_unrestricted()
# NOTE(cmurphy) As of Train, the default policies enforce the
@ -323,19 +350,23 @@ class TrustResource(ks_flask.ResourceBase):
LOG.warning(
"The policy check string for rule \"identity:delete_trust\" "
"has been overridden to \"always true\". In the next release, "
"this will cause the" "\"identity:delete_trust\" action to "
"this will cause the"
"\"identity:delete_trust\" action to "
"be fully permissive as hardcoded enforcement will be "
"removed. To correct this issue, either stop overriding the "
"\"identity:delete_trust\" rule in config to accept the "
"defaults, or explicitly set a rule that is not empty."
)
trust = PROVIDERS.trust_api.get_trust(trust_id)
if (self.oslo_context.user_id != trust.get('trustor_user_id') and
not self.oslo_context.is_admin):
if (
self.oslo_context.user_id != trust.get('trustor_user_id')
and not self.oslo_context.is_admin
):
action = _('Only admin or trustor can delete a trust')
raise exception.ForbiddenAction(action=action)
PROVIDERS.trust_api.delete_trust(trust_id,
initiator=self.audit_initiator)
PROVIDERS.trust_api.delete_trust(
trust_id, initiator=self.audit_initiator
)
return '', http.client.NO_CONTENT
@ -349,8 +380,10 @@ class RolesForTrustListResource(flask_restful.Resource):
return flask.request.environ.get(context.REQUEST_CONTEXT_ENV, None)
def get(self, trust_id):
ENFORCER.enforce_call(action='identity:list_roles_for_trust',
build_target=_build_trust_target_enforcement)
ENFORCER.enforce_call(
action='identity:list_roles_for_trust',
build_target=_build_trust_target_enforcement,
)
# NOTE(morgan): This duplicates a little of the .get_trust from the
# main resource, as it needs some of the same logic. However, due to
@ -360,7 +393,8 @@ class RolesForTrustListResource(flask_restful.Resource):
# policies are not loaded for the is_admin context, so need to
# block access here
raise exception.ForbiddenAction(
action=_('Requested user has no relation to this trust'))
action=_('Requested user has no relation to this trust')
)
trust = PROVIDERS.trust_api.get_trust(trust_id)
@ -370,7 +404,8 @@ class RolesForTrustListResource(flask_restful.Resource):
# default that would have been produced by the sample config, we need
# to enforce it again and warn that the behavior is changing.
rules = policy._ENFORCER._enforcer.rules.get(
'identity:list_roles_for_trust')
'identity:list_roles_for_trust'
)
# rule check_str is ""
if isinstance(rules, op_checks.TrueCheck):
LOG.warning(
@ -387,8 +422,7 @@ class RolesForTrustListResource(flask_restful.Resource):
_normalize_trust_expires_at(trust)
_normalize_trust_roles(trust)
return {'roles': trust['roles'],
'links': trust['roles_links']}
return {'roles': trust['roles'], 'links': trust['roles_links']}
# NOTE(morgan): Since this Resource is not being used with the automatic
@ -402,14 +436,17 @@ class RoleForTrustResource(flask_restful.Resource):
def get(self, trust_id, role_id):
"""Get a role that has been assigned to a trust."""
ENFORCER.enforce_call(action='identity:get_role_for_trust',
build_target=_build_trust_target_enforcement)
ENFORCER.enforce_call(
action='identity:get_role_for_trust',
build_target=_build_trust_target_enforcement,
)
if self.oslo_context.is_admin:
# policies are not loaded for the is_admin context, so need to
# block access here
raise exception.ForbiddenAction(
action=_('Requested user has no relation to this trust'))
action=_('Requested user has no relation to this trust')
)
trust = PROVIDERS.trust_api.get_trust(trust_id)
@ -419,7 +456,8 @@ class RoleForTrustResource(flask_restful.Resource):
# default that would have been produced by the sample config, we need
# to enforce it again and warn that the behavior is changing.
rules = policy._ENFORCER._enforcer.rules.get(
'identity:get_role_for_trust')
'identity:get_role_for_trust'
)
# rule check_str is ""
if isinstance(rules, op_checks.TrueCheck):
LOG.warning(
@ -438,8 +476,9 @@ class RoleForTrustResource(flask_restful.Resource):
raise exception.RoleNotFound(role_id=role_id)
role = PROVIDERS.role_api.get_role(role_id)
return ks_flask.ResourceBase.wrap_member(role, collection_name='roles',
member_name='role')
return ks_flask.ResourceBase.wrap_member(
role, collection_name='roles', member_name='role'
)
class TrustAPI(ks_flask.APIBase):
@ -452,9 +491,9 @@ class TrustAPI(ks_flask.APIBase):
url='/trusts/<string:trust_id>/roles',
resource_kwargs={},
rel='trust_roles',
path_vars={
'trust_id': TRUST_ID_PARAMETER_RELATION},
resource_relation_func=_build_resource_relation),
path_vars={'trust_id': TRUST_ID_PARAMETER_RELATION},
resource_relation_func=_build_resource_relation,
),
ks_flask.construct_resource_map(
resource=RoleForTrustResource,
url='/trusts/<string:trust_id>/roles/<string:role_id>',
@ -462,8 +501,10 @@ class TrustAPI(ks_flask.APIBase):
rel='trust_role',
path_vars={
'trust_id': TRUST_ID_PARAMETER_RELATION,
'role_id': json_home.Parameters.ROLE_ID},
resource_relation_func=_build_resource_relation),
'role_id': json_home.Parameters.ROLE_ID,
},
resource_relation_func=_build_resource_relation,
),
]
_api_url_prefix = '/OS-TRUST'

View File

@ -43,7 +43,8 @@ PROVIDERS = provider_api.ProviderAPIs
ACCESS_TOKEN_ID_PARAMETER_RELATION = (
json_home_relations.os_oauth1_parameter_rel_func(
parameter_name='access_token_id')
parameter_name='access_token_id'
)
)
@ -56,11 +57,13 @@ def _convert_v3_to_ec2_credential(credential):
blob = jsonutils.loads(credential['blob'])
except TypeError:
blob = credential['blob']
return {'user_id': credential.get('user_id'),
'tenant_id': credential.get('project_id'),
'access': blob.get('access'),
'secret': blob.get('secret'),
'trust_id': blob.get('trust_id')}
return {
'user_id': credential.get('user_id'),
'tenant_id': credential.get('project_id'),
'access': blob.get('access'),
'secret': blob.get('secret'),
'trust_id': blob.get('trust_id'),
}
def _format_token_entity(entity):
@ -73,12 +76,13 @@ def _format_token_entity(entity):
if 'access_secret' in entity:
formatted_entity.pop('access_secret')
url = ('/users/%(user_id)s/OS-OAUTH1/access_tokens/%(access_token_id)s'
'/roles' % {'user_id': user_id,
'access_token_id': access_token_id})
url = (
'/users/%(user_id)s/OS-OAUTH1/access_tokens/%(access_token_id)s'
'/roles' % {'user_id': user_id, 'access_token_id': access_token_id}
)
formatted_entity.setdefault('links', {})
formatted_entity['links']['roles'] = (ks_flask.base_url(url))
formatted_entity['links']['roles'] = ks_flask.base_url(url)
return formatted_entity
@ -86,9 +90,11 @@ def _format_token_entity(entity):
def _check_unrestricted_application_credential(token):
if 'application_credential' in token.methods:
if not token.application_credential['unrestricted']:
action = _("Using method 'application_credential' is not "
"allowed for managing additional application "
"credentials.")
action = _(
"Using method 'application_credential' is not "
"allowed for managing additional application "
"credentials."
)
raise ks_exception.ForbiddenAction(action=action)
@ -117,7 +123,8 @@ def _build_enforcer_target_data_owner_and_user_id_match():
if credential_id is not None:
hashed_id = utils.hash_access_key(credential_id)
ref['credential'] = PROVIDERS.credential_api.get_credential(
hashed_id)
hashed_id
)
return ref
@ -170,7 +177,8 @@ class UserResource(ks_flask.ResourceBase):
collection_key = 'users'
member_key = 'user'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='identity_api', method='get_user')
api='identity_api', method='get_user'
)
def get(self, user_id=None):
"""Get a user resource or list users.
@ -189,7 +197,7 @@ class UserResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:get_user',
build_target=_build_user_target_enforcement
build_target=_build_user_target_enforcement,
)
ref = PROVIDERS.identity_api.get_user(user_id)
return self.wrap_member(ref)
@ -199,8 +207,15 @@ class UserResource(ks_flask.ResourceBase):
GET/HEAD /v3/users
"""
filters = ('domain_id', 'enabled', 'idp_id', 'name', 'protocol_id',
'unique_id', 'password_expires_at')
filters = (
'domain_id',
'enabled',
'idp_id',
'name',
'protocol_id',
'unique_id',
'password_expires_at',
)
target = None
if self.oslo_context.domain_id:
target = {'domain_id': self.oslo_context.domain_id}
@ -212,7 +227,8 @@ class UserResource(ks_flask.ResourceBase):
if domain is None and self.oslo_context.domain_id:
domain = self.oslo_context.domain_id
refs = PROVIDERS.identity_api.list_users(
domain_scope=domain, hints=hints)
domain_scope=domain, hints=hints
)
# If the user making the request used a domain-scoped token, let's make
# sure we filter out users that are not in that domain. Otherwise, we'd
@ -242,8 +258,8 @@ class UserResource(ks_flask.ResourceBase):
user_data = self._normalize_dict(user_data)
user_data = self._normalize_domain_id(user_data)
ref = PROVIDERS.identity_api.create_user(
user_data,
initiator=self.audit_initiator)
user_data, initiator=self.audit_initiator
)
return self.wrap_member(ref), http.client.CREATED
def patch(self, user_id):
@ -253,14 +269,15 @@ class UserResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:update_user',
build_target=_build_user_target_enforcement
build_target=_build_user_target_enforcement,
)
PROVIDERS.identity_api.get_user(user_id)
user_data = self.request_body_json.get('user', {})
validation.lazy_validate(schema.user_update, user_data)
self._require_matching_id(user_data)
ref = PROVIDERS.identity_api.update_user(
user_id, user_data, initiator=self.audit_initiator)
user_id, user_data, initiator=self.audit_initiator
)
return self.wrap_member(ref)
def delete(self, user_id):
@ -270,10 +287,11 @@ class UserResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:delete_user',
build_target=_build_user_target_enforcement
build_target=_build_user_target_enforcement,
)
PROVIDERS.identity_api.delete_user(
user_id, initiator=self.audit_initiator)
user_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -293,7 +311,8 @@ class UserChangePasswordResource(ks_flask.ResourceBase):
user_id=user_id,
original_password=user_data['original_password'],
new_password=user_data['password'],
initiator=self.audit_initiator)
initiator=self.audit_initiator,
)
except AssertionError as e:
raise ks_exception.Unauthorized(
_('Error when changing user password: %s') % e
@ -305,13 +324,16 @@ class UserProjectsResource(ks_flask.ResourceBase):
collection_key = 'projects'
member_key = 'project'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='resource_api', method='get_project')
api='resource_api', method='get_project'
)
def get(self, user_id):
filters = ('domain_id', 'enabled', 'name')
ENFORCER.enforce_call(action='identity:list_user_projects',
filters=filters,
build_target=_build_user_target_enforcement)
ENFORCER.enforce_call(
action='identity:list_user_projects',
filters=filters,
build_target=_build_user_target_enforcement,
)
hints = self.build_driver_hints(filters)
refs = PROVIDERS.assignment_api.list_projects_for_user(user_id)
return self.wrap_collection(refs, hints=hints)
@ -321,7 +343,8 @@ class UserGroupsResource(ks_flask.ResourceBase):
collection_key = 'groups'
member_key = 'group'
get_member_from_driver = PROVIDERS.deferred_provider_lookup(
api='identity_api', method='get_group')
api='identity_api', method='get_group'
)
def get(self, user_id):
"""Get groups for a user.
@ -330,12 +353,15 @@ class UserGroupsResource(ks_flask.ResourceBase):
"""
filters = ('name',)
hints = self.build_driver_hints(filters)
ENFORCER.enforce_call(action='identity:list_groups_for_user',
build_target=_build_user_target_enforcement,
filters=filters)
refs = PROVIDERS.identity_api.list_groups_for_user(user_id=user_id,
hints=hints)
if (self.oslo_context.domain_id):
ENFORCER.enforce_call(
action='identity:list_groups_for_user',
build_target=_build_user_target_enforcement,
filters=filters,
)
refs = PROVIDERS.identity_api.list_groups_for_user(
user_id=user_id, hints=hints
)
if self.oslo_context.domain_id:
filtered_refs = []
for ref in refs:
if ref['domain_id'] == self.oslo_context.domain_id:
@ -358,7 +384,8 @@ class _UserOSEC2CredBaseResource(ks_flask.ResourceBase):
url = ks_flask.base_url(path) % {
'user_id': ref['user_id'],
'credential_id': ref['access']}
'credential_id': ref['access'],
}
ref.setdefault('links', {})
ref['links']['self'] = url
@ -372,10 +399,10 @@ class UserOSEC2CredentialsResourceListCreate(_UserOSEC2CredBaseResource):
ENFORCER.enforce_call(action='identity:ec2_list_credentials')
PROVIDERS.identity_api.get_user(user_id)
credential_refs = PROVIDERS.credential_api.list_credentials_for_user(
user_id, type=CRED_TYPE_EC2)
user_id, type=CRED_TYPE_EC2
)
collection_refs = [
_convert_v3_to_ec2_credential(cred)
for cred in credential_refs
_convert_v3_to_ec2_credential(cred) for cred in credential_refs
]
return self.wrap_collection(collection_refs)
@ -386,15 +413,16 @@ class UserOSEC2CredentialsResourceListCreate(_UserOSEC2CredBaseResource):
"""
target = {}
target['credential'] = {'user_id': user_id}
ENFORCER.enforce_call(action='identity:ec2_create_credential',
target_attr=target)
ENFORCER.enforce_call(
action='identity:ec2_create_credential', target_attr=target
)
PROVIDERS.identity_api.get_user(user_id)
tenant_id = self.request_body_json.get('tenant_id')
PROVIDERS.resource_api.get_project(tenant_id)
blob = dict(
access=uuid.uuid4().hex,
secret=uuid.uuid4().hex,
trust_id=self.oslo_context.trust_id
trust_id=self.oslo_context.trust_id,
)
credential_id = utils.hash_access_key(blob['access'])
cred_data = dict(
@ -402,7 +430,7 @@ class UserOSEC2CredentialsResourceListCreate(_UserOSEC2CredBaseResource):
project_id=tenant_id,
blob=jsonutils.dumps(blob),
id=credential_id,
type=CRED_TYPE_EC2
type=CRED_TYPE_EC2,
)
PROVIDERS.credential_api.create_credential(credential_id, cred_data)
ref = _convert_v3_to_ec2_credential(cred_data)
@ -415,7 +443,8 @@ class UserOSEC2CredentialsResourceGetDelete(_UserOSEC2CredBaseResource):
cred = PROVIDERS.credential_api.get_credential(credential_id)
if not cred or cred['type'] != CRED_TYPE_EC2:
raise ks_exception.Unauthorized(
message=_('EC2 access key not found.'))
message=_('EC2 access key not found.')
)
return _convert_v3_to_ec2_credential(cred)
def get(self, user_id, credential_id):
@ -425,8 +454,8 @@ class UserOSEC2CredentialsResourceGetDelete(_UserOSEC2CredBaseResource):
"""
func = _build_enforcer_target_data_owner_and_user_id_match
ENFORCER.enforce_call(
action='identity:ec2_get_credential',
build_target=func)
action='identity:ec2_get_credential', build_target=func
)
PROVIDERS.identity_api.get_user(user_id)
ec2_cred_id = utils.hash_access_key(credential_id)
cred_data = self._get_cred_data(ec2_cred_id)
@ -438,8 +467,9 @@ class UserOSEC2CredentialsResourceGetDelete(_UserOSEC2CredBaseResource):
DELETE /users/{user_id}/credentials/OS-EC2/{credential_id}
"""
func = _build_enforcer_target_data_owner_and_user_id_match
ENFORCER.enforce_call(action='identity:ec2_delete_credential',
build_target=func)
ENFORCER.enforce_call(
action='identity:ec2_delete_credential', build_target=func
)
PROVIDERS.identity_api.get_user(user_id)
ec2_cred_id = utils.hash_access_key(credential_id)
self._get_cred_data(ec2_cred_id)
@ -473,10 +503,13 @@ class OAuth1ListAccessTokensResource(_OAuth1ResourceBase):
ENFORCER.enforce_call(action='identity:list_access_tokens')
if self.oslo_context.is_delegated_auth:
raise ks_exception.Forbidden(
_('Cannot list request tokens with a token '
'issued via delegation.'))
_(
'Cannot list request tokens with a token '
'issued via delegation.'
)
)
refs = PROVIDERS.oauth_api.list_access_tokens(user_id)
formatted_refs = ([_format_token_entity(x) for x in refs])
formatted_refs = [_format_token_entity(x) for x in refs]
return self.wrap_collection(formatted_refs)
@ -500,7 +533,8 @@ class OAuth1AccessTokenCRUDResource(_OAuth1ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:ec2_delete_credential',
build_target=_build_enforcer_target_data_owner_and_user_id_match)
build_target=_build_enforcer_target_data_owner_and_user_id_match,
)
access_token = PROVIDERS.oauth_api.get_access_token(access_token_id)
reason = (
'Invalidating the token cache because an access token for '
@ -511,7 +545,8 @@ class OAuth1AccessTokenCRUDResource(_OAuth1ResourceBase):
)
notifications.invalidate_token_cache_notification(reason)
PROVIDERS.oauth_api.delete_access_token(
user_id, access_token_id, initiator=self.audit_initiator)
user_id, access_token_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -531,7 +566,7 @@ class OAuth1AccessTokenRoleListResource(ks_flask.ResourceBase):
raise ks_exception.NotFound()
authed_role_ids = access_token['role_ids']
authed_role_ids = jsonutils.loads(authed_role_ids)
refs = ([_format_role_entity(x) for x in authed_role_ids])
refs = [_format_role_entity(x) for x in authed_role_ids]
return self.wrap_collection(refs)
@ -561,19 +596,21 @@ class OAuth1AccessTokenRoleResource(ks_flask.ResourceBase):
class UserAppCredListCreateResource(ks_flask.ResourceBase):
collection_key = 'application_credentials'
member_key = 'application_credential'
_public_parameters = frozenset([
'id',
'name',
'description',
'expires_at',
'project_id',
'roles',
# secret is only exposed after create, it is not stored
'secret',
'links',
'unrestricted',
'access_rules'
])
_public_parameters = frozenset(
[
'id',
'name',
'description',
'expires_at',
'project_id',
'roles',
# secret is only exposed after create, it is not stored
'secret',
'links',
'unrestricted',
'access_rules',
]
)
@staticmethod
def _generate_secret():
@ -591,8 +628,9 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
if role.get('id'):
roles.append(role)
else:
roles.append(PROVIDERS.role_api.get_unique_role_by_name(
role['name']))
roles.append(
PROVIDERS.role_api.get_unique_role_by_name(role['name'])
)
return roles
def _get_roles(self, app_cred_data, token):
@ -606,10 +644,13 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
token_roles = [r['id'] for r in token.roles]
for role in roles:
if role['id'] not in token_roles:
detail = _('Cannot create an application credential with '
'unassigned role')
detail = _(
'Cannot create an application credential with '
'unassigned role'
)
raise ks_exception.ApplicationCredentialValidationError(
detail=detail)
detail=detail
)
else:
roles = token.roles
return roles
@ -620,8 +661,9 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
GET/HEAD /v3/users/{user_id}/application_credentials
"""
filters = ('name',)
ENFORCER.enforce_call(action='identity:list_application_credentials',
filters=filters)
ENFORCER.enforce_call(
action='identity:list_application_credentials', filters=filters
)
app_cred_api = PROVIDERS.application_credential_api
hints = self.build_driver_hints(filters)
refs = app_cred_api.list_application_credentials(user_id, hints=hints)
@ -634,14 +676,17 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(action='identity:create_application_credential')
app_cred_data = self.request_body_json.get(
'application_credential', {})
validation.lazy_validate(app_cred_schema.application_credential_create,
app_cred_data)
'application_credential', {}
)
validation.lazy_validate(
app_cred_schema.application_credential_create, app_cred_data
)
token = self.auth_context['token']
_check_unrestricted_application_credential(token)
if self.oslo_context.user_id != user_id:
action = _('Cannot create an application credential for another '
'user.')
action = _(
'Cannot create an application credential for another ' 'user.'
)
raise ks_exception.ForbiddenAction(action=action)
project_id = self.oslo_context.project_id
app_cred_data = self._assign_unique_id(app_cred_data)
@ -652,7 +697,8 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
app_cred_data['roles'] = self._get_roles(app_cred_data, token)
if app_cred_data.get('expires_at'):
app_cred_data['expires_at'] = utils.parse_expiration_date(
app_cred_data['expires_at'])
app_cred_data['expires_at']
)
if app_cred_data.get('access_rules'):
for access_rule in app_cred_data['access_rules']:
# If user provides an access rule by ID, it will be looked up
@ -668,13 +714,15 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
try:
ref = app_cred_api.create_application_credential(
app_cred_data, initiator=self.audit_initiator)
app_cred_data, initiator=self.audit_initiator
)
except ks_exception.RoleAssignmentNotFound as e:
# Raise a Bad Request, not a Not Found, in accordance with the
# API-SIG recommendations:
# https://specs.openstack.org/openstack/api-wg/guidelines/http.html#failure-code-clarifications
raise ks_exception.ApplicationCredentialValidationError(
detail=str(e))
detail=str(e)
)
return self.wrap_member(ref), http.client.CREATED
@ -694,7 +742,8 @@ class UserAppCredGetDeleteResource(ks_flask.ResourceBase):
target_attr=target,
)
ref = PROVIDERS.application_credential_api.get_application_credential(
application_credential_id)
application_credential_id
)
return self.wrap_member(ref)
def delete(self, user_id, application_credential_id):
@ -705,13 +754,13 @@ class UserAppCredGetDeleteResource(ks_flask.ResourceBase):
"""
target = _update_request_user_id_attribute()
ENFORCER.enforce_call(
action='identity:delete_application_credential',
target_attr=target
action='identity:delete_application_credential', target_attr=target
)
token = self.auth_context['token']
_check_unrestricted_application_credential(token)
PROVIDERS.application_credential_api.delete_application_credential(
application_credential_id, initiator=self.audit_initiator)
application_credential_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -724,10 +773,16 @@ class UserAccessRuleListResource(ks_flask.ResourceBase):
GET/HEAD /v3/users/{user_id}/access_rules
"""
filters = ('service', 'path', 'method',)
ENFORCER.enforce_call(action='identity:list_access_rules',
filters=filters,
build_target=_build_user_target_enforcement)
filters = (
'service',
'path',
'method',
)
ENFORCER.enforce_call(
action='identity:list_access_rules',
filters=filters,
build_target=_build_user_target_enforcement,
)
app_cred_api = PROVIDERS.application_credential_api
hints = self.build_driver_hints(filters)
refs = app_cred_api.list_access_rules_for_user(user_id, hints=hints)
@ -746,10 +801,11 @@ class UserAccessRuleGetDeleteResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:get_access_rule',
build_target=_build_user_target_enforcement
build_target=_build_user_target_enforcement,
)
ref = PROVIDERS.application_credential_api.get_access_rule(
access_rule_id)
access_rule_id
)
return self.wrap_member(ref)
def delete(self, user_id, access_rule_id):
@ -759,10 +815,11 @@ class UserAccessRuleGetDeleteResource(ks_flask.ResourceBase):
"""
ENFORCER.enforce_call(
action='identity:delete_access_rule',
build_target=_build_user_target_enforcement
build_target=_build_user_target_enforcement,
)
PROVIDERS.application_credential_api.delete_access_rule(
access_rule_id, initiator=self.audit_initiator)
access_rule_id, initiator=self.audit_initiator
)
return None, http.client.NO_CONTENT
@ -776,21 +833,21 @@ class UserAPI(ks_flask.APIBase):
url='/users/<string:user_id>/password',
resource_kwargs={},
rel='user_change_password',
path_vars={'user_id': json_home.Parameters.USER_ID}
path_vars={'user_id': json_home.Parameters.USER_ID},
),
ks_flask.construct_resource_map(
resource=UserGroupsResource,
url='/users/<string:user_id>/groups',
resource_kwargs={},
rel='user_groups',
path_vars={'user_id': json_home.Parameters.USER_ID}
path_vars={'user_id': json_home.Parameters.USER_ID},
),
ks_flask.construct_resource_map(
resource=UserProjectsResource,
url='/users/<string:user_id>/projects',
resource_kwargs={},
rel='user_projects',
path_vars={'user_id': json_home.Parameters.USER_ID}
path_vars={'user_id': json_home.Parameters.USER_ID},
),
ks_flask.construct_resource_map(
resource=UserOSEC2CredentialsResourceListCreate,
@ -798,21 +855,27 @@ class UserAPI(ks_flask.APIBase):
resource_kwargs={},
rel='user_credentials',
resource_relation_func=(
json_home_relations.os_ec2_resource_rel_func),
path_vars={'user_id': json_home.Parameters.USER_ID}
json_home_relations.os_ec2_resource_rel_func
),
path_vars={'user_id': json_home.Parameters.USER_ID},
),
ks_flask.construct_resource_map(
resource=UserOSEC2CredentialsResourceGetDelete,
url=('/users/<string:user_id>/credentials/OS-EC2/'
'<string:credential_id>'),
url=(
'/users/<string:user_id>/credentials/OS-EC2/'
'<string:credential_id>'
),
resource_kwargs={},
rel='user_credential',
resource_relation_func=(
json_home_relations.os_ec2_resource_rel_func),
json_home_relations.os_ec2_resource_rel_func
),
path_vars={
'credential_id': json_home.build_v3_parameter_relation(
'credential_id'),
'user_id': json_home.Parameters.USER_ID}
'credential_id'
),
'user_id': json_home.Parameters.USER_ID,
},
),
ks_flask.construct_resource_map(
resource=OAuth1ListAccessTokensResource,
@ -820,80 +883,99 @@ class UserAPI(ks_flask.APIBase):
resource_kwargs={},
rel='user_access_tokens',
resource_relation_func=(
json_home_relations.os_oauth1_resource_rel_func),
path_vars={'user_id': json_home.Parameters.USER_ID}
json_home_relations.os_oauth1_resource_rel_func
),
path_vars={'user_id': json_home.Parameters.USER_ID},
),
ks_flask.construct_resource_map(
resource=OAuth1AccessTokenCRUDResource,
url=('/users/<string:user_id>/OS-OAUTH1/'
'access_tokens/<string:access_token_id>'),
url=(
'/users/<string:user_id>/OS-OAUTH1/'
'access_tokens/<string:access_token_id>'
),
resource_kwargs={},
rel='user_access_token',
resource_relation_func=(
json_home_relations.os_oauth1_resource_rel_func),
json_home_relations.os_oauth1_resource_rel_func
),
path_vars={
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
'user_id': json_home.Parameters.USER_ID}
'user_id': json_home.Parameters.USER_ID,
},
),
ks_flask.construct_resource_map(
resource=OAuth1AccessTokenRoleListResource,
url=('/users/<string:user_id>/OS-OAUTH1/access_tokens/'
'<string:access_token_id>/roles'),
url=(
'/users/<string:user_id>/OS-OAUTH1/access_tokens/'
'<string:access_token_id>/roles'
),
resource_kwargs={},
rel='user_access_token_roles',
resource_relation_func=(
json_home_relations.os_oauth1_resource_rel_func),
path_vars={'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
'user_id': json_home.Parameters.USER_ID}
json_home_relations.os_oauth1_resource_rel_func
),
path_vars={
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
'user_id': json_home.Parameters.USER_ID,
},
),
ks_flask.construct_resource_map(
resource=OAuth1AccessTokenRoleResource,
url=('/users/<string:user_id>/OS-OAUTH1/access_tokens/'
'<string:access_token_id>/roles/<string:role_id>'),
url=(
'/users/<string:user_id>/OS-OAUTH1/access_tokens/'
'<string:access_token_id>/roles/<string:role_id>'
),
resource_kwargs={},
rel<