Merge "Blackify the keystone code base"

This commit is contained in:
Zuul 2024-07-24 17:15:09 +00:00 committed by Gerrit Code Review
commit 181dc7b03f
396 changed files with 44964 additions and 30269 deletions

View File

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

View File

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

View File

@ -25,20 +25,19 @@ PROVIDERS = provider_api.ProviderAPIs
def build_prior_role_response_data(prior_role_id, prior_role_name):
return {
'id': prior_role_id,
'links': {
'self': ks_flask.base_url(path='/roles/%s' % prior_role_id)
},
'name': prior_role_name}
'links': {'self': ks_flask.base_url(path='/roles/%s' % prior_role_id)},
'name': prior_role_name,
}
def build_implied_role_response_data(implied_role):
return {
'id': implied_role['id'],
'links': {
'self': ks_flask.base_url(
path='/roles/%s' % implied_role['id'])
'self': ks_flask.base_url(path='/roles/%s' % implied_role['id'])
},
'name': implied_role['name']}
'name': implied_role['name'],
}
def role_inference_response(prior_role_id):
@ -46,5 +45,8 @@ def role_inference_response(prior_role_id):
response = {
'role_inference': {
'prior_role': build_prior_role_response_data(
prior_role_id, prior_role['name'])}}
prior_role_id, prior_role['name']
)
}
}
return response

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -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({
k.replace('id', 'external_id'): v
for k, v in access_rule.items()})
access_rule_ref = AccessRuleModel.from_dict(
{
k.replace('id', 'external_id'): v
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:

View File

@ -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
)

View File

@ -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,
}

View File

@ -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

View File

@ -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(
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))
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,
)
)
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(
type=AssignmentType.USER_PROJECT,
actor_id=user_id, target_id=project_id,
role_id=role_id, inherited=False))
session.add(
RoleAssignment(
type=AssignmentType.USER_PROJECT,
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

View File

@ -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()

View File

@ -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),
sql.ForeignKey('role.id', ondelete='CASCADE'),
nullable=False, primary_key=True)
option_id = sql.Column(sql.String(4), nullable=False,
primary_key=True)
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)
option_value = sql.Column(sql.JsonBlob, nullable=True)
def __init__(self, option_id, option_value):

View File

@ -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,
}

View File

@ -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 '
'%(existing)s and %(new)s. Will use the '
'earliest value.',
{'existing': existing_val, 'new': val})
LOG.info(
'"expires_at" has conflicting values '
'%(existing)s and %(new)s. Will use the '
'earliest value.',
{'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'],
'domain' in self.auth['scope'],
'unscoped' in self.auth['scope'],
'system' in self.auth['scope'],
'OS-TRUST:trust' in self.auth['scope']]) != 1:
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
):
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`. '
'Rule list: `%(rules)s` (Enabled: `%(enabled)s`).',
{'user_id': user_id,
'rules': mfa_rules,
'enabled': mfa_rules_enabled})
LOG.debug(
'MFA Rules not processed for user `%(user_id)s`. '
'Rule list: `%(rules)s` (Enabled: `%(enabled)s`).',
{
'user_id': user_id,
'rules': mfa_rules,
'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` '
'matched MFA rule `%(rule)s`. Loaded '
'auth_methods: `%(loaded)s`',
{'user_id': user_id,
'rule': list(r_set),
'methods': auth_methods,
'loaded': cls._auth_methods()})
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,
'rule': list(r_set),
'methods': auth_methods,
'loaded': cls._auth_methods(),
},
)
return True
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})
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},
)
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, '
'no rules loaded.',
{'user_id': user_id})
LOG.error(
'Corrupt rule data structure for user %(user_id)s, '
'no rules loaded.',
{'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 '
'strings.',
{'type': type(r_list)})
LOG.info(
'Ignoring Rule %(type)r; rule must be a list of '
'strings.',
{'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 '
'non-string values.',
{'rule': r_list})
LOG.info(
'Ignoring Rule %(rule)r; rule contains '
'non-string values.',
{'rule': r_list},
)
# Rule is known to be bad, drop it from consideration.
_ok_rule = False
break

View File

@ -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
)

View File

@ -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):

