Merge "Blackify the keystone code base"

This commit is contained in:
Zuul 2024-07-24 17:15:09 +00:00 committed by Gerrit Code Review
commit 181dc7b03f
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):
@ -619,10 +657,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
@ -633,8 +674,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)
@ -647,14 +689,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)
@ -665,7 +710,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
@ -681,13 +727,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
@ -707,7 +755,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):
@ -718,13 +767,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
@ -737,10 +786,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)
@ -759,10 +814,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):
@ -772,10 +828,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
@ -789,21 +846,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,
@ -811,21 +868,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,
@ -833,80 +896,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={},