Merge "Blackify the keystone code base"
This commit is contained in:
commit
181dc7b03f
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
)
|
||||
),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
@ -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},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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,
|
||||
)
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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,)
|
||||
|
@ -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,)
|
||||
|
@ -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,
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -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,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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',
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
@ -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',
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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={},
|
||||