View File

@ -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')

View File

@ -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):

View File

@ -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,
PROVIDERS.resource_api,
PROVIDERS.federation_api,
PROVIDERS.identity_api,
PROVIDERS.assignment_api,
PROVIDERS.role_api)
response_data = handle_unscoped_token(
auth_payload,
PROVIDERS.resource_api,
PROVIDERS.federation_api,
PROVIDERS.identity_api,
PROVIDERS.assignment_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,
identity_provider,
protocol, token_id,
outcome)
notifications.send_saml_audit_notification(
'authenticate',
user_id,
group_ids,
identity_provider,
protocol,
token_id,
outcome,
)
raise
else:
outcome = taxonomy.OUTCOME_SUCCESS
notifications.send_saml_audit_notification('authenticate',
user_id, group_ids,
identity_provider,
protocol, token_id,
outcome)
notifications.send_saml_audit_notification(
'authenticate',
user_id,
group_ids,
identity_provider,
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. "
"Either mapping rules must specify user id/name or "
"REMOTE_USER environment variable must be set.")
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."
)
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'],
)

View File

@ -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
)

View File

@ -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
)

View File

@ -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

View File

@ -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
)

View File

@ -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)

View File

@ -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

View File

@ -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'),
nullable=True,
default=None)
service_id = sql.Column(sql.String(64),
sql.ForeignKey('service.id'),
nullable=False)
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
)
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({
'tenant_id': project_id,
'project_id': project_id,
})
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'),
)

View File

@ -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({
'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', ]
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():
@ -270,12 +284,14 @@ class Catalog(base.CatalogDriverBase):
# { 'interface': 'public', 'url': '<URL>', 'region':
# 'region: '<region_name>' }
if attr.endswith('URL'):
v3_interface = attr[:-len('URL')]
v3_catalog[service_type]['endpoints'].append({
'interface': v3_interface,
'region': region_name,
'url': value,
})
v3_interface = attr[: -len('URL')]
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):

View File

@ -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:

View File

@ -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,
}

View File

@ -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 "
"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})
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},
)
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 '
'user %(username)s.',
{
'role': self.admin_role_name,
'project': self.project_name,
'username': self.admin_username,
},
)
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})
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 '
'the system.',
{'username': self.admin_username,
'role': self.admin_role_name})
LOG.info(
'User %(username)s already has role %(role)s on '
'the system.',
{
'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,
'internal': self.internal_url,
'admin': self.admin_url}
expected_endpoints = {
'public': self.public_url,
'internal': self.internal_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,
'interface': interface,
'url': url,
'service_id': self.service_id,
'enabled': True}
endpoint_ref = {
'id': uuid.uuid4().hex,
'interface': interface,
'url': url,
'service_id': self.service_id,
'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

View File

@ -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

View File

@ -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()

View File

@ -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()
)

View File

@ -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
)

View File

@ -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'
'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)
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
)
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

View File

@ -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
)

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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}),
),
)

View File

@ -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):

View File

@ -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
)

View File

@ -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):

View File

@ -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,
'comparator': comparator,
'case_sensitive': case_sensitive})
self.filters.append(
{
'name': name,
'value': value,
'comparator': comparator,
'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):

View File

@ -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 '
'`[fernet_tokens] max_active_keys = %(max)d`; perhaps '
'there have not been enough key rotations to reach '
'`max_active_keys` yet?')
LOG.debug(msg, {
'count': len(keys),
'max': self.max_active_keys,
'dir': self.key_repository})
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,
{
'count': len(keys),
'max': self.max_active_keys,
'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)]

View File

@ -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):

View File

@ -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,
)
)

View File

