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"
|
||||
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})
|
||||
" 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"
|
||||
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})
|
||||
" 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"
|
||||
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})
|
||||
" 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'),
|
||||
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'))]
|
||||
('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(
|
||||
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)
|
||||
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(
|
||||
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)
|
||||
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': [{
|
||||
'links': [
|
||||
{
|
||||
'rel': 'self',
|
||||
'href': identity_url,
|
||||
}],
|
||||
'media-types': [{
|
||||
'base': 'application/json',
|
||||
'type': MEDIA_TYPE_JSON % 'v3'
|
||||
}]
|
||||
}
|
||||
],
|
||||
'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
|
||||
|
||||
|
||||
@ -185,7 +189,8 @@ class DomainConfigBase(ks_flask.ResourceBase):
|
||||
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' % {
|
||||
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']})
|
||||
'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 "
|
||||
)
|
||||
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)
|
||||
"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 '
|
||||
LOG.info(
|
||||
'Group (%(group_id)s) was not found. Enforcement '
|
||||
'target of `group` remains empty.',
|
||||
{'group_id': group_id})
|
||||
{'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 '
|
||||
LOG.info(
|
||||
'Domain (%(domain_id)s) was not found. Enforcement '
|
||||
'target of `domain` remains empty.',
|
||||
{'domain_id': domain_id})
|
||||
{'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 '
|
||||
LOG.info(
|
||||
'Project (%(project_id)s) was not found. Enforcement '
|
||||
'target of `project` remains empty.',
|
||||
{'project_id': project_id})
|
||||
{'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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
group_id=group_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
group_id=group_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
group_id=group_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
group_id=group_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
user_id=user_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
user_id=user_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
user_id=user_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
domain_id=domain_id,
|
||||
user_id=user_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
project_id=project_id,
|
||||
user_id=user_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
project_id=project_id,
|
||||
user_id=user_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
project_id=project_id,
|
||||
user_id=user_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
project_id=project_id,
|
||||
group_id=group_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
project_id=project_id,
|
||||
group_id=group_id,
|
||||
role_id=role_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,
|
||||
build_target=functools.partial(
|
||||
_build_enforcement_target_attr,
|
||||
project_id=project_id,
|
||||
group_id=group_id,
|
||||
role_id=role_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({
|
||||
resp = make_response(
|
||||
{
|
||||
'access_token': token.id,
|
||||
'token_type': 'Bearer',
|
||||
'expires_in': CONF.token.expiration
|
||||
})
|
||||
'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: '
|
||||
_('Client authentication failed.'),
|
||||
)
|
||||
LOG.info(
|
||||
'Get OAuth2.0 Access Token API: '
|
||||
'mapping id %s is not found. ',
|
||||
mapping_id)
|
||||
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: '
|
||||
_('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))
|
||||
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: '
|
||||
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))
|
||||
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. '
|
||||
_('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'))
|
||||
'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. '
|
||||
_('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'))
|
||||
'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. '
|
||||
_('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'))
|
||||
'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. '
|
||||
_('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'))
|
||||
'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. '
|
||||
_('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'))
|
||||
'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: '
|
||||
_('Client authentication failed.'),
|
||||
)
|
||||
LOG.info(
|
||||
'Get OAuth2.0 Access Token API: '
|
||||
'the user does not exist. user id: %s.',
|
||||
client_id)
|
||||
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: '
|
||||
_('Client authentication failed.'),
|
||||
)
|
||||
LOG.info(
|
||||
'Get OAuth2.0 Access Token API: '
|
||||
'the user does not have default project. user id: %s.',
|
||||
client_id)
|
||||
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({
|
||||
resp = make_response(
|
||||
{
|
||||
'access_token': token.id,
|
||||
'token_type': 'Bearer',
|
||||
'expires_in': CONF.token.expiration
|
||||
})
|
||||
'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,11 +53,13 @@ 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],
|
||||
response = {
|
||||
'events': [event.to_dict() for event in events],
|
||||
'links': {
|
||||
'next': None,
|
||||
'self': ks_flask.base_url(path='/OS-REVOKE/events'),
|
||||
'previous': None}
|
||||
'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',
|
||||
ENFORCER.enforce_call(
|
||||
action='identity:list_projects',
|
||||
filters=filters,
|
||||
target_attr=target)
|
||||
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,
|
||||
def _build_enforcement_target_attr(
|
||||
role_id=None,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
allow_non_existing=False):
|
||||
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',
|
||||
ENFORCER.enforce_call(
|
||||
action='identity:list_role_assignments',
|
||||
filters=filters,
|
||||
target_attr=target)
|
||||
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': {
|
||||
formatted_entity['scope'] = {
|
||||
'project': {
|
||||
'id': entity['project_id'],
|
||||
'name': entity['project_name'],
|
||||
'domain': {'id': entity['project_domain_id'],
|
||||
'name': entity['project_domain_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_role_link = '/prior_role/%(prior)s/implies/%(implied)s' % {
|
||||
'prior': entity['role_id'],
|
||||
'implied': entity['indirect']['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',
|
||||
ENFORCER.enforce_call(
|
||||
action='identity:get_domain_role',
|
||||
member_target_type='role',
|
||||
member_target=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',
|
||||
ENFORCER.enforce_call(
|
||||
action='identity:update_domain_role',
|
||||
member_target_type='role',
|
||||
member_target=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',
|
||||
ENFORCER.enforce_call(
|
||||
action='identity:delete_domain_role',
|
||||
member_target_type='role',
|
||||
member_target=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'),
|
||||
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')}
|
||||
'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 "
|
||||
action = _(
|
||||
"Using method 'application_credential' is not "
|
||||
"allowed for managing additional application "
|
||||
"credentials.")
|
||||
"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',
|
||||
ENFORCER.enforce_call(
|
||||
action='identity:list_user_projects',
|
||||
filters=filters,
|
||||
build_target=_build_user_target_enforcement)
|
||||
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',
|
||||
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):
|
||||
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,7 +596,8 @@ class OAuth1AccessTokenRoleResource(ks_flask.ResourceBase):
|
||||
class UserAppCredListCreateResource(ks_flask.ResourceBase):
|
||||
collection_key = 'application_credentials'
|
||||
member_key = 'application_credential'
|
||||
_public_parameters = frozenset([
|
||||
_public_parameters = frozenset(
|
||||
[
|
||||
'id',
|
||||
'name',
|
||||
'description',
|
||||
@ -572,8 +608,9 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
|
||||
'secret',
|
||||
'links',
|
||||
'unrestricted',
|
||||
'access_rules'
|
||||
])
|
||||
'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 = (
|
||||
'service',
|
||||
'path',
|
||||
'method',
|
||||
)
|
||||
ENFORCER.enforce_call(
|
||||
action='identity:list_access_rules',
|
||||
filters=filters,
|
||||
build_target=_build_user_target_enforcement)
|
||||
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={},
|
||||
rel='user_access_token_role',
|
||||
resource_relation_func=(
|
||||
json_home_relations.os_oauth1_resource_rel_func),
|
||||
path_vars={'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
||||
json_home_relations.os_oauth1_resource_rel_func
|
||||
),
|
||||
path_vars={
|
||||
'access_token_id': ACCESS_TOKEN_ID_PARAMETER_RELATION,
|
||||
'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=UserAppCredListCreateResource,
|
||||
url='/users/<string:user_id>/application_credentials',
|
||||
resource_kwargs={},
|
||||
rel='application_credentials',
|
||||
path_vars={'user_id': json_home.Parameters.USER_ID}
|
||||
path_vars={'user_id': json_home.Parameters.USER_ID},
|
||||
),
|
||||
ks_flask.construct_resource_map(
|
||||
resource=UserAppCredGetDeleteResource,
|
||||
url=('/users/<string:user_id>/application_credentials/'
|
||||
'<string:application_credential_id>'),
|
||||
url=(
|
||||
'/users/<string:user_id>/application_credentials/'
|
||||
'<string:application_credential_id>'
|
||||
),
|
||||
resource_kwargs={},
|
||||
rel='application_credential',
|
||||
path_vars={
|
||||
'user_id': json_home.Parameters.USER_ID,
|
||||
'application_credential_id':
|
||||
json_home.Parameters.APPLICATION_CRED_ID}
|
||||
'application_credential_id': json_home.Parameters.APPLICATION_CRED_ID,
|
||||
},
|
||||
),
|
||||
ks_flask.construct_resource_map(
|
||||
resource=UserAccessRuleListResource,
|
||||
url='/users/<string:user_id>/access_rules',
|
||||
resource_kwargs={},
|
||||
rel='access_rules',
|
||||
path_vars={'user_id': json_home.Parameters.USER_ID}
|
||||
path_vars={'user_id': json_home.Parameters.USER_ID},
|
||||
),
|
||||
ks_flask.construct_resource_map(
|
||||
resource=UserAccessRuleGetDeleteResource,
|
||||
url=('/users/<string:user_id>/access_rules/'
|
||||
'<string:access_rule_id>'),
|
||||
url=(
|
||||
'/users/<string:user_id>/access_rules/'
|
||||
'<string:access_rule_id>'
|
||||
),
|
||||
resource_kwargs={},
|
||||
rel='access_rule',
|
||||
path_vars={
|
||||
'user_id': json_home.Parameters.USER_ID,
|
||||
'access_rule_id':
|
||||
json_home.Parameters.ACCESS_RULE_ID}
|
||||
)
|
||||
'access_rule_id': json_home.Parameters.ACCESS_RULE_ID,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -81,8 +81,9 @@ class ApplicationCredentialDriverBase(object, metaclass=abc.ABCMeta):
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_application_credentials_for_user_on_project(self, user_id,
|
||||
project_id):
|
||||
def delete_application_credentials_for_user_on_project(
|
||||
self, user_id, project_id
|
||||
):
|
||||
"""Delete all application credentials for a user on a given project.
|
||||
|
||||
:param str user_id: ID of a user to whose application credentials
|
||||
|
@ -25,9 +25,18 @@ from keystone.i18n import _
|
||||
|
||||
class ApplicationCredentialModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
__tablename__ = 'application_credential'
|
||||
attributes = ['internal_id', 'id', 'name', 'secret_hash', 'description',
|
||||
'user_id', 'project_id', 'system', 'expires_at',
|
||||
'unrestricted']
|
||||
attributes = [
|
||||
'internal_id',
|
||||
'id',
|
||||
'name',
|
||||
'secret_hash',
|
||||
'description',
|
||||
'user_id',
|
||||
'project_id',
|
||||
'system',
|
||||
'expires_at',
|
||||
'unrestricted',
|
||||
]
|
||||
internal_id = sql.Column(sql.Integer, primary_key=True, nullable=False)
|
||||
id = sql.Column(sql.String(64), nullable=False)
|
||||
name = sql.Column(sql.String(255), nullable=False)
|
||||
@ -38,19 +47,24 @@ class ApplicationCredentialModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
system = sql.Column(sql.String(64), nullable=True)
|
||||
expires_at = sql.Column(sql.DateTimeInt())
|
||||
unrestricted = sql.Column(sql.Boolean)
|
||||
__table_args__ = (sql.UniqueConstraint('name', 'user_id',
|
||||
name='duplicate_app_cred_constraint'),)
|
||||
__table_args__ = (
|
||||
sql.UniqueConstraint(
|
||||
'name', 'user_id', name='duplicate_app_cred_constraint'
|
||||
),
|
||||
)
|
||||
|
||||
roles = sqlalchemy.orm.relationship(
|
||||
'ApplicationCredentialRoleModel',
|
||||
backref=sqlalchemy.orm.backref('application_credential'),
|
||||
cascade='all, delete-orphan',
|
||||
cascade_backrefs=False)
|
||||
cascade_backrefs=False,
|
||||
)
|
||||
access_rules = sqlalchemy.orm.relationship(
|
||||
'ApplicationCredentialAccessRuleModel',
|
||||
backref=sqlalchemy.orm.backref('application_credential'),
|
||||
cascade='all, delete-orphan',
|
||||
cascade_backrefs=False)
|
||||
cascade_backrefs=False,
|
||||
)
|
||||
|
||||
|
||||
class ApplicationCredentialRoleModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
@ -58,10 +72,12 @@ class ApplicationCredentialRoleModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
attributes = ['application_credential_id', 'role_id']
|
||||
application_credential_id = sql.Column(
|
||||
sql.Integer,
|
||||
sql.ForeignKey('application_credential.internal_id',
|
||||
ondelete='cascade'),
|
||||
sql.ForeignKey(
|
||||
'application_credential.internal_id', ondelete='cascade'
|
||||
),
|
||||
primary_key=True,
|
||||
nullable=False)
|
||||
nullable=False,
|
||||
)
|
||||
role_id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
@ -75,13 +91,19 @@ class AccessRuleModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
path = sql.Column(sql.String(128))
|
||||
method = sql.Column(sql.String(16))
|
||||
__table_args__ = (
|
||||
sql.UniqueConstraint('user_id', 'service', 'path', 'method',
|
||||
name='duplicate_access_rule_for_user_constraint'),
|
||||
sql.UniqueConstraint(
|
||||
'user_id',
|
||||
'service',
|
||||
'path',
|
||||
'method',
|
||||
name='duplicate_access_rule_for_user_constraint',
|
||||
),
|
||||
)
|
||||
application_credential = sqlalchemy.orm.relationship(
|
||||
'ApplicationCredentialAccessRuleModel',
|
||||
backref=sqlalchemy.orm.backref('access_rule'),
|
||||
cascade_backrefs=False)
|
||||
cascade_backrefs=False,
|
||||
)
|
||||
|
||||
|
||||
class ApplicationCredentialAccessRuleModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
@ -89,15 +111,18 @@ class ApplicationCredentialAccessRuleModel(sql.ModelBase, sql.ModelDictMixin):
|
||||
attributes = ['application_credential_id', 'access_rule_id']
|
||||
application_credential_id = sql.Column(
|
||||
sql.Integer,
|
||||
sql.ForeignKey('application_credential.internal_id',
|
||||
ondelete='cascade'),
|
||||
sql.ForeignKey(
|
||||
'application_credential.internal_id', ondelete='cascade'
|
||||
),
|
||||
primary_key=True,
|
||||
nullable=False)
|
||||
nullable=False,
|
||||
)
|
||||
access_rule_id = sql.Column(
|
||||
sql.Integer,
|
||||
sql.ForeignKey('access_rule.id'),
|
||||
primary_key=True,
|
||||
nullable=False)
|
||||
nullable=False,
|
||||
)
|
||||
|
||||
|
||||
class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
@ -115,7 +140,8 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
msg = _('Invalid application credential ID or secret')
|
||||
try:
|
||||
app_cred_ref = self.get_application_credential(
|
||||
application_credential_id)
|
||||
application_credential_id
|
||||
)
|
||||
except exception.ApplicationCredentialNotFound:
|
||||
raise AssertionError(msg)
|
||||
if not self._check_secret(secret, app_cred_ref):
|
||||
@ -129,8 +155,9 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
app_cred_ref['secret_hash'] = hashed_secret
|
||||
|
||||
@sql.handle_conflicts(conflict_type='application_credential')
|
||||
def create_application_credential(self, application_credential, roles,
|
||||
access_rules=None):
|
||||
def create_application_credential(
|
||||
self, application_credential, roles, access_rules=None
|
||||
):
|
||||
app_cred = application_credential.copy()
|
||||
self._hash_secret(app_cred)
|
||||
with sql.session_for_write() as session:
|
||||
@ -143,23 +170,31 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
session.add(app_cred_role)
|
||||
if access_rules:
|
||||
for access_rule in access_rules:
|
||||
access_rule_ref = session.query(AccessRuleModel).filter_by(
|
||||
external_id=access_rule['id']).first()
|
||||
access_rule_ref = (
|
||||
session.query(AccessRuleModel)
|
||||
.filter_by(external_id=access_rule['id'])
|
||||
.first()
|
||||
)
|
||||
if not access_rule_ref:
|
||||
query = session.query(AccessRuleModel)
|
||||
access_rule_ref = query.filter_by(
|
||||
user_id=app_cred['user_id'],
|
||||
service=access_rule['service'],
|
||||
path=access_rule['path'],
|
||||
method=access_rule['method']).first()
|
||||
method=access_rule['method'],
|
||||
).first()
|
||||
if not access_rule_ref:
|
||||
access_rule_ref = AccessRuleModel.from_dict({
|
||||
access_rule_ref = AccessRuleModel.from_dict(
|
||||
{
|
||||
k.replace('id', 'external_id'): v
|
||||
for k, v in access_rule.items()})
|
||||
for k, v in access_rule.items()
|
||||
}
|
||||
)
|
||||
access_rule_ref['user_id'] = app_cred['user_id']
|
||||
session.add(access_rule_ref)
|
||||
app_cred_access_rule = (
|
||||
ApplicationCredentialAccessRuleModel())
|
||||
ApplicationCredentialAccessRuleModel()
|
||||
)
|
||||
app_cred_access_rule.application_credential = ref
|
||||
app_cred_access_rule.access_rule = access_rule_ref
|
||||
session.add(app_cred_access_rule)
|
||||
@ -190,19 +225,22 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
def get_application_credential(self, application_credential_id):
|
||||
with sql.session_for_read() as session:
|
||||
query = session.query(ApplicationCredentialModel).filter_by(
|
||||
id=application_credential_id)
|
||||
id=application_credential_id
|
||||
)
|
||||
ref = query.first()
|
||||
if ref is None:
|
||||
raise exception.ApplicationCredentialNotFound(
|
||||
application_credential_id=application_credential_id)
|
||||
application_credential_id=application_credential_id
|
||||
)
|
||||
app_cred_dict = self._to_dict(ref)
|
||||
return app_cred_dict
|
||||
|
||||
def list_application_credentials_for_user(self, user_id, hints):
|
||||
with sql.session_for_read() as session:
|
||||
query = session.query(ApplicationCredentialModel)
|
||||
query = sql.filter_limit_query(ApplicationCredentialModel, query,
|
||||
hints)
|
||||
query = sql.filter_limit_query(
|
||||
ApplicationCredentialModel, query, hints
|
||||
)
|
||||
app_creds = query.filter_by(user_id=user_id)
|
||||
return [self._to_dict(ref) for ref in app_creds]
|
||||
|
||||
@ -211,10 +249,12 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
with sql.session_for_write() as session:
|
||||
query = session.query(ApplicationCredentialModel)
|
||||
app_cred_ref = query.filter_by(
|
||||
id=application_credential_id).first()
|
||||
id=application_credential_id
|
||||
).first()
|
||||
if not app_cred_ref:
|
||||
raise exception.ApplicationCredentialNotFound(
|
||||
application_credential_id=application_credential_id)
|
||||
application_credential_id=application_credential_id
|
||||
)
|
||||
session.delete(app_cred_ref)
|
||||
|
||||
def delete_application_credentials_for_user(self, user_id):
|
||||
@ -223,8 +263,9 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
query = query.filter_by(user_id=user_id)
|
||||
query.delete()
|
||||
|
||||
def delete_application_credentials_for_user_on_project(self, user_id,
|
||||
project_id):
|
||||
def delete_application_credentials_for_user_on_project(
|
||||
self, user_id, project_id
|
||||
):
|
||||
with sql.session_for_write() as session:
|
||||
query = session.query(ApplicationCredentialModel)
|
||||
query = query.filter_by(user_id=user_id)
|
||||
@ -234,11 +275,13 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
def get_access_rule(self, access_rule_id):
|
||||
with sql.session_for_read() as session:
|
||||
query = session.query(AccessRuleModel).filter_by(
|
||||
external_id=access_rule_id)
|
||||
external_id=access_rule_id
|
||||
)
|
||||
ref = query.first()
|
||||
if not ref:
|
||||
raise exception.AccessRuleNotFound(
|
||||
access_rule_id=access_rule_id)
|
||||
access_rule_id=access_rule_id
|
||||
)
|
||||
access_rule = self._access_rule_to_dict(ref)
|
||||
return access_rule
|
||||
|
||||
@ -255,11 +298,13 @@ class ApplicationCredential(base.ApplicationCredentialDriverBase):
|
||||
ref = query.filter_by(external_id=access_rule_id).first()
|
||||
if not ref:
|
||||
raise exception.AccessRuleNotFound(
|
||||
access_rule_id=access_rule_id)
|
||||
access_rule_id=access_rule_id
|
||||
)
|
||||
session.delete(ref)
|
||||
except AssertionError:
|
||||
raise exception.ForbiddenNotSecurity(
|
||||
"May not delete access rule in use")
|
||||
"May not delete access rule in use"
|
||||
)
|
||||
|
||||
def delete_access_rules_for_user(self, user_id):
|
||||
with sql.session_for_write() as session:
|
||||
|
@ -48,43 +48,50 @@ class Manager(manager.Manager):
|
||||
|
||||
def _register_callback_listeners(self):
|
||||
notifications.register_event_callback(
|
||||
notifications.ACTIONS.deleted, 'user',
|
||||
self._delete_app_creds_on_user_delete_callback)
|
||||
notifications.ACTIONS.deleted,
|
||||
'user',
|
||||
self._delete_app_creds_on_user_delete_callback,
|
||||
)
|
||||
notifications.register_event_callback(
|
||||
notifications.ACTIONS.disabled, 'user',
|
||||
self._delete_app_creds_on_user_delete_callback)
|
||||
notifications.ACTIONS.disabled,
|
||||
'user',
|
||||
self._delete_app_creds_on_user_delete_callback,
|
||||
)
|
||||
notifications.register_event_callback(
|
||||
notifications.ACTIONS.internal,
|
||||
notifications.REMOVE_APP_CREDS_FOR_USER,
|
||||
self._delete_app_creds_on_assignment_removal)
|
||||
self._delete_app_creds_on_assignment_removal,
|
||||
)
|
||||
|
||||
def _delete_app_creds_on_user_delete_callback(
|
||||
self, service, resource_type, operation, payload):
|
||||
self, service, resource_type, operation, payload
|
||||
):
|
||||
user_id = payload['resource_info']
|
||||
self._delete_application_credentials_for_user(user_id)
|
||||
self._delete_access_rules_for_user(user_id)
|
||||
|
||||
def _delete_app_creds_on_assignment_removal(
|
||||
self, service, resource_type, operation, payload):
|
||||
self, service, resource_type, operation, payload
|
||||
):
|
||||
user_id = payload['resource_info']['user_id']
|
||||
project_id = payload['resource_info']['project_id']
|
||||
self._delete_application_credentials_for_user_on_project(user_id,
|
||||
project_id)
|
||||
self._delete_application_credentials_for_user_on_project(
|
||||
user_id, project_id
|
||||
)
|
||||
|
||||
def _get_user_roles(self, user_id, project_id):
|
||||
assignment_list = self.assignment_api.list_role_assignments(
|
||||
user_id=user_id,
|
||||
project_id=project_id,
|
||||
effective=True)
|
||||
user_id=user_id, project_id=project_id, effective=True
|
||||
)
|
||||
return list(set([x['role_id'] for x in assignment_list]))
|
||||
|
||||
def _require_user_has_role_in_project(self, roles, user_id, project_id):
|
||||
user_roles = self._get_user_roles(user_id, project_id)
|
||||
for role in roles:
|
||||
if role['id'] not in user_roles:
|
||||
raise exception.RoleAssignmentNotFound(role_id=role['id'],
|
||||
actor_id=user_id,
|
||||
target_id=project_id)
|
||||
raise exception.RoleAssignmentNotFound(
|
||||
role_id=role['id'], actor_id=user_id, target_id=project_id
|
||||
)
|
||||
|
||||
def _assert_limit_not_exceeded(self, user_id):
|
||||
user_limit = CONF.application_credential.user_limit
|
||||
@ -92,7 +99,8 @@ class Manager(manager.Manager):
|
||||
app_cred_count = len(self.list_application_credentials(user_id))
|
||||
if app_cred_count >= user_limit:
|
||||
raise exception.ApplicationCredentialLimitExceeded(
|
||||
limit=user_limit)
|
||||
limit=user_limit
|
||||
)
|
||||
|
||||
def _get_role_list(self, app_cred_roles):
|
||||
roles = []
|
||||
@ -112,12 +120,12 @@ class Manager(manager.Manager):
|
||||
def _process_app_cred(self, app_cred_ref):
|
||||
app_cred_ref = app_cred_ref.copy()
|
||||
app_cred_ref.pop('secret_hash')
|
||||
app_cred_ref['roles'] = self._get_role_list(
|
||||
app_cred_ref['roles'])
|
||||
app_cred_ref['roles'] = self._get_role_list(app_cred_ref['roles'])
|
||||
return app_cred_ref
|
||||
|
||||
def create_application_credential(self, application_credential,
|
||||
initiator=None):
|
||||
def create_application_credential(
|
||||
self, application_credential, initiator=None
|
||||
):
|
||||
"""Create a new application credential.
|
||||
|
||||
:param dict application_credential: Application Credential data
|
||||
@ -135,13 +143,13 @@ class Manager(manager.Manager):
|
||||
self._require_user_has_role_in_project(roles, user_id, project_id)
|
||||
unhashed_secret = application_credential['secret']
|
||||
ref = self.driver.create_application_credential(
|
||||
application_credential, roles, access_rules)
|
||||
application_credential, roles, access_rules
|
||||
)
|
||||
ref['secret'] = unhashed_secret
|
||||
ref = self._process_app_cred(ref)
|
||||
notifications.Audit.created(
|
||||
self._APP_CRED,
|
||||
application_credential['id'],
|
||||
initiator)
|
||||
self._APP_CRED, application_credential['id'], initiator
|
||||
)
|
||||
return ref
|
||||
|
||||
@MEMOIZE
|
||||
@ -153,7 +161,8 @@ class Manager(manager.Manager):
|
||||
:returns: an application credential
|
||||
"""
|
||||
app_cred = self.driver.get_application_credential(
|
||||
application_credential_id)
|
||||
application_credential_id
|
||||
)
|
||||
return self._process_app_cred(app_cred)
|
||||
|
||||
def list_application_credentials(self, user_id, hints=None):
|
||||
@ -166,7 +175,8 @@ class Manager(manager.Manager):
|
||||
"""
|
||||
hints = hints or driver_hints.Hints()
|
||||
app_cred_list = self.driver.list_application_credentials_for_user(
|
||||
user_id, hints)
|
||||
user_id, hints
|
||||
)
|
||||
return [self._process_app_cred(app_cred) for app_cred in app_cred_list]
|
||||
|
||||
@MEMOIZE
|
||||
@ -189,8 +199,9 @@ class Manager(manager.Manager):
|
||||
hints = hints or driver_hints.Hints()
|
||||
return self.driver.list_access_rules_for_user(user_id, hints)
|
||||
|
||||
def delete_application_credential(self, application_credential_id,
|
||||
initiator=None):
|
||||
def delete_application_credential(
|
||||
self, application_credential_id, initiator=None
|
||||
):
|
||||
"""Delete an application credential.
|
||||
|
||||
:param str application_credential_id: Application Credential ID
|
||||
@ -200,13 +211,16 @@ class Manager(manager.Manager):
|
||||
application credential doesn't exist.
|
||||
"""
|
||||
self.driver.delete_application_credential(application_credential_id)
|
||||
self.get_application_credential.invalidate(self,
|
||||
application_credential_id)
|
||||
self.get_application_credential.invalidate(
|
||||
self, application_credential_id
|
||||
)
|
||||
notifications.Audit.deleted(
|
||||
self._APP_CRED, application_credential_id, initiator)
|
||||
self._APP_CRED, application_credential_id, initiator
|
||||
)
|
||||
|
||||
def _delete_application_credentials_for_user(self, user_id,
|
||||
initiator=None):
|
||||
def _delete_application_credentials_for_user(
|
||||
self, user_id, initiator=None
|
||||
):
|
||||
"""Delete all application credentials for a user.
|
||||
|
||||
:param str user_id: User ID
|
||||
@ -214,15 +228,18 @@ class Manager(manager.Manager):
|
||||
This is triggered when a user is deleted.
|
||||
"""
|
||||
app_creds = self.driver.list_application_credentials_for_user(
|
||||
user_id, driver_hints.Hints())
|
||||
user_id, driver_hints.Hints()
|
||||
)
|
||||
self.driver.delete_application_credentials_for_user(user_id)
|
||||
for app_cred in app_creds:
|
||||
self.get_application_credential.invalidate(self, app_cred['id'])
|
||||
notifications.Audit.deleted(self._APP_CRED, app_cred['id'],
|
||||
initiator)
|
||||
notifications.Audit.deleted(
|
||||
self._APP_CRED, app_cred['id'], initiator
|
||||
)
|
||||
|
||||
def _delete_application_credentials_for_user_on_project(self, user_id,
|
||||
project_id):
|
||||
def _delete_application_credentials_for_user_on_project(
|
||||
self, user_id, project_id
|
||||
):
|
||||
"""Delete all application credentials for a user on a given project.
|
||||
|
||||
:param str user_id: User ID
|
||||
@ -233,10 +250,12 @@ class Manager(manager.Manager):
|
||||
hints = driver_hints.Hints()
|
||||
hints.add_filter('project_id', project_id)
|
||||
app_creds = self.driver.list_application_credentials_for_user(
|
||||
user_id, hints)
|
||||
user_id, hints
|
||||
)
|
||||
|
||||
self.driver.delete_application_credentials_for_user_on_project(
|
||||
user_id, project_id)
|
||||
user_id, project_id
|
||||
)
|
||||
for app_cred in app_creds:
|
||||
self.get_application_credential.invalidate(self, app_cred['id'])
|
||||
|
||||
@ -252,7 +271,8 @@ class Manager(manager.Manager):
|
||||
self.driver.delete_access_rule(access_rule_id)
|
||||
self.get_access_rule.invalidate(self, access_rule_id)
|
||||
notifications.Audit.deleted(
|
||||
self._ACCESS_RULE, access_rule_id, initiator)
|
||||
self._ACCESS_RULE, access_rule_id, initiator
|
||||
)
|
||||
|
||||
def _delete_access_rules_for_user(self, user_id, initiator=None):
|
||||
"""Delete all access rules for a user.
|
||||
@ -262,9 +282,11 @@ class Manager(manager.Manager):
|
||||
This is triggered when a user is deleted.
|
||||
"""
|
||||
access_rules = self.driver.list_access_rules_for_user(
|
||||
user_id, driver_hints.Hints())
|
||||
user_id, driver_hints.Hints()
|
||||
)
|
||||
self.driver.delete_access_rules_for_user(user_id)
|
||||
for rule in access_rules:
|
||||
self.get_access_rule.invalidate(self, rule['id'])
|
||||
notifications.Audit.deleted(self._ACCESS_RULE, rule['id'],
|
||||
initiator)
|
||||
notifications.Audit.deleted(
|
||||
self._ACCESS_RULE, rule['id'], initiator
|
||||
)
|
||||
|
@ -21,12 +21,12 @@ _role_properties = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': parameter_types.id_string,
|
||||
'name': parameter_types.name
|
||||
'name': parameter_types.name,
|
||||
},
|
||||
'minProperties': 1,
|
||||
'maxProperties': 1,
|
||||
'additionalProperties': False
|
||||
}
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
_access_rules_properties = {
|
||||
@ -38,36 +38,32 @@ _access_rules_properties = {
|
||||
'type': 'string',
|
||||
'minLength': 0,
|
||||
'maxLength': 225,
|
||||
'pattern': r'^\/.*'
|
||||
'pattern': r'^\/.*',
|
||||
},
|
||||
'method': {
|
||||
'type': 'string',
|
||||
'pattern': r'^(POST|GET|HEAD|PATCH|PUT|DELETE)$'
|
||||
'pattern': r'^(POST|GET|HEAD|PATCH|PUT|DELETE)$',
|
||||
},
|
||||
'service': parameter_types.id_string,
|
||||
'id': parameter_types.id_string,
|
||||
},
|
||||
'additionalProperties': False
|
||||
}
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
_application_credential_properties = {
|
||||
'name': parameter_types.name,
|
||||
'description': validation.nullable(parameter_types.description),
|
||||
'secret': {
|
||||
'type': ['null', 'string']
|
||||
},
|
||||
'expires_at': {
|
||||
'type': ['null', 'string']
|
||||
},
|
||||
'secret': {'type': ['null', 'string']},
|
||||
'expires_at': {'type': ['null', 'string']},
|
||||
'roles': _role_properties,
|
||||
'unrestricted': parameter_types.boolean,
|
||||
'access_rules': _access_rules_properties
|
||||
'access_rules': _access_rules_properties,
|
||||
}
|
||||
|
||||
application_credential_create = {
|
||||
'type': 'object',
|
||||
'properties': _application_credential_properties,
|
||||
'required': ['name'],
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
@ -48,9 +48,15 @@ class AssignmentDriverBase(object, metaclass=abc.ABCMeta):
|
||||
# assignment/grant crud
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_grant(self, role_id, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def create_grant(
|
||||
self,
|
||||
role_id,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
"""Create a new assignment/grant.
|
||||
|
||||
If the assignment is to a domain, then optionally it may be
|
||||
@ -61,16 +67,27 @@ class AssignmentDriverBase(object, metaclass=abc.ABCMeta):
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_grant_role_ids(self, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def list_grant_role_ids(
|
||||
self,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
"""List role ids for assignments/grants."""
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def check_grant_role_id(self, role_id, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def check_grant_role_id(
|
||||
self,
|
||||
role_id,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
"""Check an assignment/grant role id.
|
||||
|
||||
:raises keystone.exception.RoleAssignmentNotFound: If the role
|
||||
@ -81,9 +98,15 @@ class AssignmentDriverBase(object, metaclass=abc.ABCMeta):
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_grant(self, role_id, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def delete_grant(
|
||||
self,
|
||||
role_id,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
"""Delete assignments/grants.
|
||||
|
||||
:raises keystone.exception.RoleAssignmentNotFound: If the role
|
||||
@ -93,10 +116,15 @@ class AssignmentDriverBase(object, metaclass=abc.ABCMeta):
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def list_role_assignments(self, role_id=None,
|
||||
user_id=None, group_ids=None,
|
||||
domain_id=None, project_ids=None,
|
||||
inherited_to_projects=None):
|
||||
def list_role_assignments(
|
||||
self,
|
||||
role_id=None,
|
||||
user_id=None,
|
||||
group_ids=None,
|
||||
domain_id=None,
|
||||
project_ids=None,
|
||||
inherited_to_projects=None,
|
||||
):
|
||||
"""Return a list of role assignments for actors on targets.
|
||||
|
||||
Available parameters represent values in which the returned role
|
||||
@ -144,8 +172,9 @@ class AssignmentDriverBase(object, metaclass=abc.ABCMeta):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_system_grant(self, role_id, actor_id, target_id,
|
||||
assignment_type, inherited):
|
||||
def create_system_grant(
|
||||
self, role_id, actor_id, target_id, assignment_type, inherited
|
||||
):
|
||||
"""Grant a user or group a role on the system.
|
||||
|
||||
:param role_id: the unique ID of the role to grant to the user
|
||||
|
@ -46,27 +46,42 @@ class Assignment(base.AssignmentDriverBase):
|
||||
def default_role_driver(cls):
|
||||
return 'sql'
|
||||
|
||||
def create_grant(self, role_id, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def create_grant(
|
||||
self,
|
||||
role_id,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
|
||||
assignment_type = AssignmentType.calculate_type(
|
||||
user_id, group_id, project_id, domain_id)
|
||||
user_id, group_id, project_id, domain_id
|
||||
)
|
||||
try:
|
||||
with sql.session_for_write() as session:
|
||||
session.add(RoleAssignment(
|
||||
session.add(
|
||||
RoleAssignment(
|
||||
type=assignment_type,
|
||||
actor_id=user_id or group_id,
|
||||
target_id=project_id or domain_id,
|
||||
role_id=role_id,
|
||||
inherited=inherited_to_projects))
|
||||
inherited=inherited_to_projects,
|
||||
)
|
||||
)
|
||||
except sql.DBDuplicateEntry: # nosec : The v3 grant APIs are silent if
|
||||
# the assignment already exists
|
||||
pass
|
||||
|
||||
def list_grant_role_ids(self, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def list_grant_role_ids(
|
||||
self,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
with sql.session_for_read() as session:
|
||||
q = session.query(RoleAssignment.role_id)
|
||||
q = q.filter(RoleAssignment.actor_id == (user_id or group_id))
|
||||
@ -74,62 +89,104 @@ class Assignment(base.AssignmentDriverBase):
|
||||
q = q.filter(RoleAssignment.inherited == inherited_to_projects)
|
||||
return [x.role_id for x in q.all()]
|
||||
|
||||
def _build_grant_filter(self, session, role_id, user_id, group_id,
|
||||
domain_id, project_id, inherited_to_projects):
|
||||
def _build_grant_filter(
|
||||
self,
|
||||
session,
|
||||
role_id,
|
||||
user_id,
|
||||
group_id,
|
||||
domain_id,
|
||||
project_id,
|
||||
inherited_to_projects,
|
||||
):
|
||||
q = session.query(RoleAssignment)
|
||||
q = q.filter_by(actor_id=user_id or group_id)
|
||||
if domain_id:
|
||||
q = q.filter_by(target_id=domain_id).filter(
|
||||
(RoleAssignment.type == AssignmentType.USER_DOMAIN) |
|
||||
(RoleAssignment.type == AssignmentType.GROUP_DOMAIN))
|
||||
(RoleAssignment.type == AssignmentType.USER_DOMAIN)
|
||||
| (RoleAssignment.type == AssignmentType.GROUP_DOMAIN)
|
||||
)
|
||||
else:
|
||||
q = q.filter_by(target_id=project_id).filter(
|
||||
(RoleAssignment.type == AssignmentType.USER_PROJECT) |
|
||||
(RoleAssignment.type == AssignmentType.GROUP_PROJECT))
|
||||
(RoleAssignment.type == AssignmentType.USER_PROJECT)
|
||||
| (RoleAssignment.type == AssignmentType.GROUP_PROJECT)
|
||||
)
|
||||
q = q.filter_by(role_id=role_id)
|
||||
q = q.filter_by(inherited=inherited_to_projects)
|
||||
return q
|
||||
|
||||
def check_grant_role_id(self, role_id, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def check_grant_role_id(
|
||||
self,
|
||||
role_id,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
with sql.session_for_read() as session:
|
||||
try:
|
||||
q = self._build_grant_filter(
|
||||
session, role_id, user_id, group_id, domain_id, project_id,
|
||||
inherited_to_projects)
|
||||
session,
|
||||
role_id,
|
||||
user_id,
|
||||
group_id,
|
||||
domain_id,
|
||||
project_id,
|
||||
inherited_to_projects,
|
||||
)
|
||||
q.one()
|
||||
except sql.NotFound:
|
||||
actor_id = user_id or group_id
|
||||
target_id = domain_id or project_id
|
||||
raise exception.RoleAssignmentNotFound(role_id=role_id,
|
||||
actor_id=actor_id,
|
||||
target_id=target_id)
|
||||
raise exception.RoleAssignmentNotFound(
|
||||
role_id=role_id, actor_id=actor_id, target_id=target_id
|
||||
)
|
||||
|
||||
def delete_grant(self, role_id, user_id=None, group_id=None,
|
||||
domain_id=None, project_id=None,
|
||||
inherited_to_projects=False):
|
||||
def delete_grant(
|
||||
self,
|
||||
role_id,
|
||||
user_id=None,
|
||||
group_id=None,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
inherited_to_projects=False,
|
||||
):
|
||||
with sql.session_for_write() as session:
|
||||
q = self._build_grant_filter(
|
||||
session, role_id, user_id, group_id, domain_id, project_id,
|
||||
inherited_to_projects)
|
||||
session,
|
||||
role_id,
|
||||
user_id,
|
||||
group_id,
|
||||
domain_id,
|
||||
project_id,
|
||||
inherited_to_projects,
|
||||
)
|
||||
if not q.delete(False):
|
||||
actor_id = user_id or group_id
|
||||
target_id = domain_id or project_id
|
||||
raise exception.RoleAssignmentNotFound(role_id=role_id,
|
||||
actor_id=actor_id,
|
||||
target_id=target_id)
|
||||
raise exception.RoleAssignmentNotFound(
|
||||
role_id=role_id, actor_id=actor_id, target_id=target_id
|
||||
)
|
||||
|
||||
def add_role_to_user_and_project(self, user_id, project_id, role_id):
|
||||
try:
|
||||
with sql.session_for_write() as session:
|
||||
session.add(RoleAssignment(
|
||||
session.add(
|
||||
RoleAssignment(
|
||||
type=AssignmentType.USER_PROJECT,
|
||||
actor_id=user_id, target_id=project_id,
|
||||
role_id=role_id, inherited=False))
|
||||
actor_id=user_id,
|
||||
target_id=project_id,
|
||||
role_id=role_id,
|
||||
inherited=False,
|
||||
)
|
||||
)
|
||||
except sql.DBDuplicateEntry:
|
||||
msg = ('User %s already has role %s in tenant %s'
|
||||
% (user_id, role_id, project_id))
|
||||
msg = 'User %s already has role %s in tenant %s' % (
|
||||
user_id,
|
||||
role_id,
|
||||
project_id,
|
||||
)
|
||||
raise exception.Conflict(type='role grant', details=msg)
|
||||
|
||||
def remove_role_from_user_and_project(self, user_id, project_id, role_id):
|
||||
@ -139,9 +196,12 @@ class Assignment(base.AssignmentDriverBase):
|
||||
q = q.filter_by(target_id=project_id)
|
||||
q = q.filter_by(role_id=role_id)
|
||||
if q.delete() == 0:
|
||||
raise exception.RoleNotFound(message=_(
|
||||
'Cannot remove role that has not been granted, %s') %
|
||||
role_id)
|
||||
raise exception.RoleNotFound(
|
||||
message=_(
|
||||
'Cannot remove role that has not been granted, %s'
|
||||
)
|
||||
% role_id
|
||||
)
|
||||
|
||||
def _get_user_assignment_types(self):
|
||||
return [AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN]
|
||||
@ -195,10 +255,15 @@ class Assignment(base.AssignmentDriverBase):
|
||||
|
||||
return actor_types or target_types
|
||||
|
||||
def list_role_assignments(self, role_id=None,
|
||||
user_id=None, group_ids=None,
|
||||
domain_id=None, project_ids=None,
|
||||
inherited_to_projects=None):
|
||||
def list_role_assignments(
|
||||
self,
|
||||
role_id=None,
|
||||
user_id=None,
|
||||
group_ids=None,
|
||||
domain_id=None,
|
||||
project_ids=None,
|
||||
inherited_to_projects=None,
|
||||
):
|
||||
|
||||
def denormalize_role(ref):
|
||||
assignment = {}
|
||||
@ -215,9 +280,10 @@ class Assignment(base.AssignmentDriverBase):
|
||||
assignment['group_id'] = ref.actor_id
|
||||
assignment['domain_id'] = ref.target_id
|
||||
else:
|
||||
raise exception.Error(message=_(
|
||||
'Unexpected assignment type encountered, %s') %
|
||||
ref.type)
|
||||
raise exception.Error(
|
||||
message=_('Unexpected assignment type encountered, %s')
|
||||
% ref.type
|
||||
)
|
||||
assignment['role_id'] = ref.role_id
|
||||
if ref.inherited:
|
||||
assignment['inherited_to_projects'] = 'projects'
|
||||
@ -225,7 +291,8 @@ class Assignment(base.AssignmentDriverBase):
|
||||
|
||||
with sql.session_for_read() as session:
|
||||
assignment_types = self._get_assignment_types(
|
||||
user_id, group_ids, project_ids, domain_id)
|
||||
user_id, group_ids, project_ids, domain_id
|
||||
)
|
||||
|
||||
targets = None
|
||||
if project_ids:
|
||||
@ -258,8 +325,9 @@ class Assignment(base.AssignmentDriverBase):
|
||||
with sql.session_for_write() as session:
|
||||
q = session.query(RoleAssignment)
|
||||
q = q.filter_by(target_id=project_id).filter(
|
||||
RoleAssignment.type.in_((AssignmentType.USER_PROJECT,
|
||||
AssignmentType.GROUP_PROJECT))
|
||||
RoleAssignment.type.in_(
|
||||
(AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT)
|
||||
)
|
||||
)
|
||||
q.delete(False)
|
||||
|
||||
@ -278,16 +346,18 @@ class Assignment(base.AssignmentDriverBase):
|
||||
with sql.session_for_write() as session:
|
||||
q = session.query(RoleAssignment)
|
||||
q = q.filter(RoleAssignment.target_id == domain_id).filter(
|
||||
(RoleAssignment.type == AssignmentType.USER_DOMAIN) |
|
||||
(RoleAssignment.type == AssignmentType.GROUP_DOMAIN))
|
||||
(RoleAssignment.type == AssignmentType.USER_DOMAIN)
|
||||
| (RoleAssignment.type == AssignmentType.GROUP_DOMAIN)
|
||||
)
|
||||
q.delete(False)
|
||||
|
||||
def delete_user_assignments(self, user_id):
|
||||
with sql.session_for_write() as session:
|
||||
q = session.query(RoleAssignment)
|
||||
q = q.filter_by(actor_id=user_id).filter(
|
||||
RoleAssignment.type.in_((AssignmentType.USER_PROJECT,
|
||||
AssignmentType.USER_DOMAIN))
|
||||
RoleAssignment.type.in_(
|
||||
(AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN)
|
||||
)
|
||||
)
|
||||
q.delete(False)
|
||||
|
||||
@ -295,13 +365,15 @@ class Assignment(base.AssignmentDriverBase):
|
||||
with sql.session_for_write() as session:
|
||||
q = session.query(RoleAssignment)
|
||||
q = q.filter_by(actor_id=group_id).filter(
|
||||
RoleAssignment.type.in_((AssignmentType.GROUP_PROJECT,
|
||||
AssignmentType.GROUP_DOMAIN))
|
||||
RoleAssignment.type.in_(
|
||||
(AssignmentType.GROUP_PROJECT, AssignmentType.GROUP_DOMAIN)
|
||||
)
|
||||
)
|
||||
q.delete(False)
|
||||
|
||||
def create_system_grant(self, role_id, actor_id, target_id,
|
||||
assignment_type, inherited):
|
||||
def create_system_grant(
|
||||
self, role_id, actor_id, target_id, assignment_type, inherited
|
||||
):
|
||||
try:
|
||||
with sql.session_for_write() as session:
|
||||
session.add(
|
||||
@ -310,7 +382,7 @@ class Assignment(base.AssignmentDriverBase):
|
||||
actor_id=actor_id,
|
||||
target_id=target_id,
|
||||
role_id=role_id,
|
||||
inherited=inherited
|
||||
inherited=inherited,
|
||||
)
|
||||
)
|
||||
except sql.DBDuplicateEntry: # nosec : The v3 grant APIs are silent if
|
||||
@ -368,17 +440,23 @@ class RoleAssignment(sql.ModelBase, sql.ModelDictMixin):
|
||||
attributes = ['type', 'actor_id', 'target_id', 'role_id', 'inherited']
|
||||
# NOTE(henry-nash): Postgres requires a name to be defined for an Enum
|
||||
type = sql.Column(
|
||||
sql.Enum(AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT,
|
||||
AssignmentType.USER_DOMAIN, AssignmentType.GROUP_DOMAIN,
|
||||
name='type'),
|
||||
nullable=False)
|
||||
sql.Enum(
|
||||
AssignmentType.USER_PROJECT,
|
||||
AssignmentType.GROUP_PROJECT,
|
||||
AssignmentType.USER_DOMAIN,
|
||||
AssignmentType.GROUP_DOMAIN,
|
||||
name='type',
|
||||
),
|
||||
nullable=False,
|
||||
)
|
||||
actor_id = sql.Column(sql.String(64), nullable=False)
|
||||
target_id = sql.Column(sql.String(64), nullable=False)
|
||||
role_id = sql.Column(sql.String(64), nullable=False)
|
||||
inherited = sql.Column(sql.Boolean, default=False, nullable=False)
|
||||
__table_args__ = (
|
||||
sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id',
|
||||
'inherited'),
|
||||
sql.PrimaryKeyConstraint(
|
||||
'type', 'actor_id', 'target_id', 'role_id', 'inherited'
|
||||
),
|
||||
sql.Index('ix_actor_id', 'actor_id'),
|
||||
)
|
||||
|
||||
@ -400,8 +478,9 @@ class SystemRoleAssignment(sql.ModelBase, sql.ModelDictMixin):
|
||||
role_id = sql.Column(sql.String(64), nullable=False)
|
||||
inherited = sql.Column(sql.Boolean, default=False, nullable=False)
|
||||
__table_args__ = (
|
||||
sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id',
|
||||
'inherited'),
|
||||
sql.PrimaryKeyConstraint(
|
||||
'type', 'actor_id', 'target_id', 'role_id', 'inherited'
|
||||
),
|
||||
sql.Index('ix_system_actor_id', 'actor_id'),
|
||||
)
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -41,7 +41,7 @@ class Role(base.RoleDriverBase):
|
||||
# filter_limit_query() below, which will remove the filter from the
|
||||
# hints (hence ensuring our substitution is not exposed to the caller).
|
||||
for f in hints.filters:
|
||||
if (f['name'] == 'domain_id' and f['value'] is None):
|
||||
if f['name'] == 'domain_id' and f['value'] is None:
|
||||
f['value'] = base.NULL_DOMAIN_ID
|
||||
|
||||
with sql.session_for_read() as session:
|
||||
@ -85,11 +85,15 @@ class Role(base.RoleDriverBase):
|
||||
# Move the "_resource_options" attribute over to the real ref
|
||||
# so that resource_options.resource_options_ref_to_mapper can
|
||||
# handle the work.
|
||||
setattr(ref, '_resource_options',
|
||||
getattr(new_role, '_resource_options', {}))
|
||||
setattr(
|
||||
ref,
|
||||
'_resource_options',
|
||||
getattr(new_role, '_resource_options', {}),
|
||||
)
|
||||
# Move options into the propper attribute mapper construct
|
||||
resource_options.resource_options_ref_to_mapper(
|
||||
ref, sql_model.RoleOption)
|
||||
ref, sql_model.RoleOption
|
||||
)
|
||||
return ref.to_dict()
|
||||
|
||||
def delete_role(self, role_id):
|
||||
@ -98,22 +102,28 @@ class Role(base.RoleDriverBase):
|
||||
session.delete(ref)
|
||||
|
||||
def _get_implied_role(self, session, prior_role_id, implied_role_id):
|
||||
query = session.query(sql_model.ImpliedRoleTable).filter(
|
||||
sql_model.ImpliedRoleTable.prior_role_id == prior_role_id).filter(
|
||||
sql_model.ImpliedRoleTable.implied_role_id == implied_role_id)
|
||||
query = (
|
||||
session.query(sql_model.ImpliedRoleTable)
|
||||
.filter(sql_model.ImpliedRoleTable.prior_role_id == prior_role_id)
|
||||
.filter(
|
||||
sql_model.ImpliedRoleTable.implied_role_id == implied_role_id
|
||||
)
|
||||
)
|
||||
try:
|
||||
ref = query.one()
|
||||
except sql.NotFound:
|
||||
raise exception.ImpliedRoleNotFound(
|
||||
prior_role_id=prior_role_id,
|
||||
implied_role_id=implied_role_id)
|
||||
prior_role_id=prior_role_id, implied_role_id=implied_role_id
|
||||
)
|
||||
return ref
|
||||
|
||||
@sql.handle_conflicts(conflict_type='implied_role')
|
||||
def create_implied_role(self, prior_role_id, implied_role_id):
|
||||
with sql.session_for_write() as session:
|
||||
inference = {'prior_role_id': prior_role_id,
|
||||
'implied_role_id': implied_role_id}
|
||||
inference = {
|
||||
'prior_role_id': prior_role_id,
|
||||
'implied_role_id': implied_role_id,
|
||||
}
|
||||
ref = sql_model.ImpliedRoleTable.from_dict(inference)
|
||||
try:
|
||||
session.add(ref)
|
||||
@ -126,15 +136,16 @@ class Role(base.RoleDriverBase):
|
||||
|
||||
def delete_implied_role(self, prior_role_id, implied_role_id):
|
||||
with sql.session_for_write() as session:
|
||||
ref = self._get_implied_role(session, prior_role_id,
|
||||
implied_role_id)
|
||||
ref = self._get_implied_role(
|
||||
session, prior_role_id, implied_role_id
|
||||
)
|
||||
session.delete(ref)
|
||||
|
||||
def list_implied_roles(self, prior_role_id):
|
||||
with sql.session_for_read() as session:
|
||||
query = session.query(
|
||||
sql_model.ImpliedRoleTable).filter(
|
||||
sql_model.ImpliedRoleTable.prior_role_id == prior_role_id)
|
||||
query = session.query(sql_model.ImpliedRoleTable).filter(
|
||||
sql_model.ImpliedRoleTable.prior_role_id == prior_role_id
|
||||
)
|
||||
refs = query.all()
|
||||
return [ref.to_dict() for ref in refs]
|
||||
|
||||
@ -146,6 +157,7 @@ class Role(base.RoleDriverBase):
|
||||
|
||||
def get_implied_role(self, prior_role_id, implied_role_id):
|
||||
with sql.session_for_read() as session:
|
||||
ref = self._get_implied_role(session, prior_role_id,
|
||||
implied_role_id)
|
||||
ref = self._get_implied_role(
|
||||
session, prior_role_id, implied_role_id
|
||||
)
|
||||
return ref.to_dict()
|
||||
|
@ -22,7 +22,8 @@ class RoleTable(sql.ModelBase, sql.ModelDictMixinWithExtras):
|
||||
|
||||
def to_dict(self, include_extra_dict=False):
|
||||
d = super(RoleTable, self).to_dict(
|
||||
include_extra_dict=include_extra_dict)
|
||||
include_extra_dict=include_extra_dict
|
||||
)
|
||||
if d['domain_id'] == base.NULL_DOMAIN_ID:
|
||||
d['domain_id'] = None
|
||||
# NOTE(notmorgan): Eventually it may make sense to drop the empty
|
||||
@ -56,8 +57,9 @@ class RoleTable(sql.ModelBase, sql.ModelDictMixinWithExtras):
|
||||
resource_options_registry = ro.ROLE_OPTIONS_REGISTRY
|
||||
id = sql.Column(sql.String(64), primary_key=True)
|
||||
name = sql.Column(sql.String(255), nullable=False)
|
||||
domain_id = sql.Column(sql.String(64), nullable=False,
|
||||
server_default=base.NULL_DOMAIN_ID)
|
||||
domain_id = sql.Column(
|
||||
sql.String(64), nullable=False, server_default=base.NULL_DOMAIN_ID
|
||||
)
|
||||
description = sql.Column(sql.String(255), nullable=True)
|
||||
extra = sql.Column(sql.JsonBlob())
|
||||
_resource_option_mapper = orm.relationship(
|
||||
@ -66,7 +68,7 @@ class RoleTable(sql.ModelBase, sql.ModelDictMixinWithExtras):
|
||||
cascade='all,delete,delete-orphan',
|
||||
lazy='subquery',
|
||||
backref='role',
|
||||
collection_class=collections.attribute_mapped_collection('option_id')
|
||||
collection_class=collections.attribute_mapped_collection('option_id'),
|
||||
)
|
||||
__table_args__ = (sql.UniqueConstraint('name', 'domain_id'),)
|
||||
|
||||
@ -77,11 +79,13 @@ class ImpliedRoleTable(sql.ModelBase, sql.ModelDictMixin):
|
||||
prior_role_id = sql.Column(
|
||||
sql.String(64),
|
||||
sql.ForeignKey('role.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
primary_key=True,
|
||||
)
|
||||
implied_role_id = sql.Column(
|
||||
sql.String(64),
|
||||
sql.ForeignKey('role.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
primary_key=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dictionary):
|
||||
@ -102,11 +106,13 @@ class ImpliedRoleTable(sql.ModelBase, sql.ModelDictMixin):
|
||||
|
||||
class RoleOption(sql.ModelBase):
|
||||
__tablename__ = 'role_option'
|
||||
role_id = sql.Column(sql.String(64),
|
||||
role_id = sql.Column(
|
||||
sql.String(64),
|
||||
sql.ForeignKey('role.id', ondelete='CASCADE'),
|
||||
nullable=False, primary_key=True)
|
||||
option_id = sql.Column(sql.String(4), nullable=False,
|
||||
primary_key=True)
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
)
|
||||
option_id = sql.Column(sql.String(4), nullable=False, primary_key=True)
|
||||
option_value = sql.Column(sql.JsonBlob, nullable=True)
|
||||
|
||||
def __init__(self, option_id, option_value):
|
||||
|
@ -18,19 +18,19 @@ from keystone.common.validation import parameter_types
|
||||
_role_properties = {
|
||||
'name': parameter_types.name,
|
||||
'description': parameter_types.description,
|
||||
'options': ro.ROLE_OPTIONS_REGISTRY.json_schema
|
||||
'options': ro.ROLE_OPTIONS_REGISTRY.json_schema,
|
||||
}
|
||||
|
||||
role_create = {
|
||||
'type': 'object',
|
||||
'properties': _role_properties,
|
||||
'required': ['name'],
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
role_update = {
|
||||
'type': 'object',
|
||||
'properties': _role_properties,
|
||||
'minProperties': 1,
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
@ -75,9 +75,9 @@ class AuthContext(dict):
|
||||
"""
|
||||
|
||||
# identity attributes need to be reconciled among the auth plugins
|
||||
IDENTITY_ATTRIBUTES = frozenset(['user_id', 'project_id',
|
||||
'access_token_id', 'domain_id',
|
||||
'expires_at'])
|
||||
IDENTITY_ATTRIBUTES = frozenset(
|
||||
['user_id', 'project_id', 'access_token_id', 'domain_id', 'expires_at']
|
||||
)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
"""Override __setitem__ to prevent conflicting values."""
|
||||
@ -87,20 +87,21 @@ class AuthContext(dict):
|
||||
# special treatment for 'expires_at', we are going to take
|
||||
# the earliest expiration instead.
|
||||
if existing_val != val:
|
||||
LOG.info('"expires_at" has conflicting values '
|
||||
LOG.info(
|
||||
'"expires_at" has conflicting values '
|
||||
'%(existing)s and %(new)s. Will use the '
|
||||
'earliest value.',
|
||||
{'existing': existing_val, 'new': val})
|
||||
{'existing': existing_val, 'new': val},
|
||||
)
|
||||
if existing_val is None or val is None:
|
||||
val = existing_val or val
|
||||
else:
|
||||
val = min(existing_val, val)
|
||||
elif existing_val != val:
|
||||
msg = _('Unable to reconcile identity attribute %(attribute)s '
|
||||
'as it has conflicting values %(new)s and %(old)s') % (
|
||||
{'attribute': key,
|
||||
'new': val,
|
||||
'old': existing_val})
|
||||
msg = _(
|
||||
'Unable to reconcile identity attribute %(attribute)s '
|
||||
'as it has conflicting values %(new)s and %(old)s'
|
||||
) % ({'attribute': key, 'new': val, 'old': existing_val})
|
||||
raise exception.Unauthorized(msg)
|
||||
return super(AuthContext, self).__setitem__(key, val)
|
||||
|
||||
@ -141,8 +142,8 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
# ensure the project is enabled
|
||||
try:
|
||||
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:
|
||||
LOG.warning(e)
|
||||
raise exception.Unauthorized from e
|
||||
@ -150,8 +151,8 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
def _assert_domain_is_enabled(self, domain_ref):
|
||||
try:
|
||||
PROVIDERS.resource_api.assert_domain_enabled(
|
||||
domain_id=domain_ref['id'],
|
||||
domain=domain_ref)
|
||||
domain_id=domain_ref['id'], domain=domain_ref
|
||||
)
|
||||
except AssertionError as e:
|
||||
LOG.warning(e)
|
||||
raise exception.Unauthorized from e
|
||||
@ -161,15 +162,19 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
domain_name = domain_info.get('name')
|
||||
try:
|
||||
if domain_name:
|
||||
if (CONF.resource.domain_name_url_safe == 'strict' and
|
||||
utils.is_not_url_safe(domain_name)):
|
||||
if (
|
||||
CONF.resource.domain_name_url_safe == 'strict'
|
||||
and utils.is_not_url_safe(domain_name)
|
||||
):
|
||||
msg = 'Domain name cannot contain reserved characters.'
|
||||
tr_msg = _('Domain name cannot contain reserved '
|
||||
'characters.')
|
||||
tr_msg = _(
|
||||
'Domain name cannot contain reserved ' 'characters.'
|
||||
)
|
||||
LOG.warning(msg)
|
||||
raise exception.Unauthorized(message=tr_msg)
|
||||
domain_ref = PROVIDERS.resource_api.get_domain_by_name(
|
||||
domain_name)
|
||||
domain_name
|
||||
)
|
||||
else:
|
||||
domain_ref = PROVIDERS.resource_api.get_domain(domain_id)
|
||||
except exception.DomainNotFound as e:
|
||||
@ -183,19 +188,24 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
project_name = project_info.get('name')
|
||||
try:
|
||||
if project_name:
|
||||
if (CONF.resource.project_name_url_safe == 'strict' and
|
||||
utils.is_not_url_safe(project_name)):
|
||||
if (
|
||||
CONF.resource.project_name_url_safe == 'strict'
|
||||
and utils.is_not_url_safe(project_name)
|
||||
):
|
||||
msg = 'Project name cannot contain reserved characters.'
|
||||
tr_msg = _('Project name cannot contain reserved '
|
||||
'characters.')
|
||||
tr_msg = _(
|
||||
'Project name cannot contain reserved ' 'characters.'
|
||||
)
|
||||
LOG.warning(msg)
|
||||
raise exception.Unauthorized(message=tr_msg)
|
||||
if 'domain' not in project_info:
|
||||
raise exception.ValidationError(attribute='domain',
|
||||
target='project')
|
||||
raise exception.ValidationError(
|
||||
attribute='domain', target='project'
|
||||
)
|
||||
domain_ref = self._lookup_domain(project_info['domain'])
|
||||
project_ref = PROVIDERS.resource_api.get_project_by_name(
|
||||
project_name, domain_ref['id'])
|
||||
project_name, domain_ref['id']
|
||||
)
|
||||
else:
|
||||
project_ref = PROVIDERS.resource_api.get_project(project_id)
|
||||
domain_id = project_ref['domain_id']
|
||||
@ -214,8 +224,9 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
def _lookup_trust(self, trust_info):
|
||||
trust_id = trust_info.get('id')
|
||||
if not trust_id:
|
||||
raise exception.ValidationError(attribute='trust_id',
|
||||
target='trust')
|
||||
raise exception.ValidationError(
|
||||
attribute='trust_id', target='trust'
|
||||
)
|
||||
trust = PROVIDERS.trust_api.get_trust(trust_id)
|
||||
return trust
|
||||
|
||||
@ -228,25 +239,28 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
return get_app_cred(app_cred_id)
|
||||
name = app_cred_info.get('name')
|
||||
if not name:
|
||||
raise exception.ValidationError(attribute='name or ID',
|
||||
target='application credential')
|
||||
raise exception.ValidationError(
|
||||
attribute='name or ID', target='application credential'
|
||||
)
|
||||
user = app_cred_info.get('user')
|
||||
if not user:
|
||||
raise exception.ValidationError(attribute='user',
|
||||
target='application credential')
|
||||
raise exception.ValidationError(
|
||||
attribute='user', target='application credential'
|
||||
)
|
||||
user_id = user.get('id')
|
||||
if not user_id:
|
||||
if 'domain' not in user:
|
||||
raise exception.ValidationError(attribute='domain',
|
||||
target='user')
|
||||
raise exception.ValidationError(
|
||||
attribute='domain', target='user'
|
||||
)
|
||||
domain_ref = self._lookup_domain(user['domain'])
|
||||
user_id = PROVIDERS.identity_api.get_user_by_name(
|
||||
user['name'], domain_ref['id'])['id']
|
||||
user['name'], domain_ref['id']
|
||||
)['id']
|
||||
hints = driver_hints.Hints()
|
||||
hints.add_filter('name', name)
|
||||
app_cred_api = PROVIDERS.application_credential_api
|
||||
app_creds = app_cred_api.list_application_credentials(
|
||||
user_id, hints)
|
||||
app_creds = app_cred_api.list_application_credentials(user_id, hints)
|
||||
if len(app_creds) != 1:
|
||||
message = "Could not find application credential: %s" % name
|
||||
tr_message = _("Could not find application credential: %s") % name
|
||||
@ -267,17 +281,26 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
if 'scope' in self.auth:
|
||||
detail = "Application credentials cannot request a scope."
|
||||
raise exception.ApplicationCredentialAuthError(
|
||||
detail=detail)
|
||||
detail=detail
|
||||
)
|
||||
self._set_scope_from_app_cred(
|
||||
self.auth['identity']['application_credential'])
|
||||
self.auth['identity']['application_credential']
|
||||
)
|
||||
return
|
||||
if 'scope' not in self.auth:
|
||||
return
|
||||
if sum(['project' in self.auth['scope'],
|
||||
if (
|
||||
sum(
|
||||
[
|
||||
'project' in self.auth['scope'],
|
||||
'domain' in self.auth['scope'],
|
||||
'unscoped' in self.auth['scope'],
|
||||
'system' in self.auth['scope'],
|
||||
'OS-TRUST:trust' in self.auth['scope']]) != 1:
|
||||
'OS-TRUST:trust' in self.auth['scope'],
|
||||
]
|
||||
)
|
||||
!= 1
|
||||
):
|
||||
msg = 'system, project, domain, OS-TRUST:trust or unscoped'
|
||||
raise exception.ValidationError(attribute=msg, target='scope')
|
||||
if 'system' in self.auth['scope']:
|
||||
@ -294,13 +317,19 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
self._scope_data = (domain_ref['id'], None, None, None, None)
|
||||
elif 'OS-TRUST:trust' in self.auth['scope']:
|
||||
trust_ref = self._lookup_trust(
|
||||
self.auth['scope']['OS-TRUST:trust'])
|
||||
self.auth['scope']['OS-TRUST:trust']
|
||||
)
|
||||
# TODO(ayoung): when trusts support domains, fill in domain data
|
||||
if trust_ref.get('project_id') is not None:
|
||||
project_ref = self._lookup_project(
|
||||
{'id': trust_ref['project_id']})
|
||||
{'id': trust_ref['project_id']}
|
||||
)
|
||||
self._scope_data = (
|
||||
None, project_ref['id'], trust_ref, None, None
|
||||
None,
|
||||
project_ref['id'],
|
||||
trust_ref,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
else:
|
||||
@ -310,8 +339,9 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
# make sure all the method data/payload are provided
|
||||
for method_name in self.get_method_names():
|
||||
if method_name not in self.auth['identity']:
|
||||
raise exception.ValidationError(attribute=method_name,
|
||||
target='identity')
|
||||
raise exception.ValidationError(
|
||||
attribute=method_name, target='identity'
|
||||
)
|
||||
|
||||
# make sure auth method is supported
|
||||
for method_name in self.get_method_names():
|
||||
@ -327,8 +357,9 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
"""
|
||||
# make sure "auth" exist
|
||||
if not self.auth:
|
||||
raise exception.ValidationError(attribute='auth',
|
||||
target='request body')
|
||||
raise exception.ValidationError(
|
||||
attribute='auth', target='request body'
|
||||
)
|
||||
|
||||
# NOTE(chioleong): Tokenless auth does not provide auth methods,
|
||||
# we only care about using this method to validate the scope
|
||||
@ -360,8 +391,9 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
|
||||
"""
|
||||
if method not in self.auth['identity']['methods']:
|
||||
raise exception.ValidationError(attribute=method,
|
||||
target='identity')
|
||||
raise exception.ValidationError(
|
||||
attribute=method, target='identity'
|
||||
)
|
||||
return self.auth['identity'][method]
|
||||
|
||||
def get_scope(self):
|
||||
@ -386,8 +418,14 @@ class AuthInfo(provider_api.ProviderAPIMixin, object):
|
||||
"""
|
||||
return self._scope_data
|
||||
|
||||
def set_scope(self, domain_id=None, project_id=None, trust=None,
|
||||
unscoped=None, system=None):
|
||||
def set_scope(
|
||||
self,
|
||||
domain_id=None,
|
||||
project_id=None,
|
||||
trust=None,
|
||||
unscoped=None,
|
||||
system=None,
|
||||
):
|
||||
"""Set scope information."""
|
||||
if domain_id and project_id:
|
||||
msg = _('Scoping to both domain and project is not allowed')
|
||||
@ -430,16 +468,21 @@ class UserMFARulesValidator(provider_api.ProviderAPIMixin, object):
|
||||
user_ref = PROVIDERS.identity_api.get_user(user_id)
|
||||
mfa_rules = user_ref['options'].get(ro.MFA_RULES_OPT.option_name, [])
|
||||
mfa_rules_enabled = user_ref['options'].get(
|
||||
ro.MFA_ENABLED_OPT.option_name, True)
|
||||
ro.MFA_ENABLED_OPT.option_name, True
|
||||
)
|
||||
rules = cls._parse_rule_structure(mfa_rules, user_ref['id'])
|
||||
|
||||
if not rules or not mfa_rules_enabled:
|
||||
# return quickly if the rules are disabled for the user or not set
|
||||
LOG.debug('MFA Rules not processed for user `%(user_id)s`. '
|
||||
LOG.debug(
|
||||
'MFA Rules not processed for user `%(user_id)s`. '
|
||||
'Rule list: `%(rules)s` (Enabled: `%(enabled)s`).',
|
||||
{'user_id': user_id,
|
||||
{
|
||||
'user_id': user_id,
|
||||
'rules': mfa_rules,
|
||||
'enabled': mfa_rules_enabled})
|
||||
'enabled': mfa_rules_enabled,
|
||||
},
|
||||
)
|
||||
return True
|
||||
|
||||
for r in rules:
|
||||
@ -451,20 +494,24 @@ class UserMFARulesValidator(provider_api.ProviderAPIMixin, object):
|
||||
r_set = set(r).intersection(cls._auth_methods())
|
||||
if set(auth_methods).issuperset(r_set):
|
||||
# Rule Matches no need to continue, return here.
|
||||
LOG.debug('Auth methods for user `%(user_id)s`, `%(methods)s` '
|
||||
LOG.debug(
|
||||
'Auth methods for user `%(user_id)s`, `%(methods)s` '
|
||||
'matched MFA rule `%(rule)s`. Loaded '
|
||||
'auth_methods: `%(loaded)s`',
|
||||
{'user_id': user_id,
|
||||
{
|
||||
'user_id': user_id,
|
||||
'rule': list(r_set),
|
||||
'methods': auth_methods,
|
||||
'loaded': cls._auth_methods()})
|
||||
'loaded': cls._auth_methods(),
|
||||
},
|
||||
)
|
||||
return True
|
||||
|
||||
LOG.debug('Auth methods for user `%(user_id)s`, `%(methods)s` did not '
|
||||
LOG.debug(
|
||||
'Auth methods for user `%(user_id)s`, `%(methods)s` did not '
|
||||
'match a MFA rule in `%(rules)s`.',
|
||||
{'user_id': user_id,
|
||||
'methods': auth_methods,
|
||||
'rules': rules})
|
||||
{'user_id': user_id, 'methods': auth_methods, 'rules': rules},
|
||||
)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
@ -488,9 +535,11 @@ class UserMFARulesValidator(provider_api.ProviderAPIMixin, object):
|
||||
# processing.
|
||||
rule_set = []
|
||||
if not isinstance(rules, list):
|
||||
LOG.error('Corrupt rule data structure for user %(user_id)s, '
|
||||
LOG.error(
|
||||
'Corrupt rule data structure for user %(user_id)s, '
|
||||
'no rules loaded.',
|
||||
{'user_id': user_id})
|
||||
{'user_id': user_id},
|
||||
)
|
||||
# Corrupt Data means no rules. Auth success > MFA rules in this
|
||||
# case.
|
||||
return rule_set
|
||||
@ -502,9 +551,11 @@ class UserMFARulesValidator(provider_api.ProviderAPIMixin, object):
|
||||
if not isinstance(r_list, list):
|
||||
# Rule was not a list, it is invalid, drop the rule from
|
||||
# being considered.
|
||||
LOG.info('Ignoring Rule %(type)r; rule must be a list of '
|
||||
LOG.info(
|
||||
'Ignoring Rule %(type)r; rule must be a list of '
|
||||
'strings.',
|
||||
{'type': type(r_list)})
|
||||
{'type': type(r_list)},
|
||||
)
|
||||
continue
|
||||
|
||||
if r_list:
|
||||
@ -514,9 +565,11 @@ class UserMFARulesValidator(provider_api.ProviderAPIMixin, object):
|
||||
if not isinstance(item, str):
|
||||
# Rules may only contain strings for method names
|
||||
# Reject a rule with non-string values
|
||||
LOG.info('Ignoring Rule %(rule)r; rule contains '
|
||||
LOG.info(
|
||||
'Ignoring Rule %(rule)r; rule contains '
|
||||
'non-string values.',
|
||||
{'rule': r_list})
|
||||
{'rule': r_list},
|
||||
)
|
||||
# Rule is known to be bad, drop it from consideration.
|
||||
_ok_rule = False
|
||||
break
|
||||
|
@ -26,16 +26,19 @@ class ApplicationCredential(base.AuthMethodHandler):
|
||||
def authenticate(self, auth_payload):
|
||||
"""Authenticate an application."""
|
||||
response_data = {}
|
||||
app_cred_info = auth_plugins.AppCredInfo.create(auth_payload,
|
||||
METHOD_NAME)
|
||||
app_cred_info = auth_plugins.AppCredInfo.create(
|
||||
auth_payload, METHOD_NAME
|
||||
)
|
||||
|
||||
try:
|
||||
PROVIDERS.application_credential_api.authenticate(
|
||||
application_credential_id=app_cred_info.id,
|
||||
secret=app_cred_info.secret)
|
||||
secret=app_cred_info.secret,
|
||||
)
|
||||
except AssertionError as e:
|
||||
raise exception.Unauthorized(e)
|
||||
response_data['user_id'] = app_cred_info.user_id
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None,
|
||||
response_data=response_data)
|
||||
return base.AuthHandlerResponse(
|
||||
status=True, response_body=None, response_data=response_data
|
||||
)
|
||||
|
@ -20,11 +20,13 @@ from keystone import exception
|
||||
|
||||
|
||||
AuthHandlerResponse = collections.namedtuple(
|
||||
'AuthHandlerResponse', 'status, response_body, response_data')
|
||||
'AuthHandlerResponse', 'status, response_body, response_data'
|
||||
)
|
||||
|
||||
|
||||
class AuthMethodHandler(provider_api.ProviderAPIMixin, object,
|
||||
metaclass=abc.ABCMeta):
|
||||
class AuthMethodHandler(
|
||||
provider_api.ProviderAPIMixin, object, metaclass=abc.ABCMeta
|
||||
):
|
||||
"""Abstract base class for an authentication plugin."""
|
||||
|
||||
def __init__(self):
|
||||
|
@ -28,8 +28,9 @@ CONF = keystone.conf.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
PROVIDERS = provider_api.ProviderAPIs
|
||||
_NOTIFY_OP = 'authenticate'
|
||||
_NOTIFY_EVENT = '{service}.{event}'.format(service=notifications.SERVICE,
|
||||
event=_NOTIFY_OP)
|
||||
_NOTIFY_EVENT = '{service}.{event}'.format(
|
||||
service=notifications.SERVICE, event=_NOTIFY_OP
|
||||
)
|
||||
|
||||
|
||||
def construct_method_map_from_config():
|
||||
@ -115,8 +116,8 @@ class BaseUserInfo(provider_api.ProviderAPIMixin, object):
|
||||
def _assert_domain_is_enabled(self, domain_ref):
|
||||
try:
|
||||
PROVIDERS.resource_api.assert_domain_enabled(
|
||||
domain_id=domain_ref['id'],
|
||||
domain=domain_ref)
|
||||
domain_id=domain_ref['id'], domain=domain_ref
|
||||
)
|
||||
except AssertionError as e:
|
||||
LOG.warning(e)
|
||||
raise exception.Unauthorized from e
|
||||
@ -124,8 +125,8 @@ class BaseUserInfo(provider_api.ProviderAPIMixin, object):
|
||||
def _assert_user_is_enabled(self, user_ref):
|
||||
try:
|
||||
PROVIDERS.identity_api.assert_user_enabled(
|
||||
user_id=user_ref['id'],
|
||||
user=user_ref)
|
||||
user_id=user_ref['id'], user=user_ref
|
||||
)
|
||||
except AssertionError as e:
|
||||
LOG.warning(e)
|
||||
raise exception.Unauthorized from e
|
||||
@ -134,12 +135,14 @@ class BaseUserInfo(provider_api.ProviderAPIMixin, object):
|
||||
domain_id = domain_info.get('id')
|
||||
domain_name = domain_info.get('name')
|
||||
if not domain_id and not domain_name:
|
||||
raise exception.ValidationError(attribute='id or name',
|
||||
target='domain')
|
||||
raise exception.ValidationError(
|
||||
attribute='id or name', target='domain'
|
||||
)
|
||||
try:
|
||||
if domain_name:
|
||||
domain_ref = PROVIDERS.resource_api.get_domain_by_name(
|
||||
domain_name)
|
||||
domain_name
|
||||
)
|
||||
else:
|
||||
domain_ref = PROVIDERS.resource_api.get_domain(domain_id)
|
||||
except exception.DomainNotFound as e:
|
||||
@ -150,27 +153,32 @@ class BaseUserInfo(provider_api.ProviderAPIMixin, object):
|
||||
|
||||
def _validate_and_normalize_auth_data(self, auth_payload):
|
||||
if 'user' not in auth_payload:
|
||||
raise exception.ValidationError(attribute='user',
|
||||
target=self.METHOD_NAME)
|
||||
raise exception.ValidationError(
|
||||
attribute='user', target=self.METHOD_NAME
|
||||
)
|
||||
user_info = auth_payload['user']
|
||||
user_id = user_info.get('id')
|
||||
user_name = user_info.get('name')
|
||||
domain_ref = {}
|
||||
if not user_id and not user_name:
|
||||
raise exception.ValidationError(attribute='id or name',
|
||||
target='user')
|
||||
raise exception.ValidationError(
|
||||
attribute='id or name', target='user'
|
||||
)
|
||||
try:
|
||||
if user_name:
|
||||
if 'domain' not in user_info:
|
||||
raise exception.ValidationError(attribute='domain',
|
||||
target='user')
|
||||
raise exception.ValidationError(
|
||||
attribute='domain', target='user'
|
||||
)
|
||||
domain_ref = self._lookup_domain(user_info['domain'])
|
||||
user_ref = PROVIDERS.identity_api.get_user_by_name(
|
||||
user_name, domain_ref['id'])
|
||||
user_name, domain_ref['id']
|
||||
)
|
||||
else:
|
||||
user_ref = PROVIDERS.identity_api.get_user(user_id)
|
||||
domain_ref = PROVIDERS.resource_api.get_domain(
|
||||
user_ref['domain_id'])
|
||||
user_ref['domain_id']
|
||||
)
|
||||
self._assert_domain_is_enabled(domain_ref)
|
||||
except exception.UserNotFound as e:
|
||||
LOG.warning(e)
|
||||
@ -196,7 +204,8 @@ class BaseUserInfo(provider_api.ProviderAPIMixin, object):
|
||||
outcome=taxonomy.OUTCOME_FAILURE,
|
||||
target=resource.Resource(typeURI=taxonomy.ACCOUNT_USER),
|
||||
event_type=_NOTIFY_EVENT,
|
||||
reason=audit_reason)
|
||||
reason=audit_reason,
|
||||
)
|
||||
raise exception.Unauthorized(e)
|
||||
self._assert_user_is_enabled(user_ref)
|
||||
self.user_ref = user_ref
|
||||
@ -212,7 +221,8 @@ class UserAuthInfo(BaseUserInfo):
|
||||
|
||||
def _validate_and_normalize_auth_data(self, auth_payload):
|
||||
super(UserAuthInfo, self)._validate_and_normalize_auth_data(
|
||||
auth_payload)
|
||||
auth_payload
|
||||
)
|
||||
user_info = auth_payload['user']
|
||||
self.password = user_info.get('password')
|
||||
|
||||
@ -225,7 +235,8 @@ class TOTPUserInfo(BaseUserInfo):
|
||||
|
||||
def _validate_and_normalize_auth_data(self, auth_payload):
|
||||
super(TOTPUserInfo, self)._validate_and_normalize_auth_data(
|
||||
auth_payload)
|
||||
auth_payload
|
||||
)
|
||||
user_info = auth_payload['user']
|
||||
self.passcode = user_info.get('passcode')
|
||||
|
||||
@ -240,23 +251,28 @@ class AppCredInfo(BaseUserInfo):
|
||||
app_cred_api = PROVIDERS.application_credential_api
|
||||
if auth_payload.get('id'):
|
||||
app_cred = app_cred_api.get_application_credential(
|
||||
auth_payload['id'])
|
||||
auth_payload['id']
|
||||
)
|
||||
self.user_id = app_cred['user_id']
|
||||
if not auth_payload.get('user'):
|
||||
auth_payload['user'] = {}
|
||||
auth_payload['user']['id'] = self.user_id
|
||||
super(AppCredInfo, self)._validate_and_normalize_auth_data(
|
||||
auth_payload)
|
||||
auth_payload
|
||||
)
|
||||
elif auth_payload.get('name'):
|
||||
super(AppCredInfo, self)._validate_and_normalize_auth_data(
|
||||
auth_payload)
|
||||
auth_payload
|
||||
)
|
||||
hints = driver_hints.Hints()
|
||||
hints.add_filter('name', auth_payload['name'])
|
||||
app_cred = app_cred_api.list_application_credentials(
|
||||
self.user_id, hints)[0]
|
||||
self.user_id, hints
|
||||
)[0]
|
||||
auth_payload['id'] = app_cred['id']
|
||||
else:
|
||||
raise exception.ValidationError(attribute='id or name',
|
||||
target='application credential')
|
||||
raise exception.ValidationError(
|
||||
attribute='id or name', target='application credential'
|
||||
)
|
||||
self.id = auth_payload['id']
|
||||
self.secret = auth_payload.get('secret')
|
||||
|
@ -48,8 +48,9 @@ class Base(base.AuthMethodHandler, metaclass=abc.ABCMeta):
|
||||
raise exception.Unauthorized(msg)
|
||||
|
||||
response_data['user_id'] = user_ref['id']
|
||||
return base.AuthHandlerResponse(status=True, response_body=None,
|
||||
response_data=response_data)
|
||||
return base.AuthHandlerResponse(
|
||||
status=True, response_body=None, response_data=response_data
|
||||
)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _authenticate(self):
|
||||
@ -64,8 +65,8 @@ class DefaultDomain(Base):
|
||||
def _authenticate(self):
|
||||
"""Use remote_user to look up the user in the identity backend."""
|
||||
return PROVIDERS.identity_api.get_user_by_name(
|
||||
flask.request.remote_user,
|
||||
CONF.identity.default_domain_id)
|
||||
flask.request.remote_user, CONF.identity.default_domain_id
|
||||
)
|
||||
|
||||
|
||||
class Domain(Base):
|
||||
@ -83,7 +84,8 @@ class Domain(Base):
|
||||
domain_id = CONF.identity.default_domain_id
|
||||
|
||||
return PROVIDERS.identity_api.get_user_by_name(
|
||||
flask.request.remote_user, domain_id)
|
||||
flask.request.remote_user, domain_id
|
||||
)
|
||||
|
||||
|
||||
class KerberosDomain(Domain):
|
||||
|
@ -52,19 +52,22 @@ class Mapped(base.AuthMethodHandler):
|
||||
"""
|
||||
if 'id' in auth_payload:
|
||||
token_ref = self._get_token_ref(auth_payload)
|
||||
response_data = handle_scoped_token(token_ref,
|
||||
PROVIDERS.federation_api,
|
||||
PROVIDERS.identity_api)
|
||||
response_data = handle_scoped_token(
|
||||
token_ref, PROVIDERS.federation_api, PROVIDERS.identity_api
|
||||
)
|
||||
else:
|
||||
response_data = handle_unscoped_token(auth_payload,
|
||||
response_data = handle_unscoped_token(
|
||||
auth_payload,
|
||||
PROVIDERS.resource_api,
|
||||
PROVIDERS.federation_api,
|
||||
PROVIDERS.identity_api,
|
||||
PROVIDERS.assignment_api,
|
||||
PROVIDERS.role_api)
|
||||
PROVIDERS.role_api,
|
||||
)
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None,
|
||||
response_data=response_data)
|
||||
return base.AuthHandlerResponse(
|
||||
status=True, response_body=None, response_data=response_data
|
||||
)
|
||||
|
||||
|
||||
def handle_scoped_token(token, federation_api, identity_api):
|
||||
@ -78,15 +81,21 @@ def handle_scoped_token(token, federation_api, identity_api):
|
||||
for group_dict in token.federated_groups:
|
||||
group_ids.append(group_dict['id'])
|
||||
send_notification = functools.partial(
|
||||
notifications.send_saml_audit_notification, 'authenticate',
|
||||
user_id, group_ids, identity_provider, protocol,
|
||||
token_audit_id)
|
||||
notifications.send_saml_audit_notification,
|
||||
'authenticate',
|
||||
user_id,
|
||||
group_ids,
|
||||
identity_provider,
|
||||
protocol,
|
||||
token_audit_id,
|
||||
)
|
||||
|
||||
utils.assert_enabled_identity_provider(federation_api, identity_provider)
|
||||
|
||||
try:
|
||||
mapping = federation_api.get_mapping_from_idp_and_protocol(
|
||||
identity_provider, protocol)
|
||||
identity_provider, protocol
|
||||
)
|
||||
utils.validate_mapped_group_ids(group_ids, mapping['id'], identity_api)
|
||||
|
||||
except Exception:
|
||||
@ -106,8 +115,7 @@ def handle_scoped_token(token, federation_api, identity_api):
|
||||
return response_data
|
||||
|
||||
|
||||
def configure_project_domain(shadow_project, idp_domain_id,
|
||||
resource_api):
|
||||
def configure_project_domain(shadow_project, idp_domain_id, resource_api):
|
||||
"""Configure federated projects domain.
|
||||
|
||||
We set the domain to be the default (idp_domain_id) if the project
|
||||
@ -119,16 +127,23 @@ def configure_project_domain(shadow_project, idp_domain_id,
|
||||
db_domain = resource_api.get_domain_by_name(domain['name'])
|
||||
domain = {"id": db_domain.get('id')}
|
||||
shadow_project['domain'] = domain
|
||||
LOG.debug('Project [%s] domain ID was resolved to [%s]',
|
||||
shadow_project['name'], shadow_project['domain']['id'])
|
||||
LOG.debug(
|
||||
'Project [%s] domain ID was resolved to [%s]',
|
||||
shadow_project['name'],
|
||||
shadow_project['domain']['id'],
|
||||
)
|
||||
|
||||
|
||||
def handle_projects_from_mapping(shadow_projects, idp_domain_id,
|
||||
existing_roles, user, assignment_api,
|
||||
resource_api):
|
||||
def handle_projects_from_mapping(
|
||||
shadow_projects,
|
||||
idp_domain_id,
|
||||
existing_roles,
|
||||
user,
|
||||
assignment_api,
|
||||
resource_api,
|
||||
):
|
||||
for shadow_project in shadow_projects:
|
||||
configure_project_domain(
|
||||
shadow_project, idp_domain_id, resource_api)
|
||||
configure_project_domain(shadow_project, idp_domain_id, resource_api)
|
||||
try:
|
||||
# Check and see if the project already exists and if it
|
||||
# does not, try to create it.
|
||||
@ -139,32 +154,40 @@ def handle_projects_from_mapping(shadow_projects, idp_domain_id,
|
||||
LOG.info(
|
||||
'Project %(project_name)s does not exist. It will be '
|
||||
'automatically provisioning for user %(user_id)s.',
|
||||
{'project_name': shadow_project['name'],
|
||||
'user_id': user['id']}
|
||||
{
|
||||
'project_name': shadow_project['name'],
|
||||
'user_id': user['id'],
|
||||
},
|
||||
)
|
||||
project_ref = {
|
||||
'id': uuid.uuid4().hex,
|
||||
'name': shadow_project['name'],
|
||||
'domain_id': shadow_project['domain']['id']
|
||||
'domain_id': shadow_project['domain']['id'],
|
||||
}
|
||||
project = resource_api.create_project(
|
||||
project_ref['id'],
|
||||
project_ref
|
||||
project_ref['id'], project_ref
|
||||
)
|
||||
shadow_roles = shadow_project['roles']
|
||||
for shadow_role in shadow_roles:
|
||||
assignment_api.create_grant(
|
||||
existing_roles[shadow_role['name']]['id'],
|
||||
user_id=user['id'],
|
||||
project_id=project['id']
|
||||
project_id=project['id'],
|
||||
)
|
||||
|
||||
|
||||
def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
identity_api, assignment_api, role_api):
|
||||
def handle_unscoped_token(
|
||||
auth_payload,
|
||||
resource_api,
|
||||
federation_api,
|
||||
identity_api,
|
||||
assignment_api,
|
||||
role_api,
|
||||
):
|
||||
|
||||
def validate_shadow_mapping(shadow_projects, existing_roles,
|
||||
user_domain_id, idp_id):
|
||||
def validate_shadow_mapping(
|
||||
shadow_projects, existing_roles, user_domain_id, idp_id
|
||||
):
|
||||
# Validate that the roles in the shadow mapping actually exist. If
|
||||
# they don't we should bail early before creating anything.
|
||||
for shadow_project in shadow_projects:
|
||||
@ -176,30 +199,35 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
'Role %s was specified in the mapping but does '
|
||||
'not exist. All roles specified in a mapping must '
|
||||
'exist before assignment.',
|
||||
shadow_role['name']
|
||||
shadow_role['name'],
|
||||
)
|
||||
# NOTE(lbragstad): The RoleNotFound exception usually
|
||||
# expects a role_id as the parameter, but in this case we
|
||||
# only have a name so we'll pass that instead.
|
||||
raise exception.RoleNotFound(shadow_role['name'])
|
||||
role = existing_roles[shadow_role['name']]
|
||||
if (role['domain_id'] is not None and
|
||||
role['domain_id'] != user_domain_id):
|
||||
if (
|
||||
role['domain_id'] is not None
|
||||
and role['domain_id'] != user_domain_id
|
||||
):
|
||||
LOG.error(
|
||||
'Role %(role)s is a domain-specific role and '
|
||||
'cannot be assigned within %(domain)s.',
|
||||
{'role': shadow_role['name'], 'domain': user_domain_id}
|
||||
{
|
||||
'role': shadow_role['name'],
|
||||
'domain': user_domain_id,
|
||||
},
|
||||
)
|
||||
raise exception.DomainSpecificRoleNotWithinIdPDomain(
|
||||
role_name=shadow_role['name'],
|
||||
identity_provider=idp_id
|
||||
role_name=shadow_role['name'], identity_provider=idp_id
|
||||
)
|
||||
|
||||
def is_ephemeral_user(mapped_properties):
|
||||
return mapped_properties['user']['type'] == utils.UserType.EPHEMERAL
|
||||
|
||||
def build_ephemeral_user_context(user, mapped_properties,
|
||||
identity_provider, protocol):
|
||||
def build_ephemeral_user_context(
|
||||
user, mapped_properties, identity_provider, protocol
|
||||
):
|
||||
resp = {}
|
||||
resp['user_id'] = user['id']
|
||||
resp['group_ids'] = mapped_properties['group_ids']
|
||||
@ -210,8 +238,9 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
|
||||
def build_local_user_context(mapped_properties):
|
||||
resp = {}
|
||||
user_info = auth_plugins.UserAuthInfo.create(mapped_properties,
|
||||
METHOD_NAME)
|
||||
user_info = auth_plugins.UserAuthInfo.create(
|
||||
mapped_properties, METHOD_NAME
|
||||
)
|
||||
resp['user_id'] = user_info.user_id
|
||||
|
||||
return resp
|
||||
@ -221,12 +250,12 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
identity_provider = auth_payload['identity_provider']
|
||||
except KeyError:
|
||||
raise exception.ValidationError(
|
||||
attribute='identity_provider', target='mapped')
|
||||
attribute='identity_provider', target='mapped'
|
||||
)
|
||||
try:
|
||||
protocol = auth_payload['protocol']
|
||||
except KeyError:
|
||||
raise exception.ValidationError(
|
||||
attribute='protocol', target='mapped')
|
||||
raise exception.ValidationError(attribute='protocol', target='mapped')
|
||||
|
||||
utils.assert_enabled_identity_provider(federation_api, identity_provider)
|
||||
|
||||
@ -242,24 +271,33 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
try:
|
||||
try:
|
||||
mapped_properties, mapping_id = apply_mapping_filter(
|
||||
identity_provider, protocol, assertion, resource_api,
|
||||
federation_api, identity_api)
|
||||
identity_provider,
|
||||
protocol,
|
||||
assertion,
|
||||
resource_api,
|
||||
federation_api,
|
||||
identity_api,
|
||||
)
|
||||
except exception.ValidationError as e:
|
||||
# if mapping is either invalid or yield no valid identity,
|
||||
# it is considered a failed authentication
|
||||
raise exception.Unauthorized(e)
|
||||
|
||||
if is_ephemeral_user(mapped_properties):
|
||||
idp_domain_id = federation_api.get_idp(
|
||||
identity_provider)['domain_id']
|
||||
idp_domain_id = federation_api.get_idp(identity_provider)[
|
||||
'domain_id'
|
||||
]
|
||||
|
||||
validate_and_prepare_federated_user(mapped_properties,
|
||||
idp_domain_id, resource_api)
|
||||
validate_and_prepare_federated_user(
|
||||
mapped_properties, idp_domain_id, resource_api
|
||||
)
|
||||
|
||||
user = identity_api.shadow_federated_user(
|
||||
identity_provider,
|
||||
protocol, mapped_properties['user'],
|
||||
group_ids=mapped_properties['group_ids'])
|
||||
protocol,
|
||||
mapped_properties['user'],
|
||||
group_ids=mapped_properties['group_ids'],
|
||||
)
|
||||
|
||||
if 'projects' in mapped_properties:
|
||||
|
||||
@ -276,7 +314,7 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
mapped_properties['projects'],
|
||||
existing_roles,
|
||||
mapped_properties['user']['domain']['id'],
|
||||
identity_provider
|
||||
identity_provider,
|
||||
)
|
||||
handle_projects_from_mapping(
|
||||
mapped_properties['projects'],
|
||||
@ -284,13 +322,14 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
existing_roles,
|
||||
user,
|
||||
assignment_api,
|
||||
resource_api
|
||||
resource_api,
|
||||
)
|
||||
|
||||
user_id = user['id']
|
||||
group_ids = mapped_properties['group_ids']
|
||||
response_data = build_ephemeral_user_context(
|
||||
user, mapped_properties, identity_provider, protocol)
|
||||
user, mapped_properties, identity_provider, protocol
|
||||
)
|
||||
else:
|
||||
response_data = build_local_user_context(mapped_properties)
|
||||
|
||||
@ -299,19 +338,27 @@ def handle_unscoped_token(auth_payload, resource_api, federation_api,
|
||||
# send off failed authentication notification, raise the exception
|
||||
# after sending the notification
|
||||
outcome = taxonomy.OUTCOME_FAILURE
|
||||
notifications.send_saml_audit_notification('authenticate',
|
||||
user_id, group_ids,
|
||||
notifications.send_saml_audit_notification(
|
||||
'authenticate',
|
||||
user_id,
|
||||
group_ids,
|
||||
identity_provider,
|
||||
protocol, token_id,
|
||||
outcome)
|
||||
protocol,
|
||||
token_id,
|
||||
outcome,
|
||||
)
|
||||
raise
|
||||
else:
|
||||
outcome = taxonomy.OUTCOME_SUCCESS
|
||||
notifications.send_saml_audit_notification('authenticate',
|
||||
user_id, group_ids,
|
||||
notifications.send_saml_audit_notification(
|
||||
'authenticate',
|
||||
user_id,
|
||||
group_ids,
|
||||
identity_provider,
|
||||
protocol, token_id,
|
||||
outcome)
|
||||
protocol,
|
||||
token_id,
|
||||
outcome,
|
||||
)
|
||||
|
||||
return response_data
|
||||
|
||||
@ -321,13 +368,20 @@ def extract_assertion_data():
|
||||
return assertion
|
||||
|
||||
|
||||
def apply_mapping_filter(identity_provider, protocol, assertion,
|
||||
resource_api, federation_api, identity_api):
|
||||
def apply_mapping_filter(
|
||||
identity_provider,
|
||||
protocol,
|
||||
assertion,
|
||||
resource_api,
|
||||
federation_api,
|
||||
identity_api,
|
||||
):
|
||||
idp = federation_api.get_idp(identity_provider)
|
||||
utils.validate_idp(idp, protocol, assertion)
|
||||
|
||||
mapped_properties, mapping_id = federation_api.evaluate(
|
||||
identity_provider, protocol, assertion)
|
||||
identity_provider, protocol, assertion
|
||||
)
|
||||
|
||||
# NOTE(marek-denis): We update group_ids only here to avoid fetching
|
||||
# groups identified by name/domain twice.
|
||||
@ -339,14 +393,19 @@ def apply_mapping_filter(identity_provider, protocol, assertion,
|
||||
utils.validate_mapped_group_ids(group_ids, mapping_id, identity_api)
|
||||
group_ids.extend(
|
||||
utils.transform_to_group_ids(
|
||||
mapped_properties['group_names'], mapping_id,
|
||||
identity_api, resource_api))
|
||||
mapped_properties['group_names'],
|
||||
mapping_id,
|
||||
identity_api,
|
||||
resource_api,
|
||||
)
|
||||
)
|
||||
mapped_properties['group_ids'] = list(set(group_ids))
|
||||
return mapped_properties, mapping_id
|
||||
|
||||
|
||||
def validate_and_prepare_federated_user(
|
||||
mapped_properties, idp_domain_id, resource_api):
|
||||
mapped_properties, idp_domain_id, resource_api
|
||||
):
|
||||
"""Setup federated username.
|
||||
|
||||
Function covers all the cases for properly setting user id, a primary
|
||||
@ -386,9 +445,11 @@ def validate_and_prepare_federated_user(
|
||||
user_name = user.get('name') or flask.request.remote_user
|
||||
|
||||
if not any([user_id, user_name]):
|
||||
msg = _("Could not map user while setting ephemeral user identity. "
|
||||
msg = _(
|
||||
"Could not map user while setting ephemeral user identity. "
|
||||
"Either mapping rules must specify user id/name or "
|
||||
"REMOTE_USER environment variable must be set.")
|
||||
"REMOTE_USER environment variable must be set."
|
||||
)
|
||||
raise exception.Unauthorized(msg)
|
||||
|
||||
elif not user_name:
|
||||
@ -408,5 +469,8 @@ def validate_and_prepare_federated_user(
|
||||
domain = {"id": db_domain.get('id')}
|
||||
|
||||
user['domain'] = domain
|
||||
LOG.debug('User [%s] domain ID was resolved to [%s]', user['name'],
|
||||
user['domain']['id'])
|
||||
LOG.debug(
|
||||
'User [%s] domain ID was resolved to [%s]',
|
||||
user['name'],
|
||||
user['domain']['id'],
|
||||
)
|
||||
|
@ -36,7 +36,8 @@ class OAuth(base.AuthMethodHandler):
|
||||
|
||||
if not access_token_id:
|
||||
raise exception.ValidationError(
|
||||
attribute='oauth_token', target='request')
|
||||
attribute='oauth_token', target='request'
|
||||
)
|
||||
|
||||
acc_token = PROVIDERS.oauth_api.get_access_token(access_token_id)
|
||||
|
||||
@ -44,20 +45,22 @@ class OAuth(base.AuthMethodHandler):
|
||||
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(_('Access token is expired'))
|
||||
|
||||
url = ks_flask.base_url(path=flask.request.path)
|
||||
access_verifier = oauth.ResourceEndpoint(
|
||||
request_validator=validator.OAuthValidator(),
|
||||
token_generator=oauth.token_generator)
|
||||
token_generator=oauth.token_generator,
|
||||
)
|
||||
result, request = access_verifier.validate_protected_resource_request(
|
||||
url,
|
||||
http_method='POST',
|
||||
body=flask.request.args,
|
||||
headers=dict(flask.request.headers),
|
||||
realms=None
|
||||
realms=None,
|
||||
)
|
||||
if not result:
|
||||
msg = _('Could not validate the access token')
|
||||
@ -66,5 +69,6 @@ class OAuth(base.AuthMethodHandler):
|
||||
response_data['access_token_id'] = access_token_id
|
||||
response_data['project_id'] = acc_token['project_id']
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None,
|
||||
response_data=response_data)
|
||||
return base.AuthHandlerResponse(
|
||||
status=True, response_body=None, response_data=response_data
|
||||
)
|
||||
|
@ -32,8 +32,8 @@ class Password(base.AuthMethodHandler):
|
||||
|
||||
try:
|
||||
PROVIDERS.identity_api.authenticate(
|
||||
user_id=user_info.user_id,
|
||||
password=user_info.password)
|
||||
user_id=user_info.user_id, password=user_info.password
|
||||
)
|
||||
except AssertionError:
|
||||
# authentication failed because of invalid username or password
|
||||
msg = _('Invalid username or password')
|
||||
@ -41,5 +41,6 @@ class Password(base.AuthMethodHandler):
|
||||
|
||||
response_data['user_id'] = user_info.user_id
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None,
|
||||
response_data=response_data)
|
||||
return base.AuthHandlerResponse(
|
||||
status=True, response_body=None, response_data=response_data
|
||||
)
|
||||
|
@ -37,13 +37,11 @@ class Token(base.AuthMethodHandler):
|
||||
|
||||
def authenticate(self, auth_payload):
|
||||
if 'id' not in auth_payload:
|
||||
raise exception.ValidationError(attribute='id',
|
||||
target='token')
|
||||
raise exception.ValidationError(attribute='id', target='token')
|
||||
token = self._get_token_ref(auth_payload)
|
||||
if token.is_federated and PROVIDERS.federation_api:
|
||||
response_data = mapped.handle_scoped_token(
|
||||
token, PROVIDERS.federation_api,
|
||||
PROVIDERS.identity_api
|
||||
token, PROVIDERS.federation_api, PROVIDERS.identity_api
|
||||
)
|
||||
else:
|
||||
response_data = token_authenticate(token)
|
||||
@ -54,8 +52,9 @@ class Token(base.AuthMethodHandler):
|
||||
# AuthMethodHandlers do no such thing and this is not required.
|
||||
response_data.setdefault('method_names', []).extend(token.methods)
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None,
|
||||
response_data=response_data)
|
||||
return base.AuthHandlerResponse(
|
||||
status=True, response_body=None, response_data=response_data
|
||||
)
|
||||
|
||||
|
||||
def token_authenticate(token):
|
||||
@ -68,23 +67,23 @@ def token_authenticate(token):
|
||||
# privilege attacks
|
||||
|
||||
json_body = flask.request.get_json(silent=True, force=True) or {}
|
||||
project_scoped = 'project' in json_body['auth'].get(
|
||||
'scope', {}
|
||||
)
|
||||
domain_scoped = 'domain' in json_body['auth'].get(
|
||||
'scope', {}
|
||||
)
|
||||
project_scoped = 'project' in json_body['auth'].get('scope', {})
|
||||
domain_scoped = 'domain' in json_body['auth'].get('scope', {})
|
||||
|
||||
if token.oauth_scoped:
|
||||
raise exception.ForbiddenAction(
|
||||
action=_(
|
||||
'Using OAuth-scoped token to create another token. '
|
||||
'Create a new OAuth-scoped token instead'))
|
||||
'Create a new OAuth-scoped token instead'
|
||||
)
|
||||
)
|
||||
elif token.trust_scoped:
|
||||
raise exception.ForbiddenAction(
|
||||
action=_(
|
||||
'Using trust-scoped token to create another token. '
|
||||
'Create a new trust-scoped token instead'))
|
||||
'Create a new trust-scoped token instead'
|
||||
)
|
||||
)
|
||||
elif token.system_scoped and (project_scoped or domain_scoped):
|
||||
raise exception.ForbiddenAction(
|
||||
action=_(
|
||||
@ -97,7 +96,8 @@ def token_authenticate(token):
|
||||
# Do not allow conversion from scoped tokens.
|
||||
if token.project_scoped or token.domain_scoped:
|
||||
raise exception.ForbiddenAction(
|
||||
action=_('rescope a scoped token'))
|
||||
action=_('rescope a scoped token')
|
||||
)
|
||||
|
||||
# New tokens maintain the audit_id of the original token in the
|
||||
# chain (if possible) as the second element in the audit data
|
||||
|
@ -72,8 +72,12 @@ def _generate_totp_passcodes(secret, included_previous_windows=0):
|
||||
# HMAC-SHA1 when generating the TOTP, which is currently not insecure but
|
||||
# will still trigger when scanned by bandit.
|
||||
totp = crypto_totp.TOTP(
|
||||
decoded, PASSCODE_LENGTH, hashes.SHA1(), PASSCODE_TIME_PERIOD, # nosec
|
||||
backend=default_backend())
|
||||
decoded,
|
||||
PASSCODE_LENGTH,
|
||||
hashes.SHA1(), # nosec
|
||||
PASSCODE_TIME_PERIOD,
|
||||
backend=default_backend(),
|
||||
)
|
||||
|
||||
passcode_ts = timeutils.utcnow_ts(microsecond=True)
|
||||
passcodes = [totp.generate(passcode_ts).decode('utf-8')]
|
||||
@ -95,22 +99,29 @@ class TOTP(base.AuthMethodHandler):
|
||||
auth_passcode = auth_payload.get('user').get('passcode')
|
||||
|
||||
credentials = PROVIDERS.credential_api.list_credentials_for_user(
|
||||
user_info.user_id, type='totp')
|
||||
user_info.user_id, type='totp'
|
||||
)
|
||||
|
||||
valid_passcode = False
|
||||
for credential in credentials:
|
||||
try:
|
||||
generated_passcodes = _generate_totp_passcodes(
|
||||
credential['blob'], CONF.totp.included_previous_windows)
|
||||
credential['blob'], CONF.totp.included_previous_windows
|
||||
)
|
||||
if auth_passcode in generated_passcodes:
|
||||
valid_passcode = True
|
||||
break
|
||||
except (ValueError, KeyError):
|
||||
LOG.debug('No TOTP match; credential id: %s, user_id: %s',
|
||||
credential['id'], user_info.user_id)
|
||||
except (TypeError):
|
||||
LOG.debug('Base32 decode failed for TOTP credential %s',
|
||||
credential['id'])
|
||||
LOG.debug(
|
||||
'No TOTP match; credential id: %s, user_id: %s',
|
||||
credential['id'],
|
||||
user_info.user_id,
|
||||
)
|
||||
except TypeError:
|
||||
LOG.debug(
|
||||
'Base32 decode failed for TOTP credential %s',
|
||||
credential['id'],
|
||||
)
|
||||
|
||||
if not valid_passcode:
|
||||
# authentication failed because of invalid username or passcode
|
||||
@ -119,5 +130,6 @@ class TOTP(base.AuthMethodHandler):
|
||||
|
||||
response_data['user_id'] = user_info.user_id
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None,
|
||||
response_data=response_data)
|
||||
return base.AuthHandlerResponse(
|
||||
status=True, response_body=None, response_data=response_data
|
||||
)
|
||||
|
@ -24,7 +24,9 @@ token_issue = {
|
||||
'properties': {
|
||||
'methods': {
|
||||
'type': 'array',
|
||||
'items': {'type': 'string', },
|
||||
'items': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'password': {
|
||||
'type': 'object',
|
||||
@ -32,14 +34,24 @@ token_issue = {
|
||||
'user': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'string', },
|
||||
'name': {'type': 'string', },
|
||||
'password': {'type': 'string', },
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'name': {
|
||||
'type': 'string',
|
||||
},
|
||||
'password': {
|
||||
'type': 'string',
|
||||
},
|
||||
'domain': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'string', },
|
||||
'name': {'type': 'string', },
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'name': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -53,10 +65,14 @@ token_issue = {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
'required': ['id', ],
|
||||
'required': [
|
||||
'id',
|
||||
],
|
||||
},
|
||||
},
|
||||
'required': ['methods', ],
|
||||
'required': [
|
||||
'methods',
|
||||
],
|
||||
},
|
||||
'scope': {
|
||||
# For explicit unscoped authentication the type should not be
|
||||
@ -70,13 +86,21 @@ token_issue = {
|
||||
'project': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {'type': 'string', },
|
||||
'id': {'type': 'string', },
|
||||
'name': {
|
||||
'type': 'string',
|
||||
},
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'domain': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'string', },
|
||||
'name': {'type': 'string', },
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'name': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -84,26 +108,32 @@ token_issue = {
|
||||
'domain': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'string', },
|
||||
'name': {'type': 'string', },
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
'name': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
'OS-TRUST:trust': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': {'type': 'string', },
|
||||
}
|
||||
'id': {
|
||||
'type': 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
'system': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'all': parameter_types.boolean
|
||||
}
|
||||
}
|
||||
'properties': {'all': parameter_types.boolean},
|
||||
},
|
||||
},
|
||||
},
|
||||
'required': ['identity', ],
|
||||
},
|
||||
'required': [
|
||||
'identity',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@ -115,8 +145,10 @@ def validate_issue_token_auth(auth=None):
|
||||
user = auth['identity'].get('password', {}).get('user')
|
||||
if user is not None:
|
||||
if 'id' not in user and 'name' not in user:
|
||||
msg = _('Invalid input for field identity/password/user: '
|
||||
'id or name must be present.')
|
||||
msg = _(
|
||||
'Invalid input for field identity/password/user: '
|
||||
'id or name must be present.'
|
||||
)
|
||||
raise exception.SchemaValidationError(detail=msg)
|
||||
|
||||
domain = user.get('domain')
|
||||
@ -124,7 +156,8 @@ def validate_issue_token_auth(auth=None):
|
||||
if 'id' not in domain and 'name' not in domain:
|
||||
msg = _(
|
||||
'Invalid input for field identity/password/user/domain: '
|
||||
'id or name must be present.')
|
||||
'id or name must be present.'
|
||||
)
|
||||
raise exception.SchemaValidationError(detail=msg)
|
||||
|
||||
scope = auth.get('scope')
|
||||
@ -134,19 +167,22 @@ def validate_issue_token_auth(auth=None):
|
||||
if 'id' not in project and 'name' not in project:
|
||||
msg = _(
|
||||
'Invalid input for field scope/project: '
|
||||
'id or name must be present.')
|
||||
'id or name must be present.'
|
||||
)
|
||||
raise exception.SchemaValidationError(detail=msg)
|
||||
domain = project.get('domain')
|
||||
if domain is not None:
|
||||
if 'id' not in domain and 'name' not in domain:
|
||||
msg = _(
|
||||
'Invalid input for field scope/project/domain: '
|
||||
'id or name must be present.')
|
||||
'id or name must be present.'
|
||||
)
|
||||
raise exception.SchemaValidationError(detail=msg)
|
||||
domain = scope.get('domain')
|
||||
if domain is not None:
|
||||
if 'id' not in domain and 'name' not in domain:
|
||||
msg = _(
|
||||
'Invalid input for field scope/domain: '
|
||||
'id or name must be present.')
|
||||
'id or name must be present.'
|
||||
)
|
||||
raise exception.SchemaValidationError(detail=msg)
|
||||
|
@ -22,8 +22,9 @@ from keystone import exception
|
||||
CONF = keystone.conf.CONF
|
||||
|
||||
|
||||
class CatalogDriverBase(provider_api.ProviderAPIMixin, object,
|
||||
metaclass=abc.ABCMeta):
|
||||
class CatalogDriverBase(
|
||||
provider_api.ProviderAPIMixin, object, metaclass=abc.ABCMeta
|
||||
):
|
||||
"""Interface description for the Catalog driver."""
|
||||
|
||||
def _get_list_limit(self):
|
||||
@ -41,7 +42,8 @@ class CatalogDriverBase(provider_api.ProviderAPIMixin, object,
|
||||
# self circle
|
||||
if parent_region_id == root_region_id:
|
||||
raise exception.CircularRegionHierarchyError(
|
||||
parent_region_id=parent_region_id)
|
||||
parent_region_id=parent_region_id
|
||||
)
|
||||
parent_region = self.get_region(parent_region_id)
|
||||
parent_region_id = parent_region.get('parent_region_id')
|
||||
|
||||
@ -470,8 +472,9 @@ class CatalogDriverBase(provider_api.ProviderAPIMixin, object,
|
||||
raise exception.NotImplemented() # pragma: no cover
|
||||
|
||||
@abc.abstractmethod
|
||||
def remove_endpoint_group_from_project(self, endpoint_group_id,
|
||||
project_id):
|
||||
def remove_endpoint_group_from_project(
|
||||
self, endpoint_group_id, project_id
|
||||
):
|
||||
"""Remove an endpoint to project association.
|
||||
|
||||
:param endpoint_group_id: identity of endpoint to associate
|
||||
|
@ -52,30 +52,46 @@ class Service(sql.ModelBase, sql.ModelDictMixinWithExtras):
|
||||
attributes = ['id', 'type', 'enabled']
|
||||
id = sql.Column(sql.String(64), primary_key=True)
|
||||
type = sql.Column(sql.String(255))
|
||||
enabled = sql.Column(sql.Boolean, nullable=False, default=True,
|
||||
server_default=sqlalchemy.sql.expression.true())
|
||||
enabled = sql.Column(
|
||||
sql.Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
server_default=sqlalchemy.sql.expression.true(),
|
||||
)
|
||||
extra = sql.Column(sql.JsonBlob())
|
||||
endpoints = sqlalchemy.orm.relationship("Endpoint", backref="service")
|
||||
|
||||
|
||||
class Endpoint(sql.ModelBase, sql.ModelDictMixinWithExtras):
|
||||
__tablename__ = 'endpoint'
|
||||
attributes = ['id', 'interface', 'region_id', 'service_id', 'url',
|
||||
'legacy_endpoint_id', 'enabled']
|
||||
attributes = [
|
||||
'id',
|
||||
'interface',
|
||||
'region_id',
|
||||
'service_id',
|
||||
'url',
|
||||
'legacy_endpoint_id',
|
||||
'enabled',
|
||||
]
|
||||
id = sql.Column(sql.String(64), primary_key=True)
|
||||
legacy_endpoint_id = sql.Column(sql.String(64))
|
||||
interface = sql.Column(sql.String(8), nullable=False)
|
||||
region_id = sql.Column(sql.String(255),
|
||||
sql.ForeignKey('region.id',
|
||||
ondelete='RESTRICT'),
|
||||
region_id = sql.Column(
|
||||
sql.String(255),
|
||||
sql.ForeignKey('region.id', ondelete='RESTRICT'),
|
||||
nullable=True,
|
||||
default=None)
|
||||
service_id = sql.Column(sql.String(64),
|
||||
sql.ForeignKey('service.id'),
|
||||
nullable=False)
|
||||
default=None,
|
||||
)
|
||||
service_id = sql.Column(
|
||||
sql.String(64), sql.ForeignKey('service.id'), nullable=False
|
||||
)
|
||||
url = sql.Column(sql.Text(), nullable=False)
|
||||
enabled = sql.Column(sql.Boolean, nullable=False, default=True,
|
||||
server_default=sqlalchemy.sql.expression.true())
|
||||
enabled = sql.Column(
|
||||
sql.Boolean,
|
||||
nullable=False,
|
||||
default=True,
|
||||
server_default=sqlalchemy.sql.expression.true(),
|
||||
)
|
||||
extra = sql.Column(sql.JsonBlob())
|
||||
|
||||
@classmethod
|
||||
@ -277,17 +293,19 @@ class Catalog(base.CatalogDriverBase):
|
||||
substitutions.update({'user_id': user_id})
|
||||
silent_keyerror_failures = []
|
||||
if project_id:
|
||||
substitutions.update({
|
||||
'tenant_id': project_id,
|
||||
'project_id': project_id
|
||||
})
|
||||
substitutions.update(
|
||||
{'tenant_id': project_id, 'project_id': project_id}
|
||||
)
|
||||
else:
|
||||
silent_keyerror_failures = ['tenant_id', 'project_id']
|
||||
|
||||
with sql.session_for_read() as session:
|
||||
endpoints = (session.query(Endpoint).
|
||||
options(sql.joinedload(Endpoint.service)).
|
||||
filter(Endpoint.enabled == true()).all())
|
||||
endpoints = (
|
||||
session.query(Endpoint)
|
||||
.options(sql.joinedload(Endpoint.service))
|
||||
.filter(Endpoint.enabled == true())
|
||||
.all()
|
||||
)
|
||||
|
||||
catalog = {}
|
||||
|
||||
@ -296,8 +314,10 @@ class Catalog(base.CatalogDriverBase):
|
||||
continue
|
||||
try:
|
||||
formatted_url = utils.format_url(
|
||||
endpoint['url'], substitutions,
|
||||
silent_keyerror_failures=silent_keyerror_failures)
|
||||
endpoint['url'],
|
||||
substitutions,
|
||||
silent_keyerror_failures=silent_keyerror_failures,
|
||||
)
|
||||
if formatted_url is not None:
|
||||
url = formatted_url
|
||||
else:
|
||||
@ -310,7 +330,7 @@ class Catalog(base.CatalogDriverBase):
|
||||
default_service = {
|
||||
'id': endpoint['id'],
|
||||
'name': endpoint.service.extra.get('name', ''),
|
||||
'publicURL': ''
|
||||
'publicURL': '',
|
||||
}
|
||||
catalog.setdefault(region, {})
|
||||
catalog[region].setdefault(service_type, default_service)
|
||||
@ -336,29 +356,37 @@ class Catalog(base.CatalogDriverBase):
|
||||
d.update({'user_id': user_id})
|
||||
silent_keyerror_failures = []
|
||||
if project_id:
|
||||
d.update({
|
||||
d.update(
|
||||
{
|
||||
'tenant_id': project_id,
|
||||
'project_id': project_id,
|
||||
})
|
||||
}
|
||||
)
|
||||
else:
|
||||
silent_keyerror_failures = ['tenant_id', 'project_id']
|
||||
|
||||
with sql.session_for_read() as session:
|
||||
services = (session.query(Service).filter(
|
||||
Service.enabled == true()).options(
|
||||
sql.joinedload(Service.endpoints)).all())
|
||||
services = (
|
||||
session.query(Service)
|
||||
.filter(Service.enabled == true())
|
||||
.options(sql.joinedload(Service.endpoints))
|
||||
.all()
|
||||
)
|
||||
|
||||
def make_v3_endpoints(endpoints):
|
||||
for endpoint in (ep.to_dict()
|
||||
for ep in endpoints if ep.enabled):
|
||||
for endpoint in (
|
||||
ep.to_dict() for ep in endpoints if ep.enabled
|
||||
):
|
||||
del endpoint['service_id']
|
||||
del endpoint['legacy_endpoint_id']
|
||||
del endpoint['enabled']
|
||||
endpoint['region'] = endpoint['region_id']
|
||||
try:
|
||||
formatted_url = utils.format_url(
|
||||
endpoint['url'], d,
|
||||
silent_keyerror_failures=silent_keyerror_failures)
|
||||
endpoint['url'],
|
||||
d,
|
||||
silent_keyerror_failures=silent_keyerror_failures,
|
||||
)
|
||||
if formatted_url:
|
||||
endpoint['url'] = formatted_url
|
||||
else:
|
||||
@ -388,7 +416,8 @@ class Catalog(base.CatalogDriverBase):
|
||||
filtered_endpoints = {}
|
||||
if project_id:
|
||||
filtered_endpoints = (
|
||||
self.catalog_api.list_endpoints_for_project(project_id))
|
||||
self.catalog_api.list_endpoints_for_project(project_id)
|
||||
)
|
||||
# endpoint filter is enabled, only return the filtered endpoints.
|
||||
if filtered_endpoints:
|
||||
filtered_ids = list(filtered_endpoints.keys())
|
||||
@ -428,18 +457,21 @@ class Catalog(base.CatalogDriverBase):
|
||||
@sql.handle_conflicts(conflict_type='project_endpoint')
|
||||
def add_endpoint_to_project(self, endpoint_id, project_id):
|
||||
with sql.session_for_write() as session:
|
||||
endpoint_filter_ref = ProjectEndpoint(endpoint_id=endpoint_id,
|
||||
project_id=project_id)
|
||||
endpoint_filter_ref = ProjectEndpoint(
|
||||
endpoint_id=endpoint_id, project_id=project_id
|
||||
)
|
||||
session.add(endpoint_filter_ref)
|
||||
|
||||
def _get_project_endpoint_ref(self, session, endpoint_id, project_id):
|
||||
endpoint_filter_ref = session.get(
|
||||
ProjectEndpoint, (endpoint_id, project_id),
|
||||
ProjectEndpoint,
|
||||
(endpoint_id, project_id),
|
||||
)
|
||||
if endpoint_filter_ref is None:
|
||||
msg = _('Endpoint %(endpoint_id)s not found in project '
|
||||
'%(project_id)s') % {'endpoint_id': endpoint_id,
|
||||
'project_id': project_id}
|
||||
msg = _(
|
||||
'Endpoint %(endpoint_id)s not found in project '
|
||||
'%(project_id)s'
|
||||
) % {'endpoint_id': endpoint_id, 'project_id': project_id}
|
||||
raise exception.NotFound(msg)
|
||||
return endpoint_filter_ref
|
||||
|
||||
@ -450,7 +482,8 @@ class Catalog(base.CatalogDriverBase):
|
||||
def remove_endpoint_from_project(self, endpoint_id, project_id):
|
||||
with sql.session_for_write() as session:
|
||||
endpoint_filter_ref = self._get_project_endpoint_ref(
|
||||
session, endpoint_id, project_id)
|
||||
session, endpoint_id, project_id
|
||||
)
|
||||
session.delete(endpoint_filter_ref)
|
||||
|
||||
def list_endpoints_for_project(self, project_id):
|
||||
@ -489,40 +522,46 @@ class Catalog(base.CatalogDriverBase):
|
||||
endpoint_group_ref = session.get(EndpointGroup, endpoint_group_id)
|
||||
if endpoint_group_ref is None:
|
||||
raise exception.EndpointGroupNotFound(
|
||||
endpoint_group_id=endpoint_group_id)
|
||||
endpoint_group_id=endpoint_group_id
|
||||
)
|
||||
return endpoint_group_ref
|
||||
|
||||
def get_endpoint_group(self, endpoint_group_id):
|
||||
with sql.session_for_read() as session:
|
||||
endpoint_group_ref = self._get_endpoint_group(session,
|
||||
endpoint_group_id)
|
||||
endpoint_group_ref = self._get_endpoint_group(
|
||||
session, endpoint_group_id
|
||||
)
|
||||
return endpoint_group_ref.to_dict()
|
||||
|
||||
def update_endpoint_group(self, endpoint_group_id, endpoint_group):
|
||||
with sql.session_for_write() as session:
|
||||
endpoint_group_ref = self._get_endpoint_group(session,
|
||||
endpoint_group_id)
|
||||
endpoint_group_ref = self._get_endpoint_group(
|
||||
session, endpoint_group_id
|
||||
)
|
||||
old_endpoint_group = endpoint_group_ref.to_dict()
|
||||
old_endpoint_group.update(endpoint_group)
|
||||
new_endpoint_group = EndpointGroup.from_dict(old_endpoint_group)
|
||||
for attr in EndpointGroup.mutable_attributes:
|
||||
setattr(endpoint_group_ref, attr,
|
||||
getattr(new_endpoint_group, attr))
|
||||
setattr(
|
||||
endpoint_group_ref, attr, getattr(new_endpoint_group, attr)
|
||||
)
|
||||
return endpoint_group_ref.to_dict()
|
||||
|
||||
def delete_endpoint_group(self, endpoint_group_id):
|
||||
with sql.session_for_write() as session:
|
||||
endpoint_group_ref = self._get_endpoint_group(session,
|
||||
endpoint_group_id)
|
||||
endpoint_group_ref = self._get_endpoint_group(
|
||||
session, endpoint_group_id
|
||||
)
|
||||
self._delete_endpoint_group_association_by_endpoint_group(
|
||||
session, endpoint_group_id)
|
||||
session, endpoint_group_id
|
||||
)
|
||||
session.delete(endpoint_group_ref)
|
||||
|
||||
def get_endpoint_group_in_project(self, endpoint_group_id, project_id):
|
||||
with sql.session_for_read() as session:
|
||||
ref = self._get_endpoint_group_in_project(session,
|
||||
endpoint_group_id,
|
||||
project_id)
|
||||
ref = self._get_endpoint_group_in_project(
|
||||
session, endpoint_group_id, project_id
|
||||
)
|
||||
return ref.to_dict()
|
||||
|
||||
@sql.handle_conflicts(conflict_type='project_endpoint_group')
|
||||
@ -530,13 +569,16 @@ class Catalog(base.CatalogDriverBase):
|
||||
with sql.session_for_write() as session:
|
||||
# Create a new Project Endpoint group entity
|
||||
endpoint_group_project_ref = ProjectEndpointGroupMembership(
|
||||
endpoint_group_id=endpoint_group_id, project_id=project_id)
|
||||
endpoint_group_id=endpoint_group_id, project_id=project_id
|
||||
)
|
||||
session.add(endpoint_group_project_ref)
|
||||
|
||||
def _get_endpoint_group_in_project(self, session,
|
||||
endpoint_group_id, project_id):
|
||||
def _get_endpoint_group_in_project(
|
||||
self, session, endpoint_group_id, project_id
|
||||
):
|
||||
endpoint_group_project_ref = session.get(
|
||||
ProjectEndpointGroupMembership, (endpoint_group_id, project_id),
|
||||
ProjectEndpointGroupMembership,
|
||||
(endpoint_group_id, project_id),
|
||||
)
|
||||
if endpoint_group_project_ref is None:
|
||||
msg = _('Endpoint Group Project Association not found')
|
||||
@ -548,7 +590,8 @@ class Catalog(base.CatalogDriverBase):
|
||||
with sql.session_for_read() as session:
|
||||
query = session.query(EndpointGroup)
|
||||
endpoint_group_refs = sql.filter_limit_query(
|
||||
EndpointGroup, query, hints)
|
||||
EndpointGroup, query, hints
|
||||
)
|
||||
return [e.to_dict() for e in endpoint_group_refs]
|
||||
|
||||
def list_endpoint_groups_for_project(self, project_id):
|
||||
@ -558,11 +601,13 @@ class Catalog(base.CatalogDriverBase):
|
||||
endpoint_group_refs = query.all()
|
||||
return [ref.to_dict() for ref in endpoint_group_refs]
|
||||
|
||||
def remove_endpoint_group_from_project(self, endpoint_group_id,
|
||||
project_id):
|
||||
def remove_endpoint_group_from_project(
|
||||
self, endpoint_group_id, project_id
|
||||
):
|
||||
with sql.session_for_write() as session:
|
||||
endpoint_group_project_ref = self._get_endpoint_group_in_project(
|
||||
session, endpoint_group_id, project_id)
|
||||
session, endpoint_group_id, project_id
|
||||
)
|
||||
session.delete(endpoint_group_project_ref)
|
||||
|
||||
def list_projects_associated_with_endpoint_group(self, endpoint_group_id):
|
||||
@ -573,7 +618,8 @@ class Catalog(base.CatalogDriverBase):
|
||||
return [ref.to_dict() for ref in endpoint_group_refs]
|
||||
|
||||
def _delete_endpoint_group_association_by_endpoint_group(
|
||||
self, session, endpoint_group_id):
|
||||
self, session, endpoint_group_id
|
||||
):
|
||||
query = session.query(ProjectEndpointGroupMembership)
|
||||
query = query.filter_by(endpoint_group_id=endpoint_group_id)
|
||||
query.delete()
|
||||
@ -590,12 +636,8 @@ class ProjectEndpoint(sql.ModelBase, sql.ModelDictMixin):
|
||||
|
||||
__tablename__ = 'project_endpoint'
|
||||
attributes = ['endpoint_id', 'project_id']
|
||||
endpoint_id = sql.Column(sql.String(64),
|
||||
primary_key=True,
|
||||
nullable=False)
|
||||
project_id = sql.Column(sql.String(64),
|
||||
primary_key=True,
|
||||
nullable=False)
|
||||
endpoint_id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||
project_id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class EndpointGroup(sql.ModelBase, sql.ModelDictMixin):
|
||||
@ -615,9 +657,10 @@ class ProjectEndpointGroupMembership(sql.ModelBase, sql.ModelDictMixin):
|
||||
|
||||
__tablename__ = 'project_endpoint_group'
|
||||
attributes = ['endpoint_group_id', 'project_id']
|
||||
endpoint_group_id = sql.Column(sql.String(64),
|
||||
sql.ForeignKey('endpoint_group.id'),
|
||||
nullable=False)
|
||||
endpoint_group_id = sql.Column(
|
||||
sql.String(64), sql.ForeignKey('endpoint_group.id'), nullable=False
|
||||
)
|
||||
project_id = sql.Column(sql.String(64), nullable=False)
|
||||
__table_args__ = (sql.PrimaryKeyConstraint('endpoint_group_id',
|
||||
'project_id'),)
|
||||
__table_args__ = (
|
||||
sql.PrimaryKeyConstraint('endpoint_group_id', 'project_id'),
|
||||
)
|
||||
|
@ -83,8 +83,10 @@ class Catalog(base.CatalogDriverBase):
|
||||
|
||||
def __init__(self, templates=None):
|
||||
super(Catalog, self).__init__()
|
||||
LOG.warning('The templated catalog driver has been deprecated and '
|
||||
'will be removed in a future release.')
|
||||
LOG.warning(
|
||||
'The templated catalog driver has been deprecated and '
|
||||
'will be removed in a future release.'
|
||||
)
|
||||
if templates:
|
||||
self.templates = templates
|
||||
else:
|
||||
@ -107,8 +109,10 @@ class Catalog(base.CatalogDriverBase):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_regions(self, hints):
|
||||
return [{'id': region_id, 'description': '', 'parent_region_id': ''}
|
||||
for region_id in self.templates]
|
||||
return [
|
||||
{'id': region_id, 'description': '', 'parent_region_id': ''}
|
||||
for region_id in self.templates
|
||||
]
|
||||
|
||||
def get_region(self, region_id):
|
||||
if region_id in self.templates:
|
||||
@ -163,8 +167,11 @@ class Catalog(base.CatalogDriverBase):
|
||||
for key in service_ref:
|
||||
if key.endswith('URL'):
|
||||
interface = key[:-3]
|
||||
endpoint_id = ('%s-%s-%s' %
|
||||
(region_id, service_type, interface))
|
||||
endpoint_id = '%s-%s-%s' % (
|
||||
region_id,
|
||||
service_type,
|
||||
interface,
|
||||
)
|
||||
yield {
|
||||
'id': endpoint_id,
|
||||
'service_id': service_type,
|
||||
@ -208,12 +215,17 @@ class Catalog(base.CatalogDriverBase):
|
||||
substitutions.update({'user_id': user_id})
|
||||
silent_keyerror_failures = []
|
||||
if project_id:
|
||||
substitutions.update({
|
||||
substitutions.update(
|
||||
{
|
||||
'tenant_id': project_id,
|
||||
'project_id': project_id,
|
||||
})
|
||||
}
|
||||
)
|
||||
else:
|
||||
silent_keyerror_failures = ['tenant_id', 'project_id', ]
|
||||
silent_keyerror_failures = [
|
||||
'tenant_id',
|
||||
'project_id',
|
||||
]
|
||||
|
||||
catalog = {}
|
||||
# TODO(davechen): If there is service with no endpoints, we should
|
||||
@ -226,8 +238,10 @@ class Catalog(base.CatalogDriverBase):
|
||||
try:
|
||||
for k, v in service_ref.items():
|
||||
formatted_value = utils.format_url(
|
||||
v, substitutions,
|
||||
silent_keyerror_failures=silent_keyerror_failures)
|
||||
v,
|
||||
substitutions,
|
||||
silent_keyerror_failures=silent_keyerror_failures,
|
||||
)
|
||||
if formatted_value:
|
||||
service_data[k] = formatted_value
|
||||
except exception.MalformedEndpoint: # nosec(tkelsey)
|
||||
@ -259,7 +273,7 @@ class Catalog(base.CatalogDriverBase):
|
||||
if service_type not in v3_catalog:
|
||||
v3_catalog[service_type] = {
|
||||
'type': service_type,
|
||||
'endpoints': []
|
||||
'endpoints': [],
|
||||
}
|
||||
|
||||
for attr, value in service.items():
|
||||
@ -271,11 +285,13 @@ class Catalog(base.CatalogDriverBase):
|
||||
# 'region: '<region_name>' }
|
||||
if attr.endswith('URL'):
|
||||
v3_interface = attr[: -len('URL')]
|
||||
v3_catalog[service_type]['endpoints'].append({
|
||||
v3_catalog[service_type]['endpoints'].append(
|
||||
{
|
||||
'interface': v3_interface,
|
||||
'region': region_name,
|
||||
'url': value,
|
||||
})
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
# Other attributes are copied to the service.
|
||||
@ -331,8 +347,9 @@ class Catalog(base.CatalogDriverBase):
|
||||
def list_projects_associated_with_endpoint_group(self, endpoint_group_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def remove_endpoint_group_from_project(self, endpoint_group_id,
|
||||
project_id):
|
||||
def remove_endpoint_group_from_project(
|
||||
self, endpoint_group_id, project_id
|
||||
):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def delete_endpoint_group_association_by_project(self, project_id):
|
||||
|
@ -38,8 +38,8 @@ MEMOIZE = cache.get_memoization_decorator(group='catalog')
|
||||
# entire cache region.
|
||||
COMPUTED_CATALOG_REGION = cache.create_region(name='computed catalog region')
|
||||
MEMOIZE_COMPUTED_CATALOG = cache.get_memoization_decorator(
|
||||
group='catalog',
|
||||
region=COMPUTED_CATALOG_REGION)
|
||||
group='catalog', region=COMPUTED_CATALOG_REGION
|
||||
)
|
||||
|
||||
|
||||
class Manager(manager.Manager):
|
||||
@ -60,23 +60,31 @@ class Manager(manager.Manager):
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.catalog.driver)
|
||||
notifications.register_event_callback(
|
||||
notifications.ACTIONS.deleted, 'project',
|
||||
self._on_project_or_endpoint_delete)
|
||||
notifications.ACTIONS.deleted,
|
||||
'project',
|
||||
self._on_project_or_endpoint_delete,
|
||||
)
|
||||
notifications.register_event_callback(
|
||||
notifications.ACTIONS.deleted, 'endpoint',
|
||||
self._on_project_or_endpoint_delete)
|
||||
notifications.ACTIONS.deleted,
|
||||
'endpoint',
|
||||
self._on_project_or_endpoint_delete,
|
||||
)
|
||||
|
||||
def _on_project_or_endpoint_delete(self, service, resource_type, operation,
|
||||
payload):
|
||||
def _on_project_or_endpoint_delete(
|
||||
self, service, resource_type, operation, payload
|
||||
):
|
||||
project_or_endpoint_id = payload['resource_info']
|
||||
if resource_type == 'project':
|
||||
PROVIDERS.catalog_api.delete_association_by_project(
|
||||
project_or_endpoint_id)
|
||||
project_or_endpoint_id
|
||||
)
|
||||
PROVIDERS.catalog_api.delete_endpoint_group_association_by_project(
|
||||
project_or_endpoint_id)
|
||||
project_or_endpoint_id
|
||||
)
|
||||
else:
|
||||
PROVIDERS.catalog_api.delete_association_by_endpoint(
|
||||
project_or_endpoint_id)
|
||||
project_or_endpoint_id
|
||||
)
|
||||
|
||||
def create_region(self, region_ref, initiator=None):
|
||||
# Check duplicate ID
|
||||
@ -183,16 +191,18 @@ class Manager(manager.Manager):
|
||||
if region_id is not None:
|
||||
self.get_region(region_id)
|
||||
except exception.RegionNotFound:
|
||||
raise exception.ValidationError(attribute='endpoint region_id',
|
||||
target='region table')
|
||||
raise exception.ValidationError(
|
||||
attribute='endpoint region_id', target='region table'
|
||||
)
|
||||
|
||||
def _assert_service_exists(self, service_id):
|
||||
try:
|
||||
if service_id is not None:
|
||||
self.get_service(service_id)
|
||||
except exception.ServiceNotFound:
|
||||
raise exception.ValidationError(attribute='endpoint service_id',
|
||||
target='service table')
|
||||
raise exception.ValidationError(
|
||||
attribute='endpoint service_id', target='service table'
|
||||
)
|
||||
|
||||
def create_endpoint(self, endpoint_id, endpoint_ref, initiator=None):
|
||||
self._assert_region_exists(endpoint_ref.get('region_id'))
|
||||
@ -247,19 +257,23 @@ class Manager(manager.Manager):
|
||||
|
||||
def add_endpoint_group_to_project(self, endpoint_group_id, project_id):
|
||||
self.driver.add_endpoint_group_to_project(
|
||||
endpoint_group_id, project_id)
|
||||
endpoint_group_id, project_id
|
||||
)
|
||||
COMPUTED_CATALOG_REGION.invalidate()
|
||||
|
||||
def remove_endpoint_group_from_project(self, endpoint_group_id,
|
||||
project_id):
|
||||
def remove_endpoint_group_from_project(
|
||||
self, endpoint_group_id, project_id
|
||||
):
|
||||
self.driver.remove_endpoint_group_from_project(
|
||||
endpoint_group_id, project_id)
|
||||
endpoint_group_id, project_id
|
||||
)
|
||||
COMPUTED_CATALOG_REGION.invalidate()
|
||||
|
||||
def delete_endpoint_group_association_by_project(self, project_id):
|
||||
try:
|
||||
self.driver.delete_endpoint_group_association_by_project(
|
||||
project_id)
|
||||
project_id
|
||||
)
|
||||
except exception.NotImplemented:
|
||||
# Some catalog drivers don't support this
|
||||
pass
|
||||
@ -270,8 +284,10 @@ class Manager(manager.Manager):
|
||||
PROVIDERS.resource_api.get_project(project_id)
|
||||
try:
|
||||
refs = self.list_endpoint_groups_for_project(project_id)
|
||||
endpoint_groups = [self.get_endpoint_group(
|
||||
ref['endpoint_group_id']) for ref in refs]
|
||||
endpoint_groups = [
|
||||
self.get_endpoint_group(ref['endpoint_group_id'])
|
||||
for ref in refs
|
||||
]
|
||||
return endpoint_groups
|
||||
except exception.EndpointGroupNotFound:
|
||||
return []
|
||||
@ -307,15 +323,17 @@ class Manager(manager.Manager):
|
||||
filtered_endpoints.update({ref['endpoint_id']: endpoint})
|
||||
except exception.EndpointNotFound:
|
||||
# remove bad reference from association
|
||||
self.remove_endpoint_from_project(ref['endpoint_id'],
|
||||
project_id)
|
||||
self.remove_endpoint_from_project(
|
||||
ref['endpoint_id'], project_id
|
||||
)
|
||||
|
||||
# need to recover endpoint_groups associated with project
|
||||
# then for each endpoint group return the endpoints.
|
||||
endpoint_groups = self.get_endpoint_groups_for_project(project_id)
|
||||
for endpoint_group in endpoint_groups:
|
||||
endpoint_refs = self.get_endpoints_filtered_by_endpoint_group(
|
||||
endpoint_group['id'])
|
||||
endpoint_group['id']
|
||||
)
|
||||
# now check if any endpoints for current endpoint group are not
|
||||
# contained in the list of filtered endpoints
|
||||
for endpoint_ref in endpoint_refs:
|
||||
|
@ -13,28 +13,20 @@
|
||||
from keystone.common import validation
|
||||
from keystone.common.validation import parameter_types
|
||||
|
||||
_service_properties_type = {
|
||||
'type': 'string',
|
||||
'minLength': 1,
|
||||
'maxLength': 255
|
||||
}
|
||||
_service_properties_type = {'type': 'string', 'minLength': 1, 'maxLength': 255}
|
||||
|
||||
_region_properties = {
|
||||
'description': validation.nullable(parameter_types.description),
|
||||
# NOTE(lbragstad): Regions use ID differently. The user can specify the ID
|
||||
# or it will be generated automatically.
|
||||
'id': {
|
||||
'type': 'string'
|
||||
},
|
||||
'parent_region_id': {
|
||||
'type': ['string', 'null']
|
||||
}
|
||||
'id': {'type': 'string'},
|
||||
'parent_region_id': {'type': ['string', 'null']},
|
||||
}
|
||||
|
||||
region_create = {
|
||||
'type': 'object',
|
||||
'properties': _region_properties,
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
# NOTE(lbragstad): No parameters are required for creating regions.
|
||||
}
|
||||
|
||||
@ -42,7 +34,7 @@ region_update = {
|
||||
'type': 'object',
|
||||
'properties': _region_properties,
|
||||
'minProperties': 1,
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# Schema for Service v3
|
||||
@ -50,71 +42,60 @@ region_update = {
|
||||
_service_properties = {
|
||||
'enabled': parameter_types.boolean,
|
||||
'name': parameter_types.name,
|
||||
'type': _service_properties_type
|
||||
'type': _service_properties_type,
|
||||
}
|
||||
|
||||
service_create = {
|
||||
'type': 'object',
|
||||
'properties': _service_properties,
|
||||
'required': ['type'],
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
service_update = {
|
||||
'type': 'object',
|
||||
'properties': _service_properties,
|
||||
'minProperties': 1,
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
_endpoint_properties = {
|
||||
'enabled': parameter_types.boolean,
|
||||
'interface': {
|
||||
'type': 'string',
|
||||
'enum': ['admin', 'internal', 'public']
|
||||
},
|
||||
'region_id': {
|
||||
'type': 'string'
|
||||
},
|
||||
'region': {
|
||||
'type': 'string'
|
||||
},
|
||||
'service_id': {
|
||||
'type': 'string'
|
||||
},
|
||||
'url': parameter_types.url
|
||||
'interface': {'type': 'string', 'enum': ['admin', 'internal', 'public']},
|
||||
'region_id': {'type': 'string'},
|
||||
'region': {'type': 'string'},
|
||||
'service_id': {'type': 'string'},
|
||||
'url': parameter_types.url,
|
||||
}
|
||||
|
||||
endpoint_create = {
|
||||
'type': 'object',
|
||||
'properties': _endpoint_properties,
|
||||
'required': ['interface', 'service_id', 'url'],
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
endpoint_update = {
|
||||
'type': 'object',
|
||||
'properties': _endpoint_properties,
|
||||
'minProperties': 1,
|
||||
'additionalProperties': True
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
_endpoint_group_properties = {
|
||||
'description': validation.nullable(parameter_types.description),
|
||||
'filters': {
|
||||
'type': 'object'
|
||||
},
|
||||
'name': parameter_types.name
|
||||
'filters': {'type': 'object'},
|
||||
'name': parameter_types.name,
|
||||
}
|
||||
|
||||
endpoint_group_create = {
|
||||
'type': 'object',
|
||||
'properties': _endpoint_group_properties,
|
||||
'required': ['name', 'filters']
|
||||
'required': ['name', 'filters'],
|
||||
}
|
||||
|
||||
endpoint_group_update = {
|
||||
'type': 'object',
|
||||
'properties': _endpoint_group_properties,
|
||||
'minProperties': 1
|
||||
'minProperties': 1,
|
||||
}
|
||||
|
@ -84,17 +84,19 @@ class Bootstrapper(object):
|
||||
'id': CONF.identity.default_domain_id,
|
||||
'name': 'Default',
|
||||
'enabled': True,
|
||||
'description': 'The default domain'
|
||||
'description': 'The default domain',
|
||||
}
|
||||
try:
|
||||
PROVIDERS.resource_api.create_domain(
|
||||
domain_id=default_domain['id'],
|
||||
domain=default_domain)
|
||||
domain_id=default_domain['id'], domain=default_domain
|
||||
)
|
||||
LOG.info('Created domain %s', default_domain['id'])
|
||||
except exception.Conflict:
|
||||
# NOTE(morganfainberg): Domain already exists, continue on.
|
||||
LOG.info('Domain %s already exists, skipping creation.',
|
||||
default_domain['id'])
|
||||
LOG.info(
|
||||
'Domain %s already exists, skipping creation.',
|
||||
default_domain['id'],
|
||||
)
|
||||
|
||||
self.default_domain_id = default_domain['id']
|
||||
|
||||
@ -106,13 +108,15 @@ class Bootstrapper(object):
|
||||
'id': project_id,
|
||||
'domain_id': self.default_domain_id,
|
||||
'description': 'Bootstrap project for initializing the cloud.',
|
||||
'name': self.project_name
|
||||
'name': self.project_name,
|
||||
}
|
||||
PROVIDERS.resource_api.create_project(project_id, project)
|
||||
LOG.info('Created project %s', self.project_name)
|
||||
except exception.Conflict:
|
||||
LOG.info('Project %s already exists, skipping creation.',
|
||||
self.project_name)
|
||||
LOG.info(
|
||||
'Project %s already exists, skipping creation.',
|
||||
self.project_name,
|
||||
)
|
||||
project = PROVIDERS.resource_api.get_project_by_name(
|
||||
self.project_name, self.default_domain_id
|
||||
)
|
||||
@ -129,11 +133,14 @@ class Bootstrapper(object):
|
||||
role = PROVIDERS.role_api.create_role(role_id, role)
|
||||
LOG.info('Created role %s', role_name)
|
||||
if not self.immutable_roles:
|
||||
LOG.warning("Role %(role)s was created as a mutable role. It "
|
||||
LOG.warning(
|
||||
"Role %(role)s was created as a mutable role. It "
|
||||
"is recommended to make this role immutable by "
|
||||
"adding the 'immutable' resource option to this "
|
||||
"role, or re-running this command without "
|
||||
"--no-immutable-role.", {'role': role_name})
|
||||
"--no-immutable-role.",
|
||||
{'role': role_name},
|
||||
)
|
||||
return role
|
||||
except exception.Conflict:
|
||||
LOG.info('Role %s exists, skipping creation.', role_name)
|
||||
@ -154,18 +161,19 @@ class Bootstrapper(object):
|
||||
|
||||
def _ensure_implied_role(self, prior_role_id, implied_role_id):
|
||||
try:
|
||||
PROVIDERS.role_api.create_implied_role(prior_role_id,
|
||||
implied_role_id)
|
||||
PROVIDERS.role_api.create_implied_role(
|
||||
prior_role_id, implied_role_id
|
||||
)
|
||||
LOG.info(
|
||||
'Created implied role where %s implies %s',
|
||||
prior_role_id,
|
||||
implied_role_id
|
||||
implied_role_id,
|
||||
)
|
||||
except exception.Conflict:
|
||||
LOG.info(
|
||||
'Implied role where %s implies %s exists, skipping creation.',
|
||||
prior_role_id,
|
||||
implied_role_id
|
||||
implied_role_id,
|
||||
)
|
||||
|
||||
def _bootstrap_service_role(self):
|
||||
@ -194,8 +202,9 @@ class Bootstrapper(object):
|
||||
# "manager" role, so we need to clean up the old admin -> member
|
||||
# implied role
|
||||
try:
|
||||
PROVIDERS.role_api.delete_implied_role(self.admin_role_id,
|
||||
self.member_role_id)
|
||||
PROVIDERS.role_api.delete_implied_role(
|
||||
self.admin_role_id, self.member_role_id
|
||||
)
|
||||
except exception.ImpliedRoleNotFound:
|
||||
pass
|
||||
|
||||
@ -205,8 +214,10 @@ class Bootstrapper(object):
|
||||
user = PROVIDERS.identity_api.get_user_by_name(
|
||||
self.admin_username, self.default_domain_id
|
||||
)
|
||||
LOG.info('User %s already exists, skipping creation.',
|
||||
self.admin_username)
|
||||
LOG.info(
|
||||
'User %s already exists, skipping creation.',
|
||||
self.admin_username,
|
||||
)
|
||||
|
||||
# If the user is not enabled, re-enable them. This also helps
|
||||
# provide some useful logging output later.
|
||||
@ -232,9 +243,7 @@ class Bootstrapper(object):
|
||||
# or the user was previously disabled. This allows bootstrap to act
|
||||
# as a recovery tool, without having to create a new user.
|
||||
if update:
|
||||
user = PROVIDERS.identity_api.update_user(
|
||||
user['id'], update
|
||||
)
|
||||
user = PROVIDERS.identity_api.update_user(user['id'], update)
|
||||
LOG.info('Reset password for user %s.', self.admin_username)
|
||||
if not enabled and user['enabled']:
|
||||
# Although we always try to enable the user, this log
|
||||
@ -247,7 +256,7 @@ class Bootstrapper(object):
|
||||
'name': self.admin_username,
|
||||
'enabled': True,
|
||||
'domain_id': self.default_domain_id,
|
||||
'password': self.admin_password
|
||||
'password': self.admin_password,
|
||||
}
|
||||
)
|
||||
LOG.info('Created user %s', self.admin_username)
|
||||
@ -259,19 +268,27 @@ class Bootstrapper(object):
|
||||
PROVIDERS.assignment_api.add_role_to_user_and_project(
|
||||
user_id=self.admin_user_id,
|
||||
project_id=self.project_id,
|
||||
role_id=self.admin_role_id
|
||||
role_id=self.admin_role_id,
|
||||
)
|
||||
LOG.info('Granted role %(role)s on project %(project)s to '
|
||||
LOG.info(
|
||||
'Granted role %(role)s on project %(project)s to '
|
||||
'user %(username)s.',
|
||||
{'role': self.admin_role_name,
|
||||
'project': self.project_name,
|
||||
'username': self.admin_username})
|
||||
except exception.Conflict:
|
||||
LOG.info('User %(username)s already has role %(role)s on '
|
||||
'project %(project)s.',
|
||||
{'username': self.admin_username,
|
||||
{
|
||||
'role': self.admin_role_name,
|
||||
'project': self.project_name})
|
||||
'project': self.project_name,
|
||||
'username': self.admin_username,
|
||||
},
|
||||
)
|
||||
except exception.Conflict:
|
||||
LOG.info(
|
||||
'User %(username)s already has role %(role)s on '
|
||||
'project %(project)s.',
|
||||
{
|
||||
'username': self.admin_username,
|
||||
'role': self.admin_role_name,
|
||||
'project': self.project_name,
|
||||
},
|
||||
)
|
||||
|
||||
def _bootstrap_system_role_assignment(self):
|
||||
# NOTE(lbragstad): We need to make sure a user has at least one role on
|
||||
@ -284,15 +301,22 @@ class Bootstrapper(object):
|
||||
PROVIDERS.assignment_api.create_system_grant_for_user(
|
||||
self.admin_user_id, self.admin_role_id
|
||||
)
|
||||
LOG.info('Granted role %(role)s on the system to user'
|
||||
' %(username)s.',
|
||||
{'role': self.admin_role_name,
|
||||
'username': self.admin_username})
|
||||
LOG.info(
|
||||
'Granted role %(role)s on the system to user' ' %(username)s.',
|
||||
{
|
||||
'role': self.admin_role_name,
|
||||
'username': self.admin_username,
|
||||
},
|
||||
)
|
||||
except exception.Conflict:
|
||||
LOG.info('User %(username)s already has role %(role)s on '
|
||||
LOG.info(
|
||||
'User %(username)s already has role %(role)s on '
|
||||
'the system.',
|
||||
{'username': self.admin_username,
|
||||
'role': self.admin_role_name})
|
||||
{
|
||||
'username': self.admin_username,
|
||||
'role': self.admin_role_name,
|
||||
},
|
||||
)
|
||||
|
||||
def _bootstrap_region(self):
|
||||
if self.region_id:
|
||||
@ -302,8 +326,9 @@ class Bootstrapper(object):
|
||||
)
|
||||
LOG.info('Created region %s', self.region_id)
|
||||
except exception.Conflict:
|
||||
LOG.info('Region %s exists, skipping creation.',
|
||||
self.region_id)
|
||||
LOG.info(
|
||||
'Region %s exists, skipping creation.', self.region_id
|
||||
)
|
||||
|
||||
def _bootstrap_catalog(self):
|
||||
if self.public_url or self.admin_url or self.internal_url:
|
||||
@ -323,8 +348,10 @@ class Bootstrapper(object):
|
||||
else:
|
||||
service_id = uuid.uuid4().hex
|
||||
service = {
|
||||
'id': service_id, 'name': self.service_name,
|
||||
'type': 'identity', 'enabled': True
|
||||
'id': service_id,
|
||||
'name': self.service_name,
|
||||
'type': 'identity',
|
||||
'enabled': True,
|
||||
}
|
||||
|
||||
PROVIDERS.catalog_api.create_service(service_id, service)
|
||||
@ -332,9 +359,11 @@ class Bootstrapper(object):
|
||||
|
||||
self.service_id = service['id']
|
||||
available_interfaces = {e['interface']: e for e in endpoints}
|
||||
expected_endpoints = {'public': self.public_url,
|
||||
expected_endpoints = {
|
||||
'public': self.public_url,
|
||||
'internal': self.internal_url,
|
||||
'admin': self.admin_url}
|
||||
'admin': self.admin_url,
|
||||
}
|
||||
|
||||
for interface, url in expected_endpoints.items():
|
||||
if not url:
|
||||
@ -344,26 +373,32 @@ class Bootstrapper(object):
|
||||
try:
|
||||
endpoint_ref = available_interfaces[interface]
|
||||
except KeyError:
|
||||
endpoint_ref = {'id': uuid.uuid4().hex,
|
||||
endpoint_ref = {
|
||||
'id': uuid.uuid4().hex,
|
||||
'interface': interface,
|
||||
'url': url,
|
||||
'service_id': self.service_id,
|
||||
'enabled': True}
|
||||
'enabled': True,
|
||||
}
|
||||
|
||||
if self.region_id:
|
||||
endpoint_ref['region_id'] = self.region_id
|
||||
|
||||
PROVIDERS.catalog_api.create_endpoint(
|
||||
endpoint_id=endpoint_ref['id'],
|
||||
endpoint_ref=endpoint_ref)
|
||||
endpoint_ref=endpoint_ref,
|
||||
)
|
||||
|
||||
LOG.info('Created %(interface)s endpoint %(url)s',
|
||||
{'interface': interface, 'url': url})
|
||||
LOG.info(
|
||||
'Created %(interface)s endpoint %(url)s',
|
||||
{'interface': interface, 'url': url},
|
||||
)
|
||||
else:
|
||||
endpoint_ref['url'] = url
|
||||
PROVIDERS.catalog_api.update_endpoint(
|
||||
endpoint_id=endpoint_ref['id'],
|
||||
endpoint_ref=endpoint_ref)
|
||||
endpoint_ref=endpoint_ref,
|
||||
)
|
||||
LOG.info('%s endpoint updated', interface)
|
||||
|
||||
self.endpoints[interface] = endpoint_ref['id']
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,8 @@ SYMPTOM_MODULES = [
|
||||
ldap,
|
||||
security_compliance,
|
||||
tokens,
|
||||
tokens_fernet]
|
||||
tokens_fernet,
|
||||
]
|
||||
|
||||
|
||||
def diagnose():
|
||||
@ -50,8 +51,9 @@ def diagnose():
|
||||
# Some symptoms may take a long time to check, so let's keep
|
||||
# curious users posted on our progress as we go.
|
||||
print(
|
||||
'Checking for %s...' %
|
||||
symptom.__name__[len(SYMPTOM_PREFIX):].replace('_', ' '))
|
||||
'Checking for %s...'
|
||||
% symptom.__name__[len(SYMPTOM_PREFIX) :].replace('_', ' ')
|
||||
)
|
||||
|
||||
# All symptoms are just callables that return true when they match the
|
||||
# condition that they're looking for. When that happens, we need to
|
||||
@ -64,7 +66,9 @@ def diagnose():
|
||||
# passing a string here. Also, we include a line break here to
|
||||
# visually separate the symptom's description from any other
|
||||
# checks -- it provides a better user experience.
|
||||
print(_('\nWARNING: %s') % _(symptom.__doc__)) # noqa: See comment above.
|
||||
print(
|
||||
_('\nWARNING: %s') % _(symptom.__doc__)
|
||||
) # noqa: See comment above.
|
||||
|
||||
return symptoms_found
|
||||
|
||||
|
@ -44,10 +44,7 @@ def symptom_connection_to_memcached():
|
||||
as dead. Please ensure `keystone.conf [cache] memcache_servers` is
|
||||
configured properly.
|
||||
"""
|
||||
memcached_drivers = [
|
||||
'dogpile.cache.memcached',
|
||||
'oslo_cache.memcache_pool'
|
||||
]
|
||||
memcached_drivers = ['dogpile.cache.memcached', 'oslo_cache.memcache_pool']
|
||||
if CONF.cache.enabled and CONF.cache.backend in memcached_drivers:
|
||||
cache.configure_cache()
|
||||
cache_stats = cache.CACHE_REGION.actual_backend.client.get_stats()
|
||||
|
@ -35,9 +35,7 @@ def symptom_unique_key_repositories():
|
||||
Ensure `keystone.conf [credential] key_repository` and `keystone.conf
|
||||
[fernet_tokens] key_repository` are not pointing to the same location.
|
||||
"""
|
||||
return (
|
||||
CONF.credential.key_repository == CONF.fernet_tokens.key_repository
|
||||
)
|
||||
return CONF.credential.key_repository == CONF.fernet_tokens.key_repository
|
||||
|
||||
|
||||
def symptom_usability_of_credential_fernet_key_repository():
|
||||
@ -50,11 +48,12 @@ def symptom_usability_of_credential_fernet_key_repository():
|
||||
fernet_utils = utils.FernetUtils(
|
||||
CONF.credential.key_repository,
|
||||
credential_fernet.MAX_ACTIVE_KEYS,
|
||||
'credential'
|
||||
'credential',
|
||||
)
|
||||
return (
|
||||
'fernet' in CONF.credential.provider
|
||||
and not fernet_utils.validate_key_repository())
|
||||
and not fernet_utils.validate_key_repository()
|
||||
)
|
||||
|
||||
|
||||
def symptom_keys_in_credential_fernet_key_repository():
|
||||
@ -68,8 +67,8 @@ def symptom_keys_in_credential_fernet_key_repository():
|
||||
fernet_utils = utils.FernetUtils(
|
||||
CONF.credential.key_repository,
|
||||
credential_fernet.MAX_ACTIVE_KEYS,
|
||||
'credential'
|
||||
'credential',
|
||||
)
|
||||
return (
|
||||
'fernet' in CONF.credential.provider
|
||||
and not fernet_utils.load_keys())
|
||||
'fernet' in CONF.credential.provider and not fernet_utils.load_keys()
|
||||
)
|
||||
|
@ -26,4 +26,5 @@ def symptom_database_connection_is_not_SQLite():
|
||||
""" # noqa: D403
|
||||
return (
|
||||
CONF.database.connection is not None
|
||||
and 'sqlite' in CONF.database.connection)
|
||||
and 'sqlite' in CONF.database.connection
|
||||
)
|
||||
|
@ -30,7 +30,8 @@ def symptom_LDAP_user_enabled_emulation_dn_ignored():
|
||||
"""
|
||||
return (
|
||||
not CONF.ldap.user_enabled_emulation
|
||||
and CONF.ldap.user_enabled_emulation_dn is not None)
|
||||
and CONF.ldap.user_enabled_emulation_dn is not None
|
||||
)
|
||||
|
||||
|
||||
def symptom_LDAP_user_enabled_emulation_use_group_config_ignored():
|
||||
@ -41,7 +42,8 @@ def symptom_LDAP_user_enabled_emulation_use_group_config_ignored():
|
||||
"""
|
||||
return (
|
||||
not CONF.ldap.user_enabled_emulation
|
||||
and CONF.ldap.user_enabled_emulation_use_group_config)
|
||||
and CONF.ldap.user_enabled_emulation_use_group_config
|
||||
)
|
||||
|
||||
|
||||
def symptom_LDAP_group_members_are_ids_disabled():
|
||||
@ -55,7 +57,8 @@ def symptom_LDAP_group_members_are_ids_disabled():
|
||||
"""
|
||||
return (
|
||||
CONF.ldap.group_objectclass == 'posixGroup'
|
||||
and not CONF.ldap.group_members_are_ids)
|
||||
and not CONF.ldap.group_members_are_ids
|
||||
)
|
||||
|
||||
|
||||
def symptom_LDAP_file_based_domain_specific_configs():
|
||||
@ -69,8 +72,10 @@ def symptom_LDAP_file_based_domain_specific_configs():
|
||||
`keystone.conf [identity] domain_configurations_from_database`
|
||||
being set to `false`.
|
||||
"""
|
||||
if (not CONF.identity.domain_specific_drivers_enabled or
|
||||
CONF.identity.domain_configurations_from_database):
|
||||
if (
|
||||
not CONF.identity.domain_specific_drivers_enabled
|
||||
or CONF.identity.domain_configurations_from_database
|
||||
):
|
||||
return False
|
||||
|
||||
invalid_files = []
|
||||
@ -81,10 +86,12 @@ def symptom_LDAP_file_based_domain_specific_configs():
|
||||
invalid_files.append(filename)
|
||||
if invalid_files:
|
||||
invalid_str = ', '.join(invalid_files)
|
||||
print('Warning: The following non-config files were found: %s\n'
|
||||
print(
|
||||
'Warning: The following non-config files were found: %s\n'
|
||||
'If they are intended to be config files then rename them '
|
||||
'to the form of `keystone.<domain_name>.conf`. '
|
||||
'Otherwise, ignore this warning' % invalid_str)
|
||||
'Otherwise, ignore this warning' % invalid_str
|
||||
)
|
||||
return True
|
||||
else:
|
||||
print('Could not find directory ', filedir)
|
||||
@ -122,9 +129,11 @@ def symptom_LDAP_file_based_domain_specific_configs_formatted_correctly():
|
||||
# there is no point in continuing with this check.
|
||||
# symptom_LDAP_file_based_domain_specific_config will catch and
|
||||
# report this issue.
|
||||
if (not CONF.identity.domain_specific_drivers_enabled or
|
||||
CONF.identity.domain_configurations_from_database or
|
||||
not os.path.isdir(filedir)):
|
||||
if (
|
||||
not CONF.identity.domain_specific_drivers_enabled
|
||||
or CONF.identity.domain_configurations_from_database
|
||||
or not os.path.isdir(filedir)
|
||||
):
|
||||
return False
|
||||
|
||||
invalid_files = []
|
||||
@ -138,8 +147,10 @@ def symptom_LDAP_file_based_domain_specific_configs_formatted_correctly():
|
||||
|
||||
if invalid_files:
|
||||
invalid_str = ', '.join(invalid_files)
|
||||
print('Error: The following config files are formatted incorrectly: ',
|
||||
invalid_str)
|
||||
print(
|
||||
'Error: The following config files are formatted incorrectly: ',
|
||||
invalid_str,
|
||||
)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -60,5 +60,7 @@ def symptom_password_regular_expression_description_not_set():
|
||||
Ensure `[security_compliance] password_regex_description` is set with a
|
||||
description of your password regular expression in a language for humans.
|
||||
"""
|
||||
return (CONF.security_compliance.password_regex and not
|
||||
CONF.security_compliance.password_regex_description)
|
||||
return (
|
||||
CONF.security_compliance.password_regex
|
||||
and not CONF.security_compliance.password_regex_description
|
||||
)
|
||||
|
@ -32,4 +32,4 @@ def symptom_unreasonable_max_token_size():
|
||||
depending on the IDs returned from LDAP, resulting in longer Fernet
|
||||
tokens (adjust your `max_token_size` accordingly).
|
||||
"""
|
||||
return ('fernet' in CONF.token.provider and CONF.max_token_size > 255)
|
||||
return 'fernet' in CONF.token.provider and CONF.max_token_size > 255
|
||||
|
@ -28,11 +28,12 @@ def symptom_usability_of_Fernet_key_repository():
|
||||
fernet_utils = utils.FernetUtils(
|
||||
CONF.fernet_tokens.key_repository,
|
||||
CONF.fernet_tokens.max_active_keys,
|
||||
'fernet_tokens'
|
||||
'fernet_tokens',
|
||||
)
|
||||
return (
|
||||
'fernet' in CONF.token.provider
|
||||
and not fernet_utils.validate_key_repository())
|
||||
and not fernet_utils.validate_key_repository()
|
||||
)
|
||||
|
||||
|
||||
def symptom_keys_in_Fernet_key_repository():
|
||||
@ -46,8 +47,6 @@ def symptom_keys_in_Fernet_key_repository():
|
||||
fernet_utils = utils.FernetUtils(
|
||||
CONF.fernet_tokens.key_repository,
|
||||
CONF.fernet_tokens.max_active_keys,
|
||||
'fernet_tokens'
|
||||
'fernet_tokens',
|
||||
)
|
||||
return (
|
||||
'fernet' in CONF.token.provider
|
||||
and not fernet_utils.load_keys())
|
||||
return 'fernet' in CONF.token.provider and not fernet_utils.load_keys()
|
||||
|
@ -51,13 +51,15 @@ class Identity(object):
|
||||
'id': project_id,
|
||||
'domain_id': self.default_domain_id,
|
||||
'description': 'Bootstrap project for initializing the cloud.',
|
||||
'name': self.project_name
|
||||
'name': self.project_name,
|
||||
}
|
||||
PROVIDERS.resource_api.create_project(project_id, project)
|
||||
LOG.info('Created project %s', self.project_name)
|
||||
except exception.Conflict:
|
||||
LOG.info('Project %s already exists, skipping creation.',
|
||||
self.project_name)
|
||||
LOG.info(
|
||||
'Project %s already exists, skipping creation.',
|
||||
self.project_name,
|
||||
)
|
||||
project = PROVIDERS.resource_api.get_project_by_name(
|
||||
self.project_name, self.default_domain_id
|
||||
)
|
||||
@ -75,7 +77,8 @@ class Identity(object):
|
||||
PROVIDERS.resource_api.get_domain(domain_id)
|
||||
|
||||
_self._assert_default_project_id_is_not_domain(
|
||||
user_ref.get('default_project_id'))
|
||||
user_ref.get('default_project_id')
|
||||
)
|
||||
|
||||
# For creating a user, the domain is in the object itself
|
||||
domain_id = user_ref['domain_id']
|
||||
@ -88,7 +91,8 @@ class Identity(object):
|
||||
ref = _self._create_user_with_federated_objects(user, driver)
|
||||
notifications.Audit.created(_self._USER, user['id'], initiator)
|
||||
return _self._set_domain_id_and_mapping(
|
||||
ref, domain_id, driver, mapping.EntityType.USER)
|
||||
ref, domain_id, driver, mapping.EntityType.USER
|
||||
)
|
||||
|
||||
def user_setup(self):
|
||||
# NOTE(morganfainberg): Do not create the user if it already exists.
|
||||
@ -96,12 +100,15 @@ class Identity(object):
|
||||
user = PROVIDERS.identity_api.get_user_by_name(
|
||||
self.user_name, self.default_domain_id
|
||||
)
|
||||
LOG.info('User %s already exists, skipping creation.',
|
||||
self.user_name)
|
||||
LOG.info(
|
||||
'User %s already exists, skipping creation.', self.user_name
|
||||
)
|
||||
|
||||
if self.user_id is not None and user['id'] != self.user_id:
|
||||
msg = (f'user `{self.user_name}` already exists '
|
||||
f'with `{self.user_id}`')
|
||||
msg = (
|
||||
f'user `{self.user_name}` already exists '
|
||||
f'with `{self.user_id}`'
|
||||
)
|
||||
raise exception.Conflict(type='user_id', details=msg)
|
||||
|
||||
# If the user is not enabled, re-enable them. This also helps
|
||||
@ -128,9 +135,7 @@ class Identity(object):
|
||||
# or the user was previously disabled. This allows bootstrap to act
|
||||
# as a recovery tool, without having to create a new user.
|
||||
if update:
|
||||
user = PROVIDERS.identity_api.update_user(
|
||||
user['id'], update
|
||||
)
|
||||
user = PROVIDERS.identity_api.update_user(user['id'], update)
|
||||
LOG.info('Reset password for user %s.', self.user_name)
|
||||
if not enabled and user['enabled']:
|
||||
# Although we always try to enable the user, this log
|
||||
@ -143,7 +148,7 @@ class Identity(object):
|
||||
'name': self.user_name,
|
||||
'enabled': True,
|
||||
'domain_id': self.default_domain_id,
|
||||
'password': self.user_password
|
||||
'password': self.user_password,
|
||||
}
|
||||
)
|
||||
LOG.info('Created user %s', self.user_name)
|
||||
|
@ -22,13 +22,10 @@ from keystone.cmd import cli
|
||||
# If ../../keystone/__init__.py exists, add ../../ to Python search path, so
|
||||
# that it will override what happens to be installed in
|
||||
# /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__),
|
||||
os.pardir,
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir,
|
||||
'keystone',
|
||||
'__init__.py')):
|
||||
possible_topdir = os.path.normpath(
|
||||
os.path.join(os.path.abspath(__file__), os.pardir, os.pardir, os.pardir)
|
||||
)
|
||||
if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
|
||||
|
@ -44,7 +44,7 @@ class Checks(upgradecheck.UpgradeCommands):
|
||||
'identity:delete_trust',
|
||||
'identity:get_trust',
|
||||
'identity:list_roles_for_trust',
|
||||
'identity:get_role_for_trust'
|
||||
'identity:get_role_for_trust',
|
||||
]
|
||||
failed_rules = []
|
||||
for rule in rules:
|
||||
@ -59,17 +59,22 @@ class Checks(upgradecheck.UpgradeCommands):
|
||||
"these rules to be fully permissive as hardcoded enforcement "
|
||||
"will be removed. To correct this issue, either stop "
|
||||
"overriding these rules in config to accept the defaults, or "
|
||||
"explicitly set check strings that are not empty." %
|
||||
"\", \"".join(failed_rules)
|
||||
"explicitly set check strings that are not empty."
|
||||
% "\", \"".join(failed_rules),
|
||||
)
|
||||
return upgradecheck.Result(
|
||||
upgradecheck.Code.SUCCESS, 'Trust policies are safe.')
|
||||
upgradecheck.Code.SUCCESS, 'Trust policies are safe.'
|
||||
)
|
||||
|
||||
def check_default_roles_are_immutable(self):
|
||||
hints = driver_hints.Hints()
|
||||
hints.add_filter('domain_id', None) # Only check global roles
|
||||
roles = PROVIDERS.role_api.list_roles(hints=hints)
|
||||
default_roles = ('admin', 'member', 'reader',)
|
||||
default_roles = (
|
||||
'admin',
|
||||
'member',
|
||||
'reader',
|
||||
)
|
||||
failed_roles = []
|
||||
for role in [r for r in roles if r['name'] in default_roles]:
|
||||
if not role.get('options', {}).get('immutable'):
|
||||
@ -77,18 +82,25 @@ class Checks(upgradecheck.UpgradeCommands):
|
||||
if any(failed_roles):
|
||||
return upgradecheck.Result(
|
||||
upgradecheck.Code.FAILURE,
|
||||
"Roles are not immutable: %s" % ", ".join(failed_roles)
|
||||
"Roles are not immutable: %s" % ", ".join(failed_roles),
|
||||
)
|
||||
return upgradecheck.Result(
|
||||
upgradecheck.Code.SUCCESS, "Default roles are immutable.")
|
||||
upgradecheck.Code.SUCCESS, "Default roles are immutable."
|
||||
)
|
||||
|
||||
_upgrade_checks = (
|
||||
("Check trust policies are not empty",
|
||||
check_trust_policies_are_not_empty),
|
||||
("Check default roles are immutable",
|
||||
check_default_roles_are_immutable),
|
||||
("Policy File JSON to YAML Migration",
|
||||
(common_checks.check_policy_json, {'conf': CONF})),
|
||||
(
|
||||
"Check trust policies are not empty",
|
||||
check_trust_policies_are_not_empty,
|
||||
),
|
||||
(
|
||||
"Check default roles are immutable",
|
||||
check_default_roles_are_immutable,
|
||||
),
|
||||
(
|
||||
"Policy File JSON to YAML Migration",
|
||||
(common_checks.check_policy_json, {'conf': CONF}),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
10
keystone/common/cache/_context_cache.py
vendored
10
keystone/common/cache/_context_cache.py
vendored
@ -55,8 +55,9 @@ class _ResponseCacheProxy(proxy.ProxyBackend):
|
||||
return api.NO_VALUE
|
||||
|
||||
value = msgpackutils.loads(value)
|
||||
return api.CachedValue(payload=value['payload'],
|
||||
metadata=value['metadata'])
|
||||
return api.CachedValue(
|
||||
payload=value['payload'], metadata=value['metadata']
|
||||
)
|
||||
|
||||
def _delete_local_cache(self, key):
|
||||
# On invalidate/delete remove the value from the local request cache
|
||||
@ -91,8 +92,9 @@ class _ResponseCacheProxy(proxy.ProxyBackend):
|
||||
if v is not api.NO_VALUE:
|
||||
values[key] = v
|
||||
query_keys = set(keys).difference(set(values.keys()))
|
||||
values.update(dict(
|
||||
zip(query_keys, self.proxied.get_multi(query_keys))))
|
||||
values.update(
|
||||
dict(zip(query_keys, self.proxied.get_multi(query_keys)))
|
||||
)
|
||||
return [values[k] for k in keys]
|
||||
|
||||
def set_multi(self, mapping):
|
||||
|
21
keystone/common/cache/core.py
vendored
21
keystone/common/cache/core.py
vendored
@ -41,7 +41,8 @@ class RegionInvalidationManager(object):
|
||||
@property
|
||||
def region_id(self):
|
||||
return self._invalidation_region.get_or_create(
|
||||
self._region_key, self._generate_new_id, expiration_time=-1)
|
||||
self._region_key, self._generate_new_id, expiration_time=-1
|
||||
)
|
||||
|
||||
def invalidate_region(self):
|
||||
new_region_id = self._generate_new_id()
|
||||
@ -87,6 +88,7 @@ def key_mangler_factory(invalidation_manager, orig_key_mangler):
|
||||
if orig_key_mangler:
|
||||
key = orig_key_mangler(key)
|
||||
return key
|
||||
|
||||
return key_mangler
|
||||
|
||||
|
||||
@ -127,11 +129,14 @@ def configure_cache(region=None):
|
||||
region.wrap(_context_cache._ResponseCacheProxy)
|
||||
|
||||
region_manager = RegionInvalidationManager(
|
||||
CACHE_INVALIDATION_REGION, region.name)
|
||||
CACHE_INVALIDATION_REGION, region.name
|
||||
)
|
||||
region.key_mangler = key_mangler_factory(
|
||||
region_manager, region.key_mangler)
|
||||
region_manager, region.key_mangler
|
||||
)
|
||||
region.region_invalidator = DistributedInvalidationStrategy(
|
||||
region_manager)
|
||||
region_manager
|
||||
)
|
||||
|
||||
|
||||
def _sha1_mangle_key(key):
|
||||
@ -161,7 +166,8 @@ def configure_invalidation_region():
|
||||
config_dict['expiration_time'] = None # we don't want an expiration
|
||||
|
||||
CACHE_INVALIDATION_REGION.configure_from_config(
|
||||
config_dict, '%s.' % CONF.cache.config_prefix)
|
||||
config_dict, '%s.' % CONF.cache.config_prefix
|
||||
)
|
||||
|
||||
# NOTE(breton): Wrap the cache invalidation region to avoid excessive
|
||||
# calls to memcached, which would result in poor performance.
|
||||
@ -179,5 +185,6 @@ def configure_invalidation_region():
|
||||
def get_memoization_decorator(group, expiration_group=None, region=None):
|
||||
if region is None:
|
||||
region = CACHE_REGION
|
||||
return cache.get_memoization_decorator(CONF, region, group,
|
||||
expiration_group=expiration_group)
|
||||
return cache.get_memoization_decorator(
|
||||
CONF, region, group, expiration_group=expiration_group
|
||||
)
|
||||
|
@ -17,8 +17,9 @@ REQUEST_CONTEXT_ENV = 'keystone.oslo_request_context'
|
||||
|
||||
|
||||
def _prop(name):
|
||||
return property(lambda x: getattr(x, name),
|
||||
lambda x, y: setattr(x, name, y))
|
||||
return property(
|
||||
lambda x: getattr(x, name), lambda x, y: setattr(x, name, y)
|
||||
)
|
||||
|
||||
|
||||
class RequestContext(oslo_context.RequestContext):
|
||||
|
@ -31,12 +31,16 @@ def truncated(f):
|
||||
'truncated' boolean to 'true' in the hints limit dict.
|
||||
|
||||
"""
|
||||
|
||||
@functools.wraps(f)
|
||||
def wrapper(self, hints, *args, **kwargs):
|
||||
if not hasattr(hints, 'limit'):
|
||||
raise exception.UnexpectedError(
|
||||
_('Cannot truncate a driver call without hints list as '
|
||||
'first parameter after self '))
|
||||
_(
|
||||
'Cannot truncate a driver call without hints list as '
|
||||
'first parameter after self '
|
||||
)
|
||||
)
|
||||
|
||||
if hints.limit is None or hints.filters:
|
||||
return f(self, hints, *args, **kwargs)
|
||||
@ -55,6 +59,7 @@ def truncated(f):
|
||||
else:
|
||||
hints.set_limit(list_limit)
|
||||
return ref_list
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@ -94,17 +99,23 @@ class Hints(object):
|
||||
self.filters = list()
|
||||
self.cannot_match = False
|
||||
|
||||
def add_filter(self, name, value, comparator='equals',
|
||||
case_sensitive=False):
|
||||
def add_filter(
|
||||
self, name, value, comparator='equals', case_sensitive=False
|
||||
):
|
||||
"""Add a filter to the filters list, which is publicly accessible."""
|
||||
self.filters.append({'name': name, 'value': value,
|
||||
self.filters.append(
|
||||
{
|
||||
'name': name,
|
||||
'value': value,
|
||||
'comparator': comparator,
|
||||
'case_sensitive': case_sensitive})
|
||||
'case_sensitive': case_sensitive,
|
||||
}
|
||||
)
|
||||
|
||||
def get_exact_filter_by_name(self, name):
|
||||
"""Return a filter key and value if exact filter exists for name."""
|
||||
for entry in self.filters:
|
||||
if (entry['name'] == name and entry['comparator'] == 'equals'):
|
||||
if entry['name'] == name and entry['comparator'] == 'equals':
|
||||
return entry
|
||||
|
||||
def set_limit(self, limit, truncated=False):
|
||||
|
@ -36,8 +36,7 @@ NULL_KEY = base64.urlsafe_b64encode(b'\x00' * 32)
|
||||
|
||||
class FernetUtils(object):
|
||||
|
||||
def __init__(self, key_repository, max_active_keys,
|
||||
config_group):
|
||||
def __init__(self, key_repository, max_active_keys, config_group):
|
||||
self.key_repository = key_repository
|
||||
self.max_active_keys = max_active_keys
|
||||
self.config_group = config_group
|
||||
@ -48,36 +47,43 @@ class FernetUtils(object):
|
||||
# passed in as None because we don't set allow_no_values to True.
|
||||
|
||||
# ensure current user has sufficient access to the key repository
|
||||
is_valid = (os.access(self.key_repository, os.R_OK) and
|
||||
os.access(self.key_repository, os.X_OK))
|
||||
is_valid = os.access(self.key_repository, os.R_OK) and os.access(
|
||||
self.key_repository, os.X_OK
|
||||
)
|
||||
if requires_write:
|
||||
is_valid = (is_valid and
|
||||
os.access(self.key_repository, os.W_OK))
|
||||
is_valid = is_valid and os.access(self.key_repository, os.W_OK)
|
||||
|
||||
if not is_valid:
|
||||
LOG.error(
|
||||
'Either [%(config_group)s] key_repository does not exist '
|
||||
'or Keystone does not have sufficient permission to '
|
||||
'access it: %(key_repo)s',
|
||||
{'key_repo': self.key_repository,
|
||||
'config_group': self.config_group})
|
||||
{
|
||||
'key_repo': self.key_repository,
|
||||
'config_group': self.config_group,
|
||||
},
|
||||
)
|
||||
else:
|
||||
# ensure the key repository isn't world-readable
|
||||
stat_info = os.stat(self.key_repository)
|
||||
if (stat_info.st_mode & stat.S_IROTH or
|
||||
stat_info.st_mode & stat.S_IXOTH):
|
||||
if (
|
||||
stat_info.st_mode & stat.S_IROTH
|
||||
or stat_info.st_mode & stat.S_IXOTH
|
||||
):
|
||||
LOG.warning(
|
||||
'key_repository is world readable: %s',
|
||||
self.key_repository)
|
||||
'key_repository is world readable: %s', self.key_repository
|
||||
)
|
||||
|
||||
return is_valid
|
||||
|
||||
def create_key_directory(self, keystone_user_id=None,
|
||||
keystone_group_id=None):
|
||||
def create_key_directory(
|
||||
self, keystone_user_id=None, keystone_group_id=None
|
||||
):
|
||||
"""Attempt to create the key directory if it doesn't exist."""
|
||||
utils.create_directory(
|
||||
self.key_repository, keystone_user_id=keystone_user_id,
|
||||
keystone_group_id=keystone_group_id
|
||||
self.key_repository,
|
||||
keystone_user_id=keystone_user_id,
|
||||
keystone_group_id=keystone_group_id,
|
||||
)
|
||||
|
||||
def _create_new_key(self, keystone_user_id, keystone_group_id):
|
||||
@ -110,7 +116,9 @@ class FernetUtils(object):
|
||||
LOG.warning(
|
||||
'Unable to change the ownership of the new key without a '
|
||||
'keystone user ID and keystone group ID both being provided: '
|
||||
'%s', self.key_repository)
|
||||
'%s',
|
||||
self.key_repository,
|
||||
)
|
||||
# Determine the file name of the new key
|
||||
key_file = os.path.join(self.key_repository, '0.tmp')
|
||||
create_success = False
|
||||
@ -163,15 +171,19 @@ class FernetUtils(object):
|
||||
else:
|
||||
key = key_file.read()
|
||||
if len(key) == 0:
|
||||
LOG.warning('Ignoring empty key found in key '
|
||||
'repository: %s', path)
|
||||
LOG.warning(
|
||||
'Ignoring empty key found in key '
|
||||
'repository: %s',
|
||||
path,
|
||||
)
|
||||
continue
|
||||
key_files[key_id] = path
|
||||
keys[key_id] = key
|
||||
return key_files, keys
|
||||
|
||||
def initialize_key_repository(self, keystone_user_id=None,
|
||||
keystone_group_id=None):
|
||||
def initialize_key_repository(
|
||||
self, keystone_user_id=None, keystone_group_id=None
|
||||
):
|
||||
"""Create a key repository and bootstrap it with a key.
|
||||
|
||||
:param keystone_user_id: User ID of the Keystone user.
|
||||
@ -179,8 +191,7 @@ class FernetUtils(object):
|
||||
|
||||
"""
|
||||
# make sure we have work to do before proceeding
|
||||
if os.access(os.path.join(self.key_repository, '0'),
|
||||
os.F_OK):
|
||||
if os.access(os.path.join(self.key_repository, '0'), os.F_OK):
|
||||
LOG.info('Key repository is already initialized; aborting.')
|
||||
return
|
||||
|
||||
@ -213,10 +224,10 @@ class FernetUtils(object):
|
||||
# read the list of key files
|
||||
key_files, _ = self._get_key_files(self.key_repository)
|
||||
|
||||
LOG.info('Starting key rotation with %(count)s key files: '
|
||||
'%(list)s', {
|
||||
'count': len(key_files),
|
||||
'list': list(key_files.values())})
|
||||
LOG.info(
|
||||
'Starting key rotation with %(count)s key files: ' '%(list)s',
|
||||
{'count': len(key_files), 'list': list(key_files.values())},
|
||||
)
|
||||
|
||||
# add a tmp new key to the rotation, which will be the *next* primary
|
||||
self._create_tmp_new_key(keystone_user_id, keystone_group_id)
|
||||
@ -230,12 +241,12 @@ class FernetUtils(object):
|
||||
# promote the next primary key to be the primary
|
||||
os.rename(
|
||||
os.path.join(self.key_repository, '0'),
|
||||
os.path.join(self.key_repository, str(new_primary_key))
|
||||
os.path.join(self.key_repository, str(new_primary_key)),
|
||||
)
|
||||
key_files.pop(0)
|
||||
key_files[new_primary_key] = os.path.join(
|
||||
self.key_repository,
|
||||
str(new_primary_key))
|
||||
self.key_repository, str(new_primary_key)
|
||||
)
|
||||
LOG.info('Promoted key 0 to be the primary: %s', new_primary_key)
|
||||
|
||||
# rename the tmp key to the real staged key
|
||||
@ -279,14 +290,20 @@ class FernetUtils(object):
|
||||
# sense to log this message for tokens since credentials doesn't
|
||||
# have a `max_active_key` configuration option.
|
||||
if self.key_repository == CONF.fernet_tokens.key_repository:
|
||||
msg = ('Loaded %(count)d Fernet keys from %(dir)s, but '
|
||||
msg = (
|
||||
'Loaded %(count)d Fernet keys from %(dir)s, but '
|
||||
'`[fernet_tokens] max_active_keys = %(max)d`; perhaps '
|
||||
'there have not been enough key rotations to reach '
|
||||
'`max_active_keys` yet?')
|
||||
LOG.debug(msg, {
|
||||
'`max_active_keys` yet?'
|
||||
)
|
||||
LOG.debug(
|
||||
msg,
|
||||
{
|
||||
'count': len(keys),
|
||||
'max': self.max_active_keys,
|
||||
'dir': self.key_repository})
|
||||
'dir': self.key_repository,
|
||||
},
|
||||
)
|
||||
|
||||
# return the encryption_keys, sorted by key number, descending
|
||||
key_list = [keys[x] for x in sorted(keys.keys(), reverse=True)]
|
||||
|
@ -19,27 +19,35 @@ from keystone.i18n import _
|
||||
|
||||
|
||||
def build_v3_resource_relation(resource_name):
|
||||
return ('https://docs.openstack.org/api/openstack-identity/3/rel/%s' %
|
||||
resource_name)
|
||||
return (
|
||||
'https://docs.openstack.org/api/openstack-identity/3/rel/%s'
|
||||
% resource_name
|
||||
)
|
||||
|
||||
|
||||
def build_v3_extension_resource_relation(extension_name, extension_version,
|
||||
resource_name):
|
||||
def build_v3_extension_resource_relation(
|
||||
extension_name, extension_version, resource_name
|
||||
):
|
||||
return (
|
||||
'https://docs.openstack.org/api/openstack-identity/3/ext/%s/%s/rel/'
|
||||
'%s' % (extension_name, extension_version, resource_name))
|
||||
'%s' % (extension_name, extension_version, resource_name)
|
||||
)
|
||||
|
||||
|
||||
def build_v3_parameter_relation(parameter_name):
|
||||
return ('https://docs.openstack.org/api/openstack-identity/3/param/%s' %
|
||||
parameter_name)
|
||||
return (
|
||||
'https://docs.openstack.org/api/openstack-identity/3/param/%s'
|
||||
% parameter_name
|
||||
)
|
||||
|
||||
|
||||
def build_v3_extension_parameter_relation(extension_name, extension_version,
|
||||
parameter_name):
|
||||
def build_v3_extension_parameter_relation(
|
||||
extension_name, extension_version, parameter_name
|
||||
):
|
||||
return (
|
||||
'https://docs.openstack.org/api/openstack-identity/3/ext/%s/%s/param/'
|
||||
'%s' % (extension_name, extension_version, parameter_name))
|
||||
'%s' % (extension_name, extension_version, parameter_name)
|
||||
)
|
||||
|
||||
|
||||
class Parameters(object):
|
||||
@ -58,9 +66,9 @@ class Parameters(object):
|
||||
REGISTERED_LIMIT_ID = build_v3_parameter_relation('registered_limit_id')
|
||||
LIMIT_ID = build_v3_parameter_relation('limit_id')
|
||||
APPLICATION_CRED_ID = build_v3_parameter_relation(
|
||||
'application_credential_id')
|
||||
ACCESS_RULE_ID = build_v3_parameter_relation(
|
||||
'access_rule_id')
|
||||
'application_credential_id'
|
||||
)
|
||||
ACCESS_RULE_ID = build_v3_parameter_relation('access_rule_id')
|
||||
|
||||
|
||||
class Status(object):
|
||||
@ -81,8 +89,10 @@ class Status(object):
|
||||
resource_data['hints'] = {'status': status}
|
||||
return
|
||||
|
||||
raise exception.Error(message=_(
|
||||
'Unexpected status requested for JSON Home response, %s') % status)
|
||||
raise exception.Error(
|
||||
message=_('Unexpected status requested for JSON Home response, %s')
|
||||
% status
|
||||
)
|
||||
|
||||
|
||||
class JsonHomeResources(object):
|
||||
|
@ -29,7 +29,7 @@ def create_jws_keypair(private_key_path, public_key_path):
|
||||
private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
)
|
||||
|
||||
@ -38,6 +38,6 @@ def create_jws_keypair(private_key_path, public_key_path):
|
||||
f.write(
|
||||
public_key.public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
)
|
||||
|
@ -51,6 +51,7 @@ def response_truncated(f):
|
||||
project).
|
||||
|
||||
"""
|
||||
|
||||
@functools.wraps(f)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if kwargs.get('hints') is None:
|
||||
@ -60,18 +61,18 @@ def response_truncated(f):
|
||||
if list_limit:
|
||||
kwargs['hints'].set_limit(list_limit)
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def load_driver(namespace, driver_name, *args):
|
||||
try:
|
||||
driver_manager = stevedore.DriverManager(namespace,
|
||||
driver_name,
|
||||
invoke_on_load=True,
|
||||
invoke_args=args)
|
||||
driver_manager = stevedore.DriverManager(
|
||||
namespace, driver_name, invoke_on_load=True, invoke_args=args
|
||||
)
|
||||
return driver_manager.driver
|
||||
except stevedore.exception.NoMatches:
|
||||
msg = (_('Unable to find %(name)r driver in %(namespace)r.'))
|
||||
msg = _('Unable to find %(name)r driver in %(namespace)r.')
|
||||
raise ImportError(msg % {'name': driver_name, 'namespace': namespace})
|
||||
|
||||
|
||||
@ -89,7 +90,7 @@ class _TraceMeta(type):
|
||||
__fn_info = '%(module)s.%(classname)s.%(funcname)s' % {
|
||||
'module': inspect.getmodule(__f).__name__,
|
||||
'classname': __classname,
|
||||
'funcname': __f.__name__
|
||||
'funcname': __f.__name__,
|
||||
}
|
||||
# NOTE(morganfainberg): Omit "cls" and "self" when printing trace logs
|
||||
# the index can be calculated at wrap time rather than at runtime.
|
||||
@ -115,35 +116,46 @@ class _TraceMeta(type):
|
||||
if __do_trace:
|
||||
__subst = {
|
||||
'run_time': (time.time() - __t),
|
||||
'passed_args': ', '.join([
|
||||
', '.join([repr(a)
|
||||
for a in args[__arg_idx:]]),
|
||||
', '.join(['%(k)s=%(v)r' % {'k': k, 'v': v}
|
||||
for k, v in kwargs.items()]),
|
||||
]),
|
||||
'passed_args': ', '.join(
|
||||
[
|
||||
', '.join([repr(a) for a in args[__arg_idx:]]),
|
||||
', '.join(
|
||||
[
|
||||
'%(k)s=%(v)r' % {'k': k, 'v': v}
|
||||
for k, v in kwargs.items()
|
||||
]
|
||||
),
|
||||
]
|
||||
),
|
||||
'function': __fn_info,
|
||||
'exception': __exc,
|
||||
'ret_val': __ret_val,
|
||||
}
|
||||
if __exc is not None:
|
||||
__msg = ('[%(run_time)ss] %(function)s '
|
||||
__msg = (
|
||||
'[%(run_time)ss] %(function)s '
|
||||
'(%(passed_args)s) => raised '
|
||||
'%(exception)r')
|
||||
'%(exception)r'
|
||||
)
|
||||
else:
|
||||
# TODO(morganfainberg): find a way to indicate if this
|
||||
# was a cache hit or cache miss.
|
||||
__msg = ('[%(run_time)ss] %(function)s'
|
||||
'(%(passed_args)s) => %(ret_val)r')
|
||||
__msg = (
|
||||
'[%(run_time)ss] %(function)s'
|
||||
'(%(passed_args)s) => %(ret_val)r'
|
||||
)
|
||||
LOG.trace(__msg, __subst)
|
||||
return __ret_val
|
||||
|
||||
return wrapped
|
||||
|
||||
def __new__(meta, classname, bases, class_dict):
|
||||
final_cls_dict = {}
|
||||
for attr_name, attr in class_dict.items():
|
||||
# NOTE(morganfainberg): only wrap public instances and methods.
|
||||
if (isinstance(attr, types.FunctionType) and
|
||||
not attr_name.startswith('_')):
|
||||
if isinstance(
|
||||
attr, types.FunctionType
|
||||
) and not attr_name.startswith('_'):
|
||||
attr = _TraceMeta.wrapper(attr, classname)
|
||||
final_cls_dict[attr_name] = attr
|
||||
return type.__new__(meta, classname, bases, final_cls_dict)
|
||||
@ -167,16 +179,19 @@ class Manager(object, metaclass=_TraceMeta):
|
||||
|
||||
def __init__(self, driver_name):
|
||||
if self._provides_api is None:
|
||||
raise ValueError('Programming Error: All managers must provide an '
|
||||
raise ValueError(
|
||||
'Programming Error: All managers must provide an '
|
||||
'API that can be referenced by other components '
|
||||
'of Keystone.')
|
||||
'of Keystone.'
|
||||
)
|
||||
if driver_name is not None:
|
||||
self.driver = load_driver(self.driver_namespace, driver_name)
|
||||
self.__register_provider_api()
|
||||
|
||||
def __register_provider_api(self):
|
||||
provider_api.ProviderAPIs._register_provider_api(
|
||||
name=self._provides_api, obj=self)
|
||||
name=self._provides_api, obj=self
|
||||
)
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Forward calls to the underlying driver.
|
||||
|
@ -26,11 +26,15 @@ from keystone.i18n import _
|
||||
CONF = keystone.conf.CONF
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
SUPPORTED_HASHERS = frozenset([passlib.hash.bcrypt,
|
||||
SUPPORTED_HASHERS = frozenset(
|
||||
[
|
||||
passlib.hash.bcrypt,
|
||||
passlib.hash.bcrypt_sha256,
|
||||
passlib.hash.scrypt,
|
||||
passlib.hash.pbkdf2_sha512,
|
||||
passlib.hash.sha512_crypt])
|
||||
passlib.hash.sha512_crypt,
|
||||
]
|
||||
)
|
||||
|
||||
_HASHER_NAME_MAP = {hasher.name: hasher for hasher in SUPPORTED_HASHERS}
|
||||
|
||||
@ -55,9 +59,12 @@ def _get_hash_ident(hashers):
|
||||
|
||||
|
||||
_HASHER_IDENT_MAP = {
|
||||
prefix: module for module, prefix in itertools.chain(
|
||||
*[zip([mod] * len(ident), ident)
|
||||
for mod, ident in _get_hash_ident(SUPPORTED_HASHERS)]
|
||||
prefix: module
|
||||
for module, prefix in itertools.chain(
|
||||
*[
|
||||
zip([mod] * len(ident), ident)
|
||||
for mod, ident in _get_hash_ident(SUPPORTED_HASHERS)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
@ -67,8 +74,9 @@ def _get_hasher_from_ident(hashed):
|
||||
return _HASHER_IDENT_MAP[hashed[0 : hashed.index('$', 1) + 1]]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
_('Unsupported password hashing algorithm ident: %s') %
|
||||
hashed[0:hashed.index('$', 1) + 1])
|
||||
_('Unsupported password hashing algorithm ident: %s')
|
||||
% hashed[0 : hashed.index('$', 1) + 1]
|
||||
)
|
||||
|
||||
|
||||
def verify_length_and_trunc_password(password):
|
||||
@ -84,8 +92,10 @@ def verify_length_and_trunc_password(password):
|
||||
# bytes are fully mixed. See:
|
||||
# https://passlib.readthedocs.io/en/stable/lib/passlib.hash.bcrypt.html#security-issues
|
||||
BCRYPT_MAX_LENGTH = 72
|
||||
if (CONF.identity.password_hash_algorithm == 'bcrypt' and # nosec: B105
|
||||
CONF.identity.max_password_length > BCRYPT_MAX_LENGTH):
|
||||
if (
|
||||
CONF.identity.password_hash_algorithm == 'bcrypt' # nosec: B105
|
||||
and CONF.identity.max_password_length > BCRYPT_MAX_LENGTH
|
||||
):
|
||||
msg = "Truncating password to algorithm specific maximum length %d characters."
|
||||
LOG.warning(msg, BCRYPT_MAX_LENGTH)
|
||||
max_length = BCRYPT_MAX_LENGTH
|
||||
@ -139,8 +149,9 @@ def hash_password(password):
|
||||
|
||||
if hasher is None:
|
||||
raise RuntimeError(
|
||||
_('Password Hash Algorithm %s not found') %
|
||||
CONF.identity.password_hash_algorithm)
|
||||
_('Password Hash Algorithm %s not found')
|
||||
% CONF.identity.password_hash_algorithm
|
||||
)
|
||||
|
||||
if CONF.identity.password_hash_rounds:
|
||||
params['rounds'] = CONF.identity.password_hash_rounds
|
||||
|
@ -20,13 +20,11 @@ collection_path = '/v3/users/{user_id}/access_rules'
|
||||
resource_path = collection_path + '/{access_rule_id}'
|
||||
|
||||
SYSTEM_READER_OR_OWNER = (
|
||||
'(' + base.SYSTEM_READER + ') or '
|
||||
'user_id:%(target.user.id)s'
|
||||
'(' + base.SYSTEM_READER + ') or ' 'user_id:%(target.user.id)s'
|
||||
)
|
||||
|
||||
SYSTEM_ADMIN_OR_OWNER = (
|
||||
'(' + base.SYSTEM_ADMIN + ') or '
|
||||
'user_id:%(target.user.id)s'
|
||||
'(' + base.SYSTEM_ADMIN + ') or ' 'user_id:%(target.user.id)s'
|
||||
)
|
||||
|
||||
access_rule_policies = [
|
||||
@ -35,26 +33,28 @@ access_rule_policies = [
|
||||
check_str=SYSTEM_READER_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Show access rule details.',
|
||||
operations=[{'path': resource_path,
|
||||
'method': 'GET'},
|
||||
{'path': resource_path,
|
||||
'method': 'HEAD'}]),
|
||||
operations=[
|
||||
{'path': resource_path, 'method': 'GET'},
|
||||
{'path': resource_path, 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_access_rules',
|
||||
check_str=SYSTEM_READER_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List access rules for a user.',
|
||||
operations=[{'path': collection_path,
|
||||
'method': 'GET'},
|
||||
{'path': collection_path,
|
||||
'method': 'HEAD'}]),
|
||||
operations=[
|
||||
{'path': collection_path, 'method': 'GET'},
|
||||
{'path': collection_path, 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_access_rule',
|
||||
check_str=SYSTEM_ADMIN_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete an access_rule.',
|
||||
operations=[{'path': resource_path,
|
||||
'method': 'DELETE'}])
|
||||
operations=[{'path': resource_path, 'method': 'DELETE'}],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,47 +23,85 @@ access_token_policies = [
|
||||
# order to access these APIs.
|
||||
scope_types=['project'],
|
||||
description='Authorize OAUTH1 request token.',
|
||||
operations=[{'path': '/v3/OS-OAUTH1/authorize/{request_token_id}',
|
||||
'method': 'PUT'}]),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-OAUTH1/authorize/{request_token_id}',
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_access_token',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['project'],
|
||||
description='Get OAUTH1 access token for user by access token ID.',
|
||||
operations=[{'path': ('/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}'),
|
||||
'method': 'GET'}]),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_access_token_role',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['project'],
|
||||
description='Get role for user OAUTH1 access token.',
|
||||
operations=[{'path': ('/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}/roles/{role_id}'),
|
||||
'method': 'GET'}]),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}/roles/{role_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_access_tokens',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['project'],
|
||||
description='List OAUTH1 access tokens for user.',
|
||||
operations=[{'path': '/v3/users/{user_id}/OS-OAUTH1/access_tokens',
|
||||
'method': 'GET'}]),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/users/{user_id}/OS-OAUTH1/access_tokens',
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_access_token_roles',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['project'],
|
||||
description='List OAUTH1 access token roles.',
|
||||
operations=[{'path': ('/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}/roles'),
|
||||
'method': 'GET'}]),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}/roles'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_access_token',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['project'],
|
||||
description='Delete OAUTH1 access token.',
|
||||
operations=[{'path': ('/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}'),
|
||||
'method': 'DELETE'}])
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/users/{user_id}/OS-OAUTH1/access_tokens/'
|
||||
'{access_token_id}'
|
||||
),
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -27,19 +27,19 @@ deprecated_list_application_credentials_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_application_credentials',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_get_application_credentials_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_application_credential',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_delete_application_credentials_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_application_credential',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -49,36 +49,38 @@ application_credential_policies = [
|
||||
check_str=base.ADMIN_OR_SYSTEM_READER_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Show application credential details.',
|
||||
operations=[{'path': resource_path,
|
||||
'method': 'GET'},
|
||||
{'path': resource_path,
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_get_application_credentials_for_user),
|
||||
operations=[
|
||||
{'path': resource_path, 'method': 'GET'},
|
||||
{'path': resource_path, 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_get_application_credentials_for_user,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_application_credentials',
|
||||
check_str=base.ADMIN_OR_SYSTEM_READER_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List application credentials for a user.',
|
||||
operations=[{'path': collection_path,
|
||||
'method': 'GET'},
|
||||
{'path': collection_path,
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_list_application_credentials_for_user),
|
||||
operations=[
|
||||
{'path': collection_path, 'method': 'GET'},
|
||||
{'path': collection_path, 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_application_credentials_for_user,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_application_credential',
|
||||
check_str=base.RULE_OWNER,
|
||||
scope_types=['project'],
|
||||
description='Create an application credential.',
|
||||
operations=[{'path': collection_path,
|
||||
'method': 'POST'}]),
|
||||
operations=[{'path': collection_path, 'method': 'POST'}],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_application_credential',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete an application credential.',
|
||||
operations=[{'path': resource_path,
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_application_credentials_for_user)
|
||||
operations=[{'path': resource_path, 'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_application_credentials_for_user,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -20,63 +20,41 @@ auth_policies = [
|
||||
check_str='',
|
||||
description='Get service catalog.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/auth/catalog',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/auth/catalog',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
]
|
||||
{'path': '/v3/auth/catalog', 'method': 'GET'},
|
||||
{'path': '/v3/auth/catalog', 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_auth_projects',
|
||||
check_str='',
|
||||
description=('List all projects a user has access to via role '
|
||||
'assignments.'),
|
||||
description=(
|
||||
'List all projects a user has access to via role ' 'assignments.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/auth/projects',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/auth/projects',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
]
|
||||
{'path': '/v3/auth/projects', 'method': 'GET'},
|
||||
{'path': '/v3/auth/projects', 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_auth_domains',
|
||||
check_str='',
|
||||
description=('List all domains a user has access to via role '
|
||||
'assignments.'),
|
||||
description=(
|
||||
'List all domains a user has access to via role ' 'assignments.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/auth/domains',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/auth/domains',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
]
|
||||
{'path': '/v3/auth/domains', 'method': 'GET'},
|
||||
{'path': '/v3/auth/domains', 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_auth_system',
|
||||
check_str='',
|
||||
description='List systems a user has access to via role assignments.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/auth/system',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/auth/system',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
]
|
||||
)
|
||||
{'path': '/v3/auth/system', 'method': 'GET'},
|
||||
{'path': '/v3/auth/system', 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -18,17 +18,19 @@ RULE_OWNER = 'user_id:%(user_id)s'
|
||||
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
|
||||
RULE_ADMIN_OR_CREDENTIAL_OWNER = (
|
||||
'rule:admin_required or '
|
||||
'(rule:owner and user_id:%(target.credential.user_id)s)')
|
||||
'(rule:owner and user_id:%(target.credential.user_id)s)'
|
||||
)
|
||||
RULE_ADMIN_OR_TARGET_DOMAIN = (
|
||||
'rule:admin_required or '
|
||||
'token.project.domain.id:%(target.domain.id)s')
|
||||
'rule:admin_required or ' 'token.project.domain.id:%(target.domain.id)s'
|
||||
)
|
||||
RULE_ADMIN_OR_TARGET_PROJECT = (
|
||||
'rule:admin_required or '
|
||||
'project_id:%(target.project.id)s')
|
||||
'rule:admin_required or ' 'project_id:%(target.project.id)s'
|
||||
)
|
||||
RULE_ADMIN_OR_TOKEN_SUBJECT = 'rule:admin_or_token_subject' # nosec
|
||||
RULE_REVOKE_EVENT_OR_ADMIN = 'rule:revoke_event_or_admin'
|
||||
RULE_SERVICE_ADMIN_OR_TOKEN_SUBJECT = (
|
||||
'rule:service_admin_or_token_subject') # nosec
|
||||
'rule:service_admin_or_token_subject' # nosec
|
||||
)
|
||||
RULE_SERVICE_OR_ADMIN = 'rule:service_or_admin'
|
||||
RULE_TRUST_OWNER = 'user_id:%(trust.trustor_user_id)s'
|
||||
|
||||
@ -49,8 +51,7 @@ SYSTEM_ADMIN = 'role:admin and system_scope:all'
|
||||
DOMAIN_READER = 'role:reader and domain_id:%(target.domain_id)s'
|
||||
RULE_SYSTEM_ADMIN_OR_OWNER = '(' + SYSTEM_ADMIN + ') or rule:owner'
|
||||
ADMIN_OR_SYSTEM_READER_OR_OWNER = (
|
||||
'(' + RULE_ADMIN_REQUIRED + ') or '
|
||||
'(' + SYSTEM_READER + ') or rule:owner'
|
||||
'(' + RULE_ADMIN_REQUIRED + ') or ' '(' + SYSTEM_READER + ') or rule:owner'
|
||||
)
|
||||
RULE_ADMIN_OR_SYSTEM_READER = 'rule:admin_required or (' + SYSTEM_READER + ')'
|
||||
|
||||
@ -61,35 +62,33 @@ ADMIN_OR_SYSTEM_READER_OR_CRED_OWNER = (
|
||||
'or user_id:%(target.credential.user_id)s'
|
||||
)
|
||||
ADMIN_OR_CRED_OWNER = (
|
||||
'(' + RULE_ADMIN_REQUIRED + ') '
|
||||
'or user_id:%(target.credential.user_id)s'
|
||||
'(' + RULE_ADMIN_REQUIRED + ') ' 'or user_id:%(target.credential.user_id)s'
|
||||
)
|
||||
|
||||
rules = [
|
||||
policy.RuleDefault(
|
||||
name='admin_required',
|
||||
check_str='role:admin or is_admin:1'),
|
||||
policy.RuleDefault(
|
||||
name='service_role',
|
||||
check_str='role:service'),
|
||||
name='admin_required', check_str='role:admin or is_admin:1'
|
||||
),
|
||||
policy.RuleDefault(name='service_role', check_str='role:service'),
|
||||
policy.RuleDefault(
|
||||
name='service_or_admin',
|
||||
check_str='rule:admin_required or rule:service_role'),
|
||||
check_str='rule:admin_required or rule:service_role',
|
||||
),
|
||||
policy.RuleDefault(name='owner', check_str=RULE_OWNER),
|
||||
policy.RuleDefault(
|
||||
name='owner',
|
||||
check_str=RULE_OWNER),
|
||||
name='admin_or_owner', check_str='rule:admin_required or rule:owner'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='admin_or_owner',
|
||||
check_str='rule:admin_required or rule:owner'),
|
||||
policy.RuleDefault(
|
||||
name='token_subject',
|
||||
check_str='user_id:%(target.token.user_id)s'),
|
||||
name='token_subject', check_str='user_id:%(target.token.user_id)s'
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='admin_or_token_subject',
|
||||
check_str='rule:admin_required or rule:token_subject'),
|
||||
check_str='rule:admin_required or rule:token_subject',
|
||||
),
|
||||
policy.RuleDefault(
|
||||
name='service_admin_or_token_subject',
|
||||
check_str='rule:service_or_admin or rule:token_subject'),
|
||||
check_str='rule:service_or_admin or rule:token_subject',
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,31 +23,31 @@ deprecated_get_consumer = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_consumer',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_list_consumers = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_consumers',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_create_consumer = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_consumer',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_update_consumer = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_consumer',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_delete_consumer = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_consumer',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -57,41 +57,53 @@ consumer_policies = [
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Show OAUTH1 consumer details.',
|
||||
operations=[{'path': '/v3/OS-OAUTH1/consumers/{consumer_id}',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_consumer),
|
||||
operations=[
|
||||
{'path': '/v3/OS-OAUTH1/consumers/{consumer_id}', 'method': 'GET'}
|
||||
],
|
||||
deprecated_rule=deprecated_get_consumer,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_consumers',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List OAUTH1 consumers.',
|
||||
operations=[{'path': '/v3/OS-OAUTH1/consumers',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_consumers),
|
||||
operations=[{'path': '/v3/OS-OAUTH1/consumers', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_consumers,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_consumer',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Create OAUTH1 consumer.',
|
||||
operations=[{'path': '/v3/OS-OAUTH1/consumers',
|
||||
'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_consumer),
|
||||
operations=[{'path': '/v3/OS-OAUTH1/consumers', 'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_consumer,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_consumer',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update OAUTH1 consumer.',
|
||||
operations=[{'path': '/v3/OS-OAUTH1/consumers/{consumer_id}',
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_consumer),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-OAUTH1/consumers/{consumer_id}',
|
||||
'method': 'PATCH',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_update_consumer,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_consumer',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete OAUTH1 consumer.',
|
||||
operations=[{'path': '/v3/OS-OAUTH1/consumers/{consumer_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_consumer),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-OAUTH1/consumers/{consumer_id}',
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_consumer,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,31 +23,31 @@ deprecated_get_credential = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_credential',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_credentials = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_credentials',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_credential = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_credential',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_update_credential = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_credential',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_delete_credential = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_credential',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
|
||||
|
||||
@ -57,8 +57,9 @@ credential_policies = [
|
||||
check_str=base.ADMIN_OR_SYSTEM_READER_OR_CRED_OWNER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Show credentials details.',
|
||||
operations=[{'path': '/v3/credentials/{credential_id}',
|
||||
'method': 'GET'}],
|
||||
operations=[
|
||||
{'path': '/v3/credentials/{credential_id}', 'method': 'GET'}
|
||||
],
|
||||
deprecated_rule=deprecated_get_credential,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
@ -66,8 +67,7 @@ credential_policies = [
|
||||
check_str=base.ADMIN_OR_SYSTEM_READER_OR_CRED_OWNER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='List credentials.',
|
||||
operations=[{'path': '/v3/credentials',
|
||||
'method': 'GET'}],
|
||||
operations=[{'path': '/v3/credentials', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_credentials,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
@ -75,8 +75,7 @@ credential_policies = [
|
||||
check_str=base.ADMIN_OR_CRED_OWNER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Create credential.',
|
||||
operations=[{'path': '/v3/credentials',
|
||||
'method': 'POST'}],
|
||||
operations=[{'path': '/v3/credentials', 'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_credential,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
@ -84,8 +83,9 @@ credential_policies = [
|
||||
check_str=base.ADMIN_OR_CRED_OWNER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Update credential.',
|
||||
operations=[{'path': '/v3/credentials/{credential_id}',
|
||||
'method': 'PATCH'}],
|
||||
operations=[
|
||||
{'path': '/v3/credentials/{credential_id}', 'method': 'PATCH'}
|
||||
],
|
||||
deprecated_rule=deprecated_update_credential,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
@ -93,10 +93,11 @@ credential_policies = [
|
||||
check_str=base.ADMIN_OR_CRED_OWNER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Delete credential.',
|
||||
operations=[{'path': '/v3/credentials/{credential_id}',
|
||||
'method': 'DELETE'}],
|
||||
operations=[
|
||||
{'path': '/v3/credentials/{credential_id}', 'method': 'DELETE'}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_credential,
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,31 +23,31 @@ deprecated_list_domains = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_domains',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_get_domain = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_domain',
|
||||
check_str=base.RULE_ADMIN_OR_TARGET_DOMAIN,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_update_domain = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_domain',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_domain = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_domain',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_delete_domain = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_domain',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
ADMIN_OR_SYSTEM_USER_OR_DOMAIN_USER_OR_PROJECT_USER = (
|
||||
base.RULE_ADMIN_REQUIRED + ' or '
|
||||
@ -69,41 +69,41 @@ domain_policies = [
|
||||
check_str=ADMIN_OR_SYSTEM_USER_OR_DOMAIN_USER_OR_PROJECT_USER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Show domain details.',
|
||||
operations=[{'path': '/v3/domains/{domain_id}',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_domain),
|
||||
operations=[{'path': '/v3/domains/{domain_id}', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_domain,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_domains',
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='List domains.',
|
||||
operations=[{'path': '/v3/domains',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_domains),
|
||||
operations=[{'path': '/v3/domains', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_domains,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_domain',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Create domain.',
|
||||
operations=[{'path': '/v3/domains',
|
||||
'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_domain),
|
||||
operations=[{'path': '/v3/domains', 'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_domain,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_domain',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update domain.',
|
||||
operations=[{'path': '/v3/domains/{domain_id}',
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_domain),
|
||||
operations=[{'path': '/v3/domains/{domain_id}', 'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_domain,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_domain',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete domain.',
|
||||
operations=[{'path': '/v3/domains/{domain_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_domain),
|
||||
operations=[{'path': '/v3/domains/{domain_id}', 'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_domain,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,35 +23,35 @@ deprecated_get_domain_config = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_domain_config',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_get_domain_config_default = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_domain_config_default',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_create_domain_config = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_domain_config',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_update_domain_config = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_domain_config',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_delete_domain_config = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_domain_config',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -62,45 +62,38 @@ domain_config_policies = [
|
||||
scope_types=['system', 'project'],
|
||||
description='Create domain configuration.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config',
|
||||
'method': 'PUT'
|
||||
}
|
||||
{'path': '/v3/domains/{domain_id}/config', 'method': 'PUT'}
|
||||
],
|
||||
deprecated_rule=deprecated_create_domain_config
|
||||
deprecated_rule=deprecated_create_domain_config,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_domain_config',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description=('Get the entire domain configuration for a domain, an '
|
||||
description=(
|
||||
'Get the entire domain configuration for a domain, an '
|
||||
'option group within a domain, or a specific '
|
||||
'configuration option within a group for a domain.'),
|
||||
'configuration option within a group for a domain.'
|
||||
),
|
||||
operations=[
|
||||
{'path': '/v3/domains/{domain_id}/config', 'method': 'GET'},
|
||||
{'path': '/v3/domains/{domain_id}/config', 'method': 'HEAD'},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config',
|
||||
'method': 'HEAD'
|
||||
'path': '/v3/domains/{domain_id}/config/{group}',
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}',
|
||||
'method': 'HEAD'
|
||||
'method': 'HEAD',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}/{option}',
|
||||
'method': 'GET'
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}/{option}',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_get_domain_config,
|
||||
),
|
||||
@ -110,48 +103,53 @@ domain_config_policies = [
|
||||
# This should be accessible to anyone with a valid token, regardless of
|
||||
# system-scope or project-scope.
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description=('Get security compliance domain configuration for '
|
||||
'either a domain or a specific option in a domain.'),
|
||||
description=(
|
||||
'Get security compliance domain configuration for '
|
||||
'either a domain or a specific option in a domain.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/security_compliance',
|
||||
'method': 'GET'
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/security_compliance',
|
||||
'method': 'HEAD'
|
||||
'method': 'HEAD',
|
||||
},
|
||||
{
|
||||
'path': ('/v3/domains/{domain_id}/config/'
|
||||
'security_compliance/{option}'),
|
||||
'method': 'GET'
|
||||
'path': (
|
||||
'/v3/domains/{domain_id}/config/'
|
||||
'security_compliance/{option}'
|
||||
),
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': ('/v3/domains/{domain_id}/config/'
|
||||
'security_compliance/{option}'),
|
||||
'method': 'HEAD'
|
||||
}
|
||||
'path': (
|
||||
'/v3/domains/{domain_id}/config/'
|
||||
'security_compliance/{option}'
|
||||
),
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_domain_config',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description=('Update domain configuration for either a domain, '
|
||||
'specific group or a specific option in a group.'),
|
||||
description=(
|
||||
'Update domain configuration for either a domain, '
|
||||
'specific group or a specific option in a group.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config',
|
||||
'method': 'PATCH'
|
||||
},
|
||||
{'path': '/v3/domains/{domain_id}/config', 'method': 'PATCH'},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}',
|
||||
'method': 'PATCH'
|
||||
'method': 'PATCH',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}/{option}',
|
||||
'method': 'PATCH'
|
||||
}
|
||||
'method': 'PATCH',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_update_domain_config,
|
||||
),
|
||||
@ -159,21 +157,20 @@ domain_config_policies = [
|
||||
name=base.IDENTITY % 'delete_domain_config',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description=('Delete domain configuration for either a domain, '
|
||||
'specific group or a specific option in a group.'),
|
||||
description=(
|
||||
'Delete domain configuration for either a domain, '
|
||||
'specific group or a specific option in a group.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config',
|
||||
'method': 'DELETE'
|
||||
},
|
||||
{'path': '/v3/domains/{domain_id}/config', 'method': 'DELETE'},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}',
|
||||
'method': 'DELETE'
|
||||
'method': 'DELETE',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/{domain_id}/config/{group}/{option}',
|
||||
'method': 'DELETE'
|
||||
}
|
||||
'method': 'DELETE',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_delete_domain_config,
|
||||
),
|
||||
@ -181,36 +178,26 @@ domain_config_policies = [
|
||||
name=base.IDENTITY % 'get_domain_config_default',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description=('Get domain configuration default for either a domain, '
|
||||
'specific group or a specific option in a group.'),
|
||||
description=(
|
||||
'Get domain configuration default for either a domain, '
|
||||
'specific group or a specific option in a group.'
|
||||
),
|
||||
operations=[
|
||||
{'path': '/v3/domains/config/default', 'method': 'GET'},
|
||||
{'path': '/v3/domains/config/default', 'method': 'HEAD'},
|
||||
{'path': '/v3/domains/config/{group}/default', 'method': 'GET'},
|
||||
{'path': '/v3/domains/config/{group}/default', 'method': 'HEAD'},
|
||||
{
|
||||
'path': '/v3/domains/config/default',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/config/default',
|
||||
'method': 'HEAD'
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/config/{group}/default',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/config/{group}/default',
|
||||
'method': 'HEAD'
|
||||
'path': '/v3/domains/config/{group}/{option}/default',
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/config/{group}/{option}/default',
|
||||
'method': 'GET'
|
||||
'method': 'HEAD',
|
||||
},
|
||||
{
|
||||
'path': '/v3/domains/config/{group}/{option}/default',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_get_domain_config_default,
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,25 +23,25 @@ deprecated_ec2_get_credential = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'ec2_get_credential',
|
||||
check_str=base.RULE_ADMIN_OR_CREDENTIAL_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_ec2_list_credentials = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'ec2_list_credentials',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_ec2_create_credential = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'ec2_create_credential',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_ec2_delete_credential = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'ec2_delete_credential',
|
||||
check_str=base.RULE_ADMIN_OR_CREDENTIAL_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -51,18 +51,24 @@ ec2_credential_policies = [
|
||||
check_str=base.ADMIN_OR_SYSTEM_READER_OR_CRED_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Show ec2 credential details.',
|
||||
operations=[{'path': ('/v3/users/{user_id}/credentials/OS-EC2/'
|
||||
'{credential_id}'),
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_ec2_get_credential
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/users/{user_id}/credentials/OS-EC2/' '{credential_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_ec2_get_credential,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'ec2_list_credentials',
|
||||
check_str=base.ADMIN_OR_SYSTEM_READER_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List ec2 credentials.',
|
||||
operations=[{'path': '/v3/users/{user_id}/credentials/OS-EC2',
|
||||
'method': 'GET'}],
|
||||
operations=[
|
||||
{'path': '/v3/users/{user_id}/credentials/OS-EC2', 'method': 'GET'}
|
||||
],
|
||||
deprecated_rule=deprecated_ec2_list_credentials,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
@ -70,8 +76,12 @@ ec2_credential_policies = [
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Create ec2 credential.',
|
||||
operations=[{'path': '/v3/users/{user_id}/credentials/OS-EC2',
|
||||
'method': 'POST'}],
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/users/{user_id}/credentials/OS-EC2',
|
||||
'method': 'POST',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_ec2_create_credential,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
@ -79,11 +89,16 @@ ec2_credential_policies = [
|
||||
check_str=base.ADMIN_OR_CRED_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete ec2 credential.',
|
||||
operations=[{'path': ('/v3/users/{user_id}/credentials/OS-EC2/'
|
||||
'{credential_id}'),
|
||||
'method': 'DELETE'}],
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/users/{user_id}/credentials/OS-EC2/' '{credential_id}'
|
||||
),
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_ec2_delete_credential,
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -20,29 +20,34 @@ DEPRECATED_REASON = (
|
||||
)
|
||||
|
||||
deprecated_get_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_endpoint', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'get_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_endpoints = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_endpoints', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'list_endpoints',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_update_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_endpoint', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'update_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_endpoint', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'create_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_delete_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_endpoint', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'delete_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
|
||||
|
||||
@ -52,41 +57,45 @@ endpoint_policies = [
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Show endpoint details.',
|
||||
operations=[{'path': '/v3/endpoints/{endpoint_id}',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_endpoint),
|
||||
operations=[{'path': '/v3/endpoints/{endpoint_id}', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_endpoint,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_endpoints',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List endpoints.',
|
||||
operations=[{'path': '/v3/endpoints',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_endpoints),
|
||||
operations=[{'path': '/v3/endpoints', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_endpoints,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Create endpoint.',
|
||||
operations=[{'path': '/v3/endpoints',
|
||||
'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_endpoint),
|
||||
operations=[{'path': '/v3/endpoints', 'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_endpoint,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update endpoint.',
|
||||
operations=[{'path': '/v3/endpoints/{endpoint_id}',
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_endpoint),
|
||||
operations=[
|
||||
{'path': '/v3/endpoints/{endpoint_id}', 'method': 'PATCH'}
|
||||
],
|
||||
deprecated_rule=deprecated_update_endpoint,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete endpoint.',
|
||||
operations=[{'path': '/v3/endpoints/{endpoint_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_endpoint)
|
||||
operations=[
|
||||
{'path': '/v3/endpoints/{endpoint_id}', 'method': 'DELETE'}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_endpoint,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,77 +23,77 @@ deprecated_list_endpoint_groups = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_endpoint_groups',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_get_endpoint_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_list_projects_assoc_with_endpoint_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_projects_associated_with_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_list_endpoints_assoc_with_endpoint_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_endpoints_associated_with_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_get_endpoint_group_in_project = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_endpoint_group_in_project',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_list_endpoint_groups_for_project = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_endpoint_groups_for_project',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_create_endpoint_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_update_endpoint_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_delete_endpoint_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_add_endpoint_group_to_project = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'add_endpoint_group_to_project',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_remove_endpoint_group_from_project = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'remove_endpoint_group_from_project',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -103,106 +103,178 @@ group_endpoint_policies = [
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Create endpoint group.',
|
||||
operations=[{'path': '/v3/OS-EP-FILTER/endpoint_groups',
|
||||
'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_endpoint_group),
|
||||
operations=[
|
||||
{'path': '/v3/OS-EP-FILTER/endpoint_groups', 'method': 'POST'}
|
||||
],
|
||||
deprecated_rule=deprecated_create_endpoint_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_endpoint_groups',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List endpoint groups.',
|
||||
operations=[{'path': '/v3/OS-EP-FILTER/endpoint_groups',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_endpoint_groups),
|
||||
operations=[
|
||||
{'path': '/v3/OS-EP-FILTER/endpoint_groups', 'method': 'GET'}
|
||||
],
|
||||
deprecated_rule=deprecated_list_endpoint_groups,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Get endpoint group.',
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}'),
|
||||
'method': 'GET'},
|
||||
{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}'),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_get_endpoint_group),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/' '{endpoint_group_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/' '{endpoint_group_id}'
|
||||
),
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_get_endpoint_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update endpoint group.',
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}'),
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_endpoint_group),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/' '{endpoint_group_id}'
|
||||
),
|
||||
'method': 'PATCH',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_update_endpoint_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete endpoint group.',
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}'),
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_endpoint_group),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/' '{endpoint_group_id}'
|
||||
),
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_endpoint_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_projects_associated_with_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description=('List all projects associated with a specific endpoint '
|
||||
'group.'),
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects'),
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_projects_assoc_with_endpoint_group),
|
||||
description=(
|
||||
'List all projects associated with a specific endpoint ' 'group.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_list_projects_assoc_with_endpoint_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_endpoints_associated_with_endpoint_group',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List all endpoints associated with an endpoint group.',
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/endpoints'),
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_endpoints_assoc_with_endpoint_group),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/endpoints'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_list_endpoints_assoc_with_endpoint_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_endpoint_group_in_project',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description=('Check if an endpoint group is associated with a '
|
||||
'project.'),
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'),
|
||||
'method': 'GET'},
|
||||
{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_get_endpoint_group_in_project),
|
||||
description=(
|
||||
'Check if an endpoint group is associated with a ' 'project.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'
|
||||
),
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_get_endpoint_group_in_project,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_endpoint_groups_for_project',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List endpoint groups associated with a specific project.',
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/projects/{project_id}/'
|
||||
'endpoint_groups'),
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_endpoint_groups_for_project),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/projects/{project_id}/' 'endpoint_groups'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_list_endpoint_groups_for_project,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'add_endpoint_group_to_project',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Allow a project to access an endpoint group.',
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'),
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_add_endpoint_group_to_project),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'
|
||||
),
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_add_endpoint_group_to_project,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'remove_endpoint_group_from_project',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Remove endpoint group from project.',
|
||||
operations=[{'path': ('/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'),
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_remove_endpoint_group_from_project)
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/OS-EP-FILTER/endpoint_groups/'
|
||||
'{endpoint_group_id}/projects/{project_id}'
|
||||
),
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_remove_endpoint_group_from_project,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -85,69 +85,73 @@ deprecated_check_system_grant_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_system_grant_for_user',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_system_grants_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_system_grants_for_user',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_system_grant_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_system_grant_for_user',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_revoke_system_grant_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'revoke_system_grant_for_user',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_check_system_grant_for_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_system_grant_for_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_system_grants_for_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_system_grants_for_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_system_grant_for_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_system_grant_for_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_revoke_system_grant_for_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'revoke_system_grant_for_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_grants = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_grants', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'list_grants',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_check_grant = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_grant', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'check_grant',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_grant = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_grant', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'create_grant',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_revoke_grant = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'revoke_grant', check_str=base.RULE_ADMIN_REQUIRED,
|
||||
name=base.IDENTITY % 'revoke_grant',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
|
||||
|
||||
@ -159,36 +163,44 @@ resource_paths = [
|
||||
]
|
||||
|
||||
|
||||
resource_paths += ['/OS-INHERIT' + path + '/inherited_to_projects'
|
||||
for path in resource_paths]
|
||||
resource_paths += [
|
||||
'/OS-INHERIT' + path + '/inherited_to_projects' for path in resource_paths
|
||||
]
|
||||
|
||||
|
||||
collection_paths = [
|
||||
'/projects/{project_id}/users/{user_id}/roles',
|
||||
'/projects/{project_id}/groups/{group_id}/roles',
|
||||
'/domains/{domain_id}/users/{user_id}/roles',
|
||||
'/domains/{domain_id}/groups/{group_id}/roles'
|
||||
'/domains/{domain_id}/groups/{group_id}/roles',
|
||||
]
|
||||
|
||||
|
||||
inherited_collection_paths = [
|
||||
('/OS-INHERIT/domains/{domain_id}/groups/{group_id}/roles/'
|
||||
'inherited_to_projects'),
|
||||
('/OS-INHERIT/domains/{domain_id}/users/{user_id}/roles/'
|
||||
'inherited_to_projects')
|
||||
(
|
||||
'/OS-INHERIT/domains/{domain_id}/groups/{group_id}/roles/'
|
||||
'inherited_to_projects'
|
||||
),
|
||||
(
|
||||
'/OS-INHERIT/domains/{domain_id}/users/{user_id}/roles/'
|
||||
'inherited_to_projects'
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def list_operations(paths, methods):
|
||||
return [{'path': '/v3' + path, 'method': method}
|
||||
for path in paths for method in methods]
|
||||
return [
|
||||
{'path': '/v3' + path, 'method': method}
|
||||
for path in paths
|
||||
for method in methods
|
||||
]
|
||||
|
||||
|
||||
# NOTE(samueldmq): Unlike individual resource paths, collection
|
||||
# paths for the inherited grants do not contain a HEAD API
|
||||
list_grants_operations = (
|
||||
list_operations(collection_paths, ['GET', 'HEAD']) +
|
||||
list_operations(inherited_collection_paths, ['GET']))
|
||||
list_grants_operations = list_operations(
|
||||
collection_paths, ['GET', 'HEAD']
|
||||
) + list_operations(inherited_collection_paths, ['GET'])
|
||||
|
||||
|
||||
grant_policies = [
|
||||
@ -196,52 +208,64 @@ grant_policies = [
|
||||
name=base.IDENTITY % 'check_grant',
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description=('Check a role grant between a target and an actor. A '
|
||||
description=(
|
||||
'Check a role grant between a target and an actor. A '
|
||||
'target can be either a domain or a project. An actor '
|
||||
'can be either a user or a group. These terms also apply '
|
||||
'to the OS-INHERIT APIs, where grants on the target '
|
||||
'are inherited to all projects in the subtree, if '
|
||||
'applicable.'),
|
||||
'applicable.'
|
||||
),
|
||||
operations=list_operations(resource_paths, ['HEAD', 'GET']),
|
||||
deprecated_rule=deprecated_check_grant),
|
||||
deprecated_rule=deprecated_check_grant,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_grants',
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_LIST,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description=('List roles granted to an actor on a target. A target '
|
||||
description=(
|
||||
'List roles granted to an actor on a target. A target '
|
||||
'can be either a domain or a project. An actor can be '
|
||||
'either a user or a group. For the OS-INHERIT APIs, it '
|
||||
'is possible to list inherited role grants for actors on '
|
||||
'domains, where grants are inherited to all projects '
|
||||
'in the specified domain.'),
|
||||
'in the specified domain.'
|
||||
),
|
||||
operations=list_grants_operations,
|
||||
deprecated_rule=deprecated_list_grants),
|
||||
deprecated_rule=deprecated_list_grants,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_grant',
|
||||
check_str=ADMIN_OR_DOMAIN_ADMIN,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description=('Create a role grant between a target and an actor. A '
|
||||
description=(
|
||||
'Create a role grant between a target and an actor. A '
|
||||
'target can be either a domain or a project. An actor '
|
||||
'can be either a user or a group. These terms also apply '
|
||||
'to the OS-INHERIT APIs, where grants on the target '
|
||||
'are inherited to all projects in the subtree, if '
|
||||
'applicable.'),
|
||||
'applicable.'
|
||||
),
|
||||
operations=list_operations(resource_paths, ['PUT']),
|
||||
deprecated_rule=deprecated_create_grant),
|
||||
deprecated_rule=deprecated_create_grant,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'revoke_grant',
|
||||
check_str=ADMIN_OR_DOMAIN_ADMIN,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description=('Revoke a role grant between a target and an actor. A '
|
||||
description=(
|
||||
'Revoke a role grant between a target and an actor. A '
|
||||
'target can be either a domain or a project. An actor '
|
||||
'can be either a user or a group. These terms also apply '
|
||||
'to the OS-INHERIT APIs, where grants on the target '
|
||||
'are inherited to all projects in the subtree, if '
|
||||
'applicable. In that case, revoking the role grant in '
|
||||
'the target would remove the logical effect of '
|
||||
'inheriting it to the target\'s projects subtree.'),
|
||||
'inheriting it to the target\'s projects subtree.'
|
||||
),
|
||||
operations=list_operations(resource_paths, ['DELETE']),
|
||||
deprecated_rule=deprecated_revoke_grant),
|
||||
deprecated_rule=deprecated_revoke_grant,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_system_grants_for_user',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
@ -250,7 +274,7 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/users/{user_id}/roles',
|
||||
'method': ['HEAD', 'GET']
|
||||
'method': ['HEAD', 'GET'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_list_system_grants_for_user,
|
||||
@ -263,7 +287,7 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/users/{user_id}/roles/{role_id}',
|
||||
'method': ['HEAD', 'GET']
|
||||
'method': ['HEAD', 'GET'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_check_system_grant_for_user,
|
||||
@ -276,7 +300,7 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/users/{user_id}/roles/{role_id}',
|
||||
'method': ['PUT']
|
||||
'method': ['PUT'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_system_grant_for_user,
|
||||
@ -289,7 +313,7 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/users/{user_id}/roles/{role_id}',
|
||||
'method': ['DELETE']
|
||||
'method': ['DELETE'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_revoke_system_grant_for_user,
|
||||
@ -302,7 +326,7 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/groups/{group_id}/roles',
|
||||
'method': ['HEAD', 'GET']
|
||||
'method': ['HEAD', 'GET'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_list_system_grants_for_group,
|
||||
@ -315,7 +339,7 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/groups/{group_id}/roles/{role_id}',
|
||||
'method': ['HEAD', 'GET']
|
||||
'method': ['HEAD', 'GET'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_check_system_grant_for_group,
|
||||
@ -328,7 +352,7 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/groups/{group_id}/roles/{role_id}',
|
||||
'method': ['PUT']
|
||||
'method': ['PUT'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_system_grant_for_group,
|
||||
@ -341,11 +365,11 @@ grant_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/system/groups/{group_id}/roles/{role_id}',
|
||||
'method': ['DELETE']
|
||||
'method': ['DELETE'],
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_revoke_system_grant_for_group,
|
||||
)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -21,8 +21,10 @@ SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_USER_OR_OWNER = (
|
||||
'user_id:%(user_id)s'
|
||||
)
|
||||
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_OR_OWNER = (
|
||||
'(' + base.RULE_ADMIN_REQUIRED + ') or ' +
|
||||
SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_USER_OR_OWNER
|
||||
'('
|
||||
+ base.RULE_ADMIN_REQUIRED
|
||||
+ ') or '
|
||||
+ SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_USER_OR_OWNER
|
||||
)
|
||||
|
||||
SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP_USER = (
|
||||
@ -32,8 +34,10 @@ SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP_USER = (
|
||||
'domain_id:%(target.user.domain_id)s)'
|
||||
)
|
||||
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP = (
|
||||
'(' + base.RULE_ADMIN_REQUIRED + ') or ' +
|
||||
SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP_USER
|
||||
'('
|
||||
+ base.RULE_ADMIN_REQUIRED
|
||||
+ ') or '
|
||||
+ SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP_USER
|
||||
)
|
||||
|
||||
SYSTEM_READER_OR_DOMAIN_READER = (
|
||||
@ -41,8 +45,7 @@ SYSTEM_READER_OR_DOMAIN_READER = (
|
||||
'(role:reader and domain_id:%(target.group.domain_id)s)'
|
||||
)
|
||||
ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER = (
|
||||
'(' + base.RULE_ADMIN_REQUIRED + ') or ' +
|
||||
SYSTEM_READER_OR_DOMAIN_READER
|
||||
'(' + base.RULE_ADMIN_REQUIRED + ') or ' + SYSTEM_READER_OR_DOMAIN_READER
|
||||
)
|
||||
|
||||
SYSTEM_ADMIN_OR_DOMAIN_ADMIN = (
|
||||
@ -58,61 +61,61 @@ deprecated_get_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_groups = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_groups',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_groups_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_groups_for_user',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_users_in_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_users_in_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_check_user_in_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_user_in_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_update_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_delete_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_remove_user_from_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'remove_user_from_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_add_user_to_group = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'add_user_to_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
|
||||
group_policies = [
|
||||
@ -121,91 +124,106 @@ group_policies = [
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Show group details.',
|
||||
operations=[{'path': '/v3/groups/{group_id}',
|
||||
'method': 'GET'},
|
||||
{'path': '/v3/groups/{group_id}',
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_get_group),
|
||||
operations=[
|
||||
{'path': '/v3/groups/{group_id}', 'method': 'GET'},
|
||||
{'path': '/v3/groups/{group_id}', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_get_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_groups',
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='List groups.',
|
||||
operations=[{'path': '/v3/groups',
|
||||
'method': 'GET'},
|
||||
{'path': '/v3/groups',
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_list_groups),
|
||||
operations=[
|
||||
{'path': '/v3/groups', 'method': 'GET'},
|
||||
{'path': '/v3/groups', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_groups,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_groups_for_user',
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_OR_OWNER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='List groups to which a user belongs.',
|
||||
operations=[{'path': '/v3/users/{user_id}/groups',
|
||||
'method': 'GET'},
|
||||
{'path': '/v3/users/{user_id}/groups',
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_list_groups_for_user),
|
||||
operations=[
|
||||
{'path': '/v3/users/{user_id}/groups', 'method': 'GET'},
|
||||
{'path': '/v3/users/{user_id}/groups', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_groups_for_user,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Create group.',
|
||||
operations=[{'path': '/v3/groups',
|
||||
'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_group),
|
||||
operations=[{'path': '/v3/groups', 'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Update group.',
|
||||
operations=[{'path': '/v3/groups/{group_id}',
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_group),
|
||||
operations=[{'path': '/v3/groups/{group_id}', 'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Delete group.',
|
||||
operations=[{'path': '/v3/groups/{group_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_group),
|
||||
operations=[{'path': '/v3/groups/{group_id}', 'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_users_in_group',
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='List members of a specific group.',
|
||||
operations=[{'path': '/v3/groups/{group_id}/users',
|
||||
'method': 'GET'},
|
||||
{'path': '/v3/groups/{group_id}/users',
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_list_users_in_group),
|
||||
operations=[
|
||||
{'path': '/v3/groups/{group_id}/users', 'method': 'GET'},
|
||||
{'path': '/v3/groups/{group_id}/users', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_users_in_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'remove_user_from_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Remove user from group.',
|
||||
operations=[{'path': '/v3/groups/{group_id}/users/{user_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_remove_user_from_group),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/groups/{group_id}/users/{user_id}',
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_remove_user_from_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'check_user_in_group',
|
||||
check_str=ADMIN_OR_SYSTEM_READER_OR_DOMAIN_READER_FOR_TARGET_GROUP,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Check whether a user is a member of a group.',
|
||||
operations=[{'path': '/v3/groups/{group_id}/users/{user_id}',
|
||||
'method': 'HEAD'},
|
||||
{'path': '/v3/groups/{group_id}/users/{user_id}',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_check_user_in_group),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/groups/{group_id}/users/{user_id}',
|
||||
'method': 'HEAD',
|
||||
},
|
||||
{'path': '/v3/groups/{group_id}/users/{user_id}', 'method': 'GET'},
|
||||
],
|
||||
deprecated_rule=deprecated_check_user_in_group,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'add_user_to_group',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Add user to group.',
|
||||
operations=[{'path': '/v3/groups/{group_id}/users/{user_id}',
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_add_user_to_group)
|
||||
operations=[
|
||||
{'path': '/v3/groups/{group_id}/users/{user_id}', 'method': 'PUT'}
|
||||
],
|
||||
deprecated_rule=deprecated_add_user_to_group,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,31 +23,31 @@ deprecated_get_idp = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_identity_provider',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_idp = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_identity_providers',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_update_idp = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_identity_provider',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_idp = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_identity_provider',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_delete_idp = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_identity_provider',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
|
||||
|
||||
@ -63,23 +63,22 @@ identity_provider_policies = [
|
||||
# requires modifying configuration files.
|
||||
scope_types=['system', 'project'],
|
||||
description='Create identity provider.',
|
||||
operations=[{'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_create_idp),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_idp,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_identity_providers',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List identity providers.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/identity_providers',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/identity_providers',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
{'path': '/v3/OS-FEDERATION/identity_providers', 'method': 'GET'},
|
||||
{'path': '/v3/OS-FEDERATION/identity_providers', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_idp,
|
||||
),
|
||||
@ -91,12 +90,12 @@ identity_provider_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'GET'
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_get_idp,
|
||||
),
|
||||
@ -105,17 +104,27 @@ identity_provider_policies = [
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update identity provider.',
|
||||
operations=[{'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_idp),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'PATCH',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_update_idp,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_identity_provider',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete identity provider.',
|
||||
operations=[{'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_idp),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/identity_providers/{idp_id}',
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_idp,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,37 +23,37 @@ deprecated_get_implied_role = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_implied_role',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_list_implied_roles = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_implied_roles',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_list_role_inference_rules = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_role_inference_rules',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_check_implied_role = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_implied_role',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_create_implied_role = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_implied_role',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
deprecated_delete_implied_role = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_implied_role',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -71,9 +71,13 @@ implied_role_policies = [
|
||||
'implied role and the prior role is assigned to a user, '
|
||||
'the user also assumes the implied role.',
|
||||
operations=[
|
||||
{'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_implied_role),
|
||||
{
|
||||
'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_get_implied_role,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_implied_roles',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
@ -86,8 +90,10 @@ implied_role_policies = [
|
||||
'prior role.',
|
||||
operations=[
|
||||
{'path': '/v3/roles/{prior_role_id}/implies', 'method': 'GET'},
|
||||
{'path': '/v3/roles/{prior_role_id}/implies', 'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_list_implied_roles),
|
||||
{'path': '/v3/roles/{prior_role_id}/implies', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_implied_roles,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_implied_role',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
@ -97,9 +103,13 @@ implied_role_policies = [
|
||||
'role and the prior role is assigned to a user, the user '
|
||||
'also assumes the implied role.',
|
||||
operations=[
|
||||
{'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_create_implied_role),
|
||||
{
|
||||
'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_implied_role,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_implied_role',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
@ -110,9 +120,13 @@ implied_role_policies = [
|
||||
'also assumes the implied role. Removing the association '
|
||||
'will cause that effect to be eliminated.',
|
||||
operations=[
|
||||
{'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_implied_role),
|
||||
{
|
||||
'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_implied_role,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_role_inference_rules',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
@ -123,8 +137,10 @@ implied_role_policies = [
|
||||
'the user also assumes the implied role.',
|
||||
operations=[
|
||||
{'path': '/v3/role_inferences', 'method': 'GET'},
|
||||
{'path': '/v3/role_inferences', 'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_list_role_inference_rules),
|
||||
{'path': '/v3/role_inferences', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_role_inference_rules,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'check_implied_role',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
@ -134,9 +150,13 @@ implied_role_policies = [
|
||||
'role and the prior role is assigned to a user, the user '
|
||||
'also assumes the implied role.',
|
||||
operations=[
|
||||
{'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_check_implied_role),
|
||||
{
|
||||
'path': '/v3/roles/{prior_role_id}/implies/{implied_role_id}',
|
||||
'method': 'HEAD',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_check_implied_role,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -33,49 +33,52 @@ limit_policies = [
|
||||
check_str='',
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Get limit enforcement model.',
|
||||
operations=[{'path': '/v3/limits/model',
|
||||
'method': 'GET'},
|
||||
{'path': '/v3/limits/model',
|
||||
'method': 'HEAD'}]),
|
||||
operations=[
|
||||
{'path': '/v3/limits/model', 'method': 'GET'},
|
||||
{'path': '/v3/limits/model', 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_limit',
|
||||
check_str=ADMIN_OR_SYSTEM_OR_DOMAIN_OR_PROJECT_USER,
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='Show limit details.',
|
||||
operations=[{'path': '/v3/limits/{limit_id}',
|
||||
'method': 'GET'},
|
||||
{'path': '/v3/limits/{limit_id}',
|
||||
'method': 'HEAD'}]),
|
||||
operations=[
|
||||
{'path': '/v3/limits/{limit_id}', 'method': 'GET'},
|
||||
{'path': '/v3/limits/{limit_id}', 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_limits',
|
||||
check_str='',
|
||||
scope_types=['system', 'domain', 'project'],
|
||||
description='List limits.',
|
||||
operations=[{'path': '/v3/limits',
|
||||
'method': 'GET'},
|
||||
{'path': '/v3/limits',
|
||||
'method': 'HEAD'}]),
|
||||
operations=[
|
||||
{'path': '/v3/limits', 'method': 'GET'},
|
||||
{'path': '/v3/limits', 'method': 'HEAD'},
|
||||
],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_limits',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Create limits.',
|
||||
operations=[{'path': '/v3/limits',
|
||||
'method': 'POST'}]),
|
||||
operations=[{'path': '/v3/limits', 'method': 'POST'}],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_limit',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update limit.',
|
||||
operations=[{'path': '/v3/limits/{limit_id}',
|
||||
'method': 'PATCH'}]),
|
||||
operations=[{'path': '/v3/limits/{limit_id}', 'method': 'PATCH'}],
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_limit',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete limit.',
|
||||
operations=[{'path': '/v3/limits/{limit_id}',
|
||||
'method': 'DELETE'}])
|
||||
operations=[{'path': '/v3/limits/{limit_id}', 'method': 'DELETE'}],
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,31 +23,31 @@ deprecated_get_mapping = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_mapping',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_list_mappings = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_mappings',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_update_mapping = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_mapping',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_create_mapping = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_mapping',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
deprecated_delete_mapping = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_mapping',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.STEIN
|
||||
deprecated_since=versionutils.deprecated.STEIN,
|
||||
)
|
||||
|
||||
|
||||
@ -56,11 +56,18 @@ mapping_policies = [
|
||||
name=base.IDENTITY % 'create_mapping',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description=('Create a new federated mapping containing one or '
|
||||
'more sets of rules.'),
|
||||
operations=[{'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_create_mapping),
|
||||
description=(
|
||||
'Create a new federated mapping containing one or '
|
||||
'more sets of rules.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_mapping,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_mapping',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
@ -69,14 +76,14 @@ mapping_policies = [
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'GET'
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_get_mapping
|
||||
deprecated_rule=deprecated_get_mapping,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_mappings',
|
||||
@ -84,14 +91,8 @@ mapping_policies = [
|
||||
scope_types=['system', 'project'],
|
||||
description='List federated mappings.',
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/mappings',
|
||||
'method': 'GET'
|
||||
},
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/mappings',
|
||||
'method': 'HEAD'
|
||||
}
|
||||
{'path': '/v3/OS-FEDERATION/mappings', 'method': 'GET'},
|
||||
{'path': '/v3/OS-FEDERATION/mappings', 'method': 'HEAD'},
|
||||
],
|
||||
deprecated_rule=deprecated_list_mappings,
|
||||
),
|
||||
@ -100,17 +101,27 @@ mapping_policies = [
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete a federated mapping.',
|
||||
operations=[{'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_mapping),
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_mapping,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_mapping',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update a federated mapping.',
|
||||
operations=[{'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_mapping)
|
||||
operations=[
|
||||
{
|
||||
'path': '/v3/OS-FEDERATION/mappings/{mapping_id}',
|
||||
'method': 'PATCH',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_update_mapping,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,35 +23,35 @@ deprecated_get_policy = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_list_policies = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_policies',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_update_policy = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'update_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_create_policy = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_delete_policy = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -63,41 +63,41 @@ policy_policies = [
|
||||
# More-or-less adding scope_types to be consistent with other policies.
|
||||
scope_types=['system', 'project'],
|
||||
description='Show policy details.',
|
||||
operations=[{'path': '/v3/policies/{policy_id}',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_policy),
|
||||
operations=[{'path': '/v3/policies/{policy_id}', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_get_policy,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_policies',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List policies.',
|
||||
operations=[{'path': '/v3/policies',
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_policies),
|
||||
operations=[{'path': '/v3/policies', 'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_policies,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Create policy.',
|
||||
operations=[{'path': '/v3/policies',
|
||||
'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_policy),
|
||||
operations=[{'path': '/v3/policies', 'method': 'POST'}],
|
||||
deprecated_rule=deprecated_create_policy,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'update_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Update policy.',
|
||||
operations=[{'path': '/v3/policies/{policy_id}',
|
||||
'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_policy),
|
||||
operations=[{'path': '/v3/policies/{policy_id}', 'method': 'PATCH'}],
|
||||
deprecated_rule=deprecated_update_policy,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete policy.',
|
||||
operations=[{'path': '/v3/policies/{policy_id}',
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_policy)
|
||||
operations=[{'path': '/v3/policies/{policy_id}', 'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_policy,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
@ -28,77 +28,77 @@ deprecated_check_policy_assoc_for_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_policy_association_for_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_check_policy_assoc_for_service = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_policy_association_for_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_check_policy_assoc_for_region_and_service = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'check_policy_association_for_region_and_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_get_policy_for_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_policy_for_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_list_endpoints_for_policy = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_endpoints_for_policy',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_create_policy_assoc_for_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_policy_association_for_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_delete_policy_assoc_for_endpoint = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_policy_association_for_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_create_policy_assoc_for_service = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_policy_association_for_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_delete_policy_assoc_for_service = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_policy_association_for_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_create_policy_assoc_for_region_and_service = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'create_policy_association_for_region_and_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
deprecated_delete_policy_assoc_for_region_and_service = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_policy_association_for_region_and_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN
|
||||
deprecated_since=versionutils.deprecated.TRAIN,
|
||||
)
|
||||
|
||||
|
||||
@ -108,115 +108,207 @@ policy_association_policies = [
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Associate a policy to a specific endpoint.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'),
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_create_policy_assoc_for_endpoint),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'
|
||||
),
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_policy_assoc_for_endpoint,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'check_policy_association_for_endpoint',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Check policy association for endpoint.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'),
|
||||
'method': 'GET'},
|
||||
{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_check_policy_assoc_for_endpoint),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'
|
||||
),
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_check_policy_assoc_for_endpoint,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_policy_association_for_endpoint',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete policy association for endpoint.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'),
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_policy_assoc_for_endpoint),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints/{endpoint_id}'
|
||||
),
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_policy_assoc_for_endpoint,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_policy_association_for_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Associate a policy to a specific service.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'),
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_create_policy_assoc_for_service),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'
|
||||
),
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_policy_assoc_for_service,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'check_policy_association_for_service',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Check policy association for service.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'),
|
||||
'method': 'GET'},
|
||||
{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_check_policy_assoc_for_service),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'
|
||||
),
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_check_policy_assoc_for_service,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_policy_association_for_service',
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete policy association for service.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'),
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_policy_assoc_for_service),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}'
|
||||
),
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_policy_assoc_for_service,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % (
|
||||
'create_policy_association_for_region_and_service'),
|
||||
name=base.IDENTITY
|
||||
% ('create_policy_association_for_region_and_service'),
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description=('Associate a policy to a specific region and service '
|
||||
'combination.'),
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'),
|
||||
'method': 'PUT'}],
|
||||
deprecated_rule=deprecated_create_policy_assoc_for_region_and_service),
|
||||
description=(
|
||||
'Associate a policy to a specific region and service '
|
||||
'combination.'
|
||||
),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'
|
||||
),
|
||||
'method': 'PUT',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_create_policy_assoc_for_region_and_service,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'check_policy_association_for_region_and_service',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Check policy association for region and service.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'),
|
||||
'method': 'GET'},
|
||||
{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_check_policy_assoc_for_region_and_service),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'
|
||||
),
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'
|
||||
),
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_check_policy_assoc_for_region_and_service,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % (
|
||||
'delete_policy_association_for_region_and_service'),
|
||||
name=base.IDENTITY
|
||||
% ('delete_policy_association_for_region_and_service'),
|
||||
check_str=base.RULE_ADMIN_REQUIRED,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete policy association for region and service.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'),
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_policy_assoc_for_region_and_service),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'services/{service_id}/regions/{region_id}'
|
||||
),
|
||||
'method': 'DELETE',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_delete_policy_assoc_for_region_and_service,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_policy_for_endpoint',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Get policy for endpoint.',
|
||||
operations=[{'path': ('/v3/endpoints/{endpoint_id}/OS-ENDPOINT-POLICY/'
|
||||
'policy'),
|
||||
'method': 'GET'},
|
||||
{'path': ('/v3/endpoints/{endpoint_id}/OS-ENDPOINT-POLICY/'
|
||||
'policy'),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_get_policy_for_endpoint),
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/endpoints/{endpoint_id}/OS-ENDPOINT-POLICY/' 'policy'
|
||||
),
|
||||
'method': 'GET',
|
||||
},
|
||||
{
|
||||
'path': (
|
||||
'/v3/endpoints/{endpoint_id}/OS-ENDPOINT-POLICY/' 'policy'
|
||||
),
|
||||
'method': 'HEAD',
|
||||
},
|
||||
],
|
||||
deprecated_rule=deprecated_get_policy_for_endpoint,
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_endpoints_for_policy',
|
||||
check_str=base.RULE_ADMIN_OR_SYSTEM_READER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List endpoints for policy.',
|
||||
operations=[{'path': ('/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/'
|
||||
'endpoints'),
|
||||
'method': 'GET'}],
|
||||
deprecated_rule=deprecated_list_endpoints_for_policy)
|
||||
operations=[
|
||||
{
|
||||
'path': (
|
||||
'/v3/policies/{policy_id}/OS-ENDPOINT-POLICY/' 'endpoints'
|
||||
),
|
||||
'method': 'GET',
|
||||
}
|
||||
],
|
||||
deprecated_rule=deprecated_list_endpoints_for_policy,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user