@ -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 '
'(%(passed_args)s) => raised '
'%(exception)r')
__msg = (
'[%(run_time)ss] %(function)s '
'(%(passed_args)s) => raised '
'%(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 '
'API that can be referenced by other components '
'of Keystone.')
raise ValueError(
'Programming Error: All managers must provide an '
'API that can be referenced by other components '
'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.

View File

@ -26,11 +26,15 @@ from keystone.i18n import _
CONF = keystone.conf.CONF
LOG = log.getLogger(__name__)
SUPPORTED_HASHERS = frozenset([passlib.hash.bcrypt,
passlib.hash.bcrypt_sha256,
passlib.hash.scrypt,
passlib.hash.pbkdf2_sha512,
passlib.hash.sha512_crypt])
SUPPORTED_HASHERS = frozenset(
[
passlib.hash.bcrypt,
passlib.hash.bcrypt_sha256,
passlib.hash.scrypt,
passlib.hash.pbkdf2_sha512,
passlib.hash.sha512_crypt,
]
)
_HASHER_NAME_MAP = {hasher.name: hasher for hasher in SUPPORTED_HASHERS}
@ -55,20 +59,24 @@ 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)
]
)
}
def _get_hasher_from_ident(hashed):
try:
return _HASHER_IDENT_MAP[hashed[0:hashed.index('$', 1) + 1]]
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

View File

@ -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'}],
),
]

View File

@ -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',
}
],
),
]

View File

@ -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,
),
]

View File

@ -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'},
],
),
]

View File

@ -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',
),
]

View File

@ -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,
),
]

View File

@ -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,
)
),
]

View File

@ -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,
),
]

View File

@ -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 '
'option group within a domain, or a specific '
'configuration option within a group for a domain.'),
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.'
),
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,
)
),
]

View File

@ -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,
)
),
]

View File

@ -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,
),
]

View File

@ -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,
),
]

View File

@ -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 '
'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.'),
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.'
),
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 '
'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.'),
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.'
),
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 '
'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.'),
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.'
),
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 '
'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.'),
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.'
),
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,
)
),
]

View File

@ -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,
),
]

View File

@ -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,
),
]

View File

@ -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,
)
@ -67,76 +67,96 @@ implied_role_policies = [
# files, scope_types should include 'project'.
scope_types=['system', 'project'],
description='Get information about an association between two roles. '
'When a relationship exists between a prior role and an '
'implied role and the prior role is assigned to a user, '
'the user also assumes the implied role.',
'When a relationship exists between a prior role and an '
'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,
scope_types=['system', 'project'],
description='List associations between two roles. When a relationship '
'exists between a prior role and an implied role and the '
'prior role is assigned to a user, the user also assumes '
'the implied role. This will return all the implied roles '
'that would be assumed by the user who gets the specified '
'prior role.',
'exists between a prior role and an implied role and the '
'prior role is assigned to a user, the user also assumes '
'the implied role. This will return all the implied roles '
'that would be assumed by the user who gets the specified '
'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,
scope_types=['system', 'project'],
description='Create an association between two roles. When a '
'relationship exists between a prior role and an implied '
'role and the prior role is assigned to a user, the user '
'also assumes the implied role.',
'relationship exists between a prior role and an 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': '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,
scope_types=['system', 'project'],
description='Delete the association between two roles. When a '
'relationship exists between a prior role and an implied '
'role and the prior role is assigned to a user, the user '
'also assumes the implied role. Removing the association '
'will cause that effect to be eliminated.',
'relationship exists between a prior role and an implied '
'role and the prior role is assigned to a user, the user '
'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,
scope_types=['system', 'project'],
description='List all associations between two roles in the system. '
'When a relationship exists between a prior role and an '
'implied role and the prior role is assigned to a user, '
'the user also assumes the implied role.',
'When a relationship exists between a prior role and an '
'implied role and the prior role is assigned to a user, '
'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,
scope_types=['system', 'project'],
description='Check an association between two roles. When a '
'relationship exists between a prior role and an implied '
'role and the prior role is assigned to a user, the user '
'also assumes the implied role.',
'relationship exists between a prior role and an 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': '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,
),
]

View File

@ -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'}],
),
]

View File

@ -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,
),
]

View File

@ -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,
),
]

View File

@ -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