Trusts
Blueprint trusts creates a trust. Using a trust, one user (the trustee), can then create tokens with a subset of another user's (the trustor) roles and projects. If the impersonate flag in the trust is set, the token user_id is set to the trustor's user ID If the impersonate flag is not set, the token's user_is is set to the trustee's user ID check that both trustor and trustee are enabled prior to creating the trust token. sql and kvs backends sql upgrade scripts unit tests for backends, auth and v3 api modifications to the trust controller for creating tokens Authenticates that only user can be trustor in create Deleting a trust invalidates all tokens created from that trust Adds the trust id and the id of the trustee to the header of the token policy rules for trust This version has a workaround for testing against the KVS version of the Service catalog Change-Id: I5745f4d9a4180b59671a143a55ed87019e98ec76
This commit is contained in:
parent
ab6e552951
commit
601eeb50b6
@ -1,5 +1,9 @@
|
||||
{
|
||||
"admin_required": [["role:admin"], ["is_admin:1"]],
|
||||
"owner" : [["user_id:%(user_id)s"]],
|
||||
"admin_or_owner": [["rule:admin_required"], ["rule:owner"]],
|
||||
|
||||
"default": [["rule:admin_required"]],
|
||||
|
||||
"identity:get_service": [["rule:admin_required"]],
|
||||
"identity:list_services": [["rule:admin_required"]],
|
||||
@ -21,8 +25,9 @@
|
||||
|
||||
"identity:get_project": [["rule:admin_required"]],
|
||||
"identity:list_projects": [["rule:admin_required"]],
|
||||
"identity:list_user_projects": [["rule:admin_required"], ["user_id:%(user_id)s"]],
|
||||
"identity:create_project": [["rule:admin_required"]],
|
||||
"identity:list_user_projects": [["rule:admin_required"],
|
||||
["user_id:%(user_id)s"]],
|
||||
"identity:create_project": [["rule:admin_or_owner"]],
|
||||
"identity:update_project": [["rule:admin_required"]],
|
||||
"identity:delete_project": [["rule:admin_required"]],
|
||||
|
||||
@ -68,5 +73,14 @@
|
||||
"identity:check_token": [["rule:admin_required"]],
|
||||
"identity:validate_token": [["rule:admin_required"]],
|
||||
"identity:revocation_list": [["rule:admin_required"]],
|
||||
"identity:revoke_token": [["rule:admin_required"], ["user_id:%(user_id)s"]]
|
||||
"identity:revoke_token": [["rule:admin_required"],
|
||||
["user_id:%(user_id)s"]],
|
||||
|
||||
"identity:create_trust": [["user_id:%(trust.trustor_user_id)s"]],
|
||||
"identity:get_trust": [["rule:admin_or_owner"]],
|
||||
"identity:list_trusts": [["@"]],
|
||||
"identity:list_roles_for_trust": [["@"]],
|
||||
"identity:check_role_for_trust": [["@"]],
|
||||
"identity:get_role_for_trust": [["@"]],
|
||||
"identity:delete_trust": [["@"]]
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ from keystone import config
|
||||
from keystone import exception
|
||||
from keystone import identity
|
||||
from keystone import token
|
||||
from keystone import trust
|
||||
from keystone.openstack.common import importutils
|
||||
|
||||
|
||||
@ -63,13 +64,15 @@ class AuthInfo(object):
|
||||
|
||||
def __init__(self, context, auth=None):
|
||||
self.identity_api = identity.Manager()
|
||||
self.trust_api = trust.Manager()
|
||||
self.context = context
|
||||
self.auth = auth
|
||||
self._scope_data = (None, None)
|
||||
# self._scope_data is (domain_id, project_id)
|
||||
# project scope: (None, project_id)
|
||||
# domain scope: (domain_id, None)
|
||||
# unscoped: (None, None)
|
||||
self._scope_data = (None, None, None)
|
||||
# self._scope_data is (domain_id, project_id, trust_ref)
|
||||
# project scope: (None, project_id, None)
|
||||
# domain scope: (domain_id, None, None)
|
||||
# trust scope: (None, None, trust_id)
|
||||
# unscoped: (None, None, None)
|
||||
self._validate_and_normalize_auth_data()
|
||||
|
||||
def _assert_project_is_enabled(self, project_ref):
|
||||
@ -136,6 +139,16 @@ class AuthInfo(object):
|
||||
self._assert_project_is_enabled(project_ref)
|
||||
return project_ref
|
||||
|
||||
def _lookup_trust(self, trust_info):
|
||||
trust_id = trust_info.get('id')
|
||||
if not trust_id:
|
||||
raise exception.ValidationError(attribute='trust_id',
|
||||
target='trust')
|
||||
trust = self.trust_api.get_trust(self.context, trust_id)
|
||||
if not trust:
|
||||
raise exception.TrustNotFound(trust_id)
|
||||
return trust
|
||||
|
||||
def lookup_user(self, user_info):
|
||||
user_id = user_info.get('id')
|
||||
user_name = user_info.get('name')
|
||||
@ -165,25 +178,28 @@ class AuthInfo(object):
|
||||
""" Validate and normalize scope data """
|
||||
if 'scope' not in self.auth:
|
||||
return
|
||||
|
||||
# if scoped, only to a project or domain, but not both
|
||||
if ('project' not in self.auth['scope'] and
|
||||
'domain' not in self.auth['scope']):
|
||||
# neither domain or project provided
|
||||
raise exception.ValidationError(attribute='project or domain',
|
||||
target='scope')
|
||||
if ('project' in self.auth['scope'] and
|
||||
'domain' in self.auth['scope']):
|
||||
# both domain and project provided
|
||||
raise exception.ValidationError(attribute='project or domain',
|
||||
target='scope')
|
||||
if sum(['project' in self.auth['scope'],
|
||||
'domain' in self.auth['scope'],
|
||||
'trust' in self.auth['scope']]) != 1:
|
||||
raise exception.ValidationError(
|
||||
attribute='project, domain, or trust',
|
||||
target='scope')
|
||||
|
||||
if 'project' in self.auth['scope']:
|
||||
project_ref = self._lookup_project(self.auth['scope']['project'])
|
||||
self._scope_data = (None, project_ref['id'])
|
||||
else:
|
||||
self._scope_data = (None, project_ref['id'], None)
|
||||
elif 'domain' in self.auth['scope']:
|
||||
domain_ref = self._lookup_domain(self.auth['scope']['domain'])
|
||||
self._scope_data = (domain_ref['id'], None)
|
||||
self._scope_data = (domain_ref['id'], None, None)
|
||||
elif 'trust' in self.auth['scope']:
|
||||
trust_ref = self._lookup_trust(self.auth['scope']['trust'])
|
||||
#TODO ayoung when trusts support domain, Fill in domain data here
|
||||
if 'project_id' in trust_ref:
|
||||
project_ref = self._lookup_project(
|
||||
{'id': trust_ref['project_id']})
|
||||
self._scope_data = (None, project_ref['id'], trust_ref)
|
||||
else:
|
||||
self._scope_data = (None, None, trust_ref)
|
||||
|
||||
def _validate_auth_methods(self):
|
||||
# make sure auth methods are provided
|
||||
@ -236,20 +252,31 @@ class AuthInfo(object):
|
||||
|
||||
Verify and return the scoping information.
|
||||
|
||||
:returns: (domain_id, project_id). If scope to a project,
|
||||
(None, project_id) will be returned. If scope to a domain,
|
||||
(domain_id, None) will be returned. If unscope,
|
||||
(None, None) will be returned.
|
||||
:returns: (domain_id, project_id, trust_ref).
|
||||
If scope to a project, (None, project_id, None)
|
||||
will be returned.
|
||||
If scoped to a domain, (domain_id, None,None)
|
||||
will be returned.
|
||||
If scoped to a trust, (None, project_id, trust_ref),
|
||||
Will be returned, where the project_id comes from the
|
||||
trust definition.
|
||||
If unscoped, (None, None, None) will be returned.
|
||||
|
||||
"""
|
||||
return self._scope_data
|
||||
|
||||
def set_scope(self, domain_id=None, project_id=None):
|
||||
def set_scope(self, domain_id=None, project_id=None, trust=None):
|
||||
""" Set scope information. """
|
||||
if domain_id and project_id:
|
||||
msg = _('Scoping to both domain and project is not allowed')
|
||||
raise ValueError(msg)
|
||||
self._scope_data = (domain_id, project_id)
|
||||
if domain_id and trust:
|
||||
msg = _('Scoping to both domain and trust is not allowed')
|
||||
raise ValueError(msg)
|
||||
if project_id and trust:
|
||||
msg = _('Scoping to both project and trust is not allowed')
|
||||
raise ValueError(msg)
|
||||
self._scope_data = (domain_id, project_id, trust)
|
||||
|
||||
|
||||
class Auth(controller.V3Controller):
|
||||
@ -278,8 +305,10 @@ class Auth(controller.V3Controller):
|
||||
raise exception.Unauthorized(e)
|
||||
|
||||
def _check_and_set_default_scoping(self, context, auth_info, auth_context):
|
||||
(domain_id, project_id) = auth_info.get_scope()
|
||||
if domain_id or project_id:
|
||||
(domain_id, project_id, trust) = auth_info.get_scope()
|
||||
if trust:
|
||||
project_id = trust['project_id']
|
||||
if domain_id or project_id or trust:
|
||||
# scope is specified
|
||||
return
|
||||
|
||||
|
@ -48,6 +48,8 @@ class Token(auth.AuthMethodHandler):
|
||||
token_ref['token_data']['token']['extras'])
|
||||
user_context['method_names'].extend(
|
||||
token_ref['token_data']['token']['methods'])
|
||||
if 'trust' in token_ref['token_data']:
|
||||
raise exception.Forbidden(e)
|
||||
except AssertionError as e:
|
||||
LOG.error(e)
|
||||
raise exception.Unauthorized(e)
|
||||
|
@ -28,6 +28,7 @@ from keystone import config
|
||||
from keystone import exception
|
||||
from keystone import identity
|
||||
from keystone import token as token_module
|
||||
from keystone import trust
|
||||
from keystone.openstack.common import jsonutils
|
||||
from keystone.openstack.common import timeutils
|
||||
|
||||
@ -42,6 +43,7 @@ class TokenDataHelper(object):
|
||||
def __init__(self, context):
|
||||
self.identity_api = identity.Manager()
|
||||
self.catalog_api = catalog.Manager()
|
||||
self.trust_api = trust.Manager()
|
||||
self.context = context
|
||||
|
||||
def _get_filtered_domain(self, domain_id):
|
||||
@ -100,33 +102,77 @@ class TokenDataHelper(object):
|
||||
roles = self._get_project_roles_for_user(user_id, project_id)
|
||||
return roles
|
||||
|
||||
def _populate_user(self, token_data, user_id, domain_id, project_id):
|
||||
def _populate_user(self, token_data, user_id, domain_id, project_id,
|
||||
trust):
|
||||
user_ref = self.identity_api.get_user(self.context,
|
||||
user_id)
|
||||
if trust:
|
||||
trustor_user_ref = (self.identity_api.get_user(self.context,
|
||||
trust['trustor_user_id']))
|
||||
if not trustor_user_ref['enabled']:
|
||||
raise exception.Forbidden()
|
||||
if trust['impersonation']:
|
||||
user_ref = trustor_user_ref
|
||||
token_data['trust'] = (
|
||||
{
|
||||
'id': trust['id'],
|
||||
'trustor_user': {'id': trust['trustor_user_id']},
|
||||
'trustee_user': {'id': trust['trustee_user_id']},
|
||||
'impersonation': trust['impersonation']
|
||||
})
|
||||
filtered_user = {
|
||||
'id': user_ref['id'],
|
||||
'name': user_ref['name'],
|
||||
'domain': self._get_filtered_domain(user_ref['domain_id'])}
|
||||
token_data['user'] = filtered_user
|
||||
|
||||
def _populate_roles(self, token_data, user_id, domain_id, project_id):
|
||||
if domain_id or project_id:
|
||||
roles = self._get_roles_for_user(user_id, domain_id, project_id)
|
||||
# we only care about id and name
|
||||
def _populate_roles(self, token_data, user_id, domain_id, project_id,
|
||||
trust):
|
||||
if trust:
|
||||
token_user_id = trust['trustor_user_id']
|
||||
token_project_id = trust['project_id']
|
||||
#trusts do not support domains yet
|
||||
token_domain_id = None
|
||||
else:
|
||||
token_user_id = user_id
|
||||
token_project_id = project_id
|
||||
token_domain_id = domain_id
|
||||
|
||||
if token_domain_id or token_project_id:
|
||||
roles = self._get_roles_for_user(token_user_id,
|
||||
token_domain_id,
|
||||
token_project_id)
|
||||
filtered_roles = []
|
||||
for role in roles:
|
||||
filtered_roles.append({'id': role['id'], 'name': role['name']})
|
||||
if trust:
|
||||
for trust_role in trust['roles']:
|
||||
match_roles = [x for x in roles
|
||||
if x['id'] == trust_role['id']]
|
||||
if match_roles:
|
||||
filtered_roles.append(match_roles[0])
|
||||
else:
|
||||
raise exception.Forbidden()
|
||||
else:
|
||||
for role in roles:
|
||||
filtered_roles.append({'id': role['id'],
|
||||
'name': role['name']})
|
||||
token_data['roles'] = filtered_roles
|
||||
|
||||
def _populate_service_catalog(self, token_data, user_id,
|
||||
domain_id, project_id):
|
||||
domain_id, project_id, trust):
|
||||
if trust:
|
||||
user_id = trust['trustor_user_id']
|
||||
if project_id or domain_id:
|
||||
service_catalog = self.catalog_api.get_v3_catalog(
|
||||
self.context, user_id, project_id)
|
||||
try:
|
||||
service_catalog = self.catalog_api.get_v3_catalog(
|
||||
self.context, user_id, project_id)
|
||||
#TODO KVS backend needs a sample implementation
|
||||
except exception.NotImplemented:
|
||||
service_catalog = {}
|
||||
# TODO(gyee): v3 service catalog is not quite completed yet
|
||||
#TODO Enforce Endpoints for trust
|
||||
token_data['catalog'] = service_catalog
|
||||
|
||||
def _populate_token(self, token_data, expires=None):
|
||||
def _populate_token(self, token_data, expires=None, trust=None):
|
||||
if not expires:
|
||||
expires = token_module.default_expire_time()
|
||||
if not isinstance(expires, basestring):
|
||||
@ -135,15 +181,20 @@ class TokenDataHelper(object):
|
||||
token_data['issued_at'] = timeutils.isotime(subsecond=True)
|
||||
|
||||
def get_token_data(self, user_id, method_names, extras,
|
||||
domain_id=None, project_id=None, expires=None):
|
||||
domain_id=None, project_id=None, expires=None,
|
||||
trust=None):
|
||||
token_data = {'methods': method_names,
|
||||
'extras': extras}
|
||||
if trust:
|
||||
if user_id != trust['trustee_user_id']:
|
||||
raise exception.Forbidden()
|
||||
|
||||
self._populate_scope(token_data, domain_id, project_id)
|
||||
self._populate_user(token_data, user_id, domain_id, project_id)
|
||||
self._populate_roles(token_data, user_id, domain_id, project_id)
|
||||
self._populate_user(token_data, user_id, domain_id, project_id, trust)
|
||||
self._populate_roles(token_data, user_id, domain_id, project_id, trust)
|
||||
self._populate_service_catalog(token_data, user_id, domain_id,
|
||||
project_id)
|
||||
self._populate_token(token_data, expires)
|
||||
project_id, trust)
|
||||
self._populate_token(token_data, expires, trust)
|
||||
return {'token': token_data}
|
||||
|
||||
|
||||
@ -189,7 +240,7 @@ def recreate_token_data(context, token_data=None, expires=None,
|
||||
|
||||
def create_token(context, auth_context, auth_info):
|
||||
token_data_helper = TokenDataHelper(context)
|
||||
(domain_id, project_id) = auth_info.get_scope()
|
||||
(domain_id, project_id, trust) = auth_info.get_scope()
|
||||
method_names = list(set(auth_info.get_method_names() +
|
||||
auth_context.get('method_names', [])))
|
||||
token_data = token_data_helper.get_token_data(
|
||||
@ -198,7 +249,9 @@ def create_token(context, auth_context, auth_info):
|
||||
auth_context['extras'],
|
||||
domain_id,
|
||||
project_id,
|
||||
auth_context.get('expires_at', None))
|
||||
auth_context.get('expires_at', None),
|
||||
trust)
|
||||
|
||||
if CONF.signing.token_format == 'UUID':
|
||||
token_id = uuid.uuid4().hex
|
||||
elif CONF.signing.token_format == 'PKI':
|
||||
@ -214,7 +267,7 @@ def create_token(context, auth_context, auth_info):
|
||||
try:
|
||||
expiry = token_data['token']['expires_at']
|
||||
if isinstance(expiry, basestring):
|
||||
expiry = timeutils.parse_isotime(expiry)
|
||||
expiry = timeutils.normalize_time(timeutils.parse_isotime(expiry))
|
||||
role_ids = []
|
||||
if 'project' in token_data['token']:
|
||||
# project-scoped token, fill in the v2 token data
|
||||
|
@ -148,7 +148,8 @@ def filterprotected(*filters):
|
||||
return _filterprotected
|
||||
|
||||
|
||||
@dependency.requires('identity_api', 'policy_api', 'token_api', 'catalog_api')
|
||||
@dependency.requires('identity_api', 'policy_api', 'token_api',
|
||||
'trust_api', 'catalog_api')
|
||||
class V2Controller(wsgi.Application):
|
||||
"""Base controller class for Identity API v2."""
|
||||
|
||||
|
@ -42,6 +42,7 @@ class Token(Model):
|
||||
user
|
||||
tenant
|
||||
metadata
|
||||
trust_id
|
||||
"""
|
||||
|
||||
required_keys = ('id', 'expires')
|
||||
@ -147,3 +148,17 @@ class Role(Model):
|
||||
|
||||
required_keys = ('id', 'name')
|
||||
optional_keys = tuple()
|
||||
|
||||
|
||||
class Trust(Model):
|
||||
"""Trust object.
|
||||
|
||||
Required keys:
|
||||
id
|
||||
trustor_user_id
|
||||
trustee_user_id
|
||||
project_id
|
||||
"""
|
||||
|
||||
required_keys = ('id', 'trustor_user_id', 'trustee_user_id', 'project_id')
|
||||
optional_keys = tuple('expires_at')
|
||||
|
@ -0,0 +1,68 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import migrate
|
||||
import sqlalchemy as sql
|
||||
|
||||
|
||||
def upgrade(migrate_engine):
|
||||
# Upgrade operations go here. Don't create your own engine; bind
|
||||
# migrate_engine to your metadata
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
|
||||
user_table = sql.Table('user', meta, autoload=True)
|
||||
role_table = sql.Table('role', meta, autoload=True)
|
||||
tenant_table = sql.Table('project', meta, autoload=True)
|
||||
|
||||
trust_table = sql.Table(
|
||||
'trust',
|
||||
meta,
|
||||
sql.Column('id', sql.String(64), primary_key=True),
|
||||
sql.Column('trustor_user_id',
|
||||
sql.String(64),
|
||||
unique=False,
|
||||
nullable=False,),
|
||||
sql.Column('trustee_user_id',
|
||||
sql.String(64),
|
||||
unique=False,
|
||||
nullable=False),
|
||||
sql.Column('project_id', sql.String(64),
|
||||
unique=False,
|
||||
nullable=True),
|
||||
sql.Column("impersonation", sql.types.Boolean, nullable=False),
|
||||
sql.Column("deleted_at", sql.types.DateTime, nullable=True),
|
||||
sql.Column("expires_at", sql.types.DateTime, nullable=True),
|
||||
sql.Column('extra', sql.Text()))
|
||||
trust_table.create(migrate_engine, checkfirst=True)
|
||||
|
||||
trust_role_table = sql.Table(
|
||||
'trust_role',
|
||||
meta,
|
||||
sql.Column('trust_id', sql.String(64), primary_key=True,
|
||||
nullable=False),
|
||||
sql.Column('role_id', sql.String(64), primary_key=True,
|
||||
nullable=False))
|
||||
trust_role_table.create(migrate_engine, checkfirst=True)
|
||||
|
||||
|
||||
def downgrade(migrate_engine):
|
||||
meta = sql.MetaData()
|
||||
meta.bind = migrate_engine
|
||||
# Operations to reverse the above upgrade go here.
|
||||
for table_name in ['trust_role', 'trust']:
|
||||
table = sql.Table(table_name, meta, autoload=True)
|
||||
table.drop()
|
@ -235,6 +235,8 @@ register_str('driver', group='policy',
|
||||
default='keystone.policy.backends.sql.Policy')
|
||||
register_str('driver', group='token',
|
||||
default='keystone.token.backends.kvs.Token')
|
||||
register_str('driver', group='trust',
|
||||
default='keystone.trust.backends.sql.Trust')
|
||||
register_str('driver', group='ec2',
|
||||
default='keystone.contrib.ec2.backends.kvs.Ec2')
|
||||
register_str('driver', group='stats',
|
||||
|
@ -190,6 +190,10 @@ class GroupNotFound(NotFound):
|
||||
"""Could not find group: %(group_id)s"""
|
||||
|
||||
|
||||
class TrustNotFound(NotFound):
|
||||
"""Could not find trust: %(trust_id)s"""
|
||||
|
||||
|
||||
class Conflict(Error):
|
||||
"""Conflict occurred attempting to store %(type)s.
|
||||
|
||||
|
@ -14,8 +14,6 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import functools
|
||||
|
||||
from keystone import clean
|
||||
from keystone import config
|
||||
from keystone.common import sql
|
||||
@ -195,18 +193,7 @@ class Identity(sql.Base, identity.Driver):
|
||||
# FIXME(gyee): this should really be
|
||||
# get_roles_for_user_and_project() after the dusts settle
|
||||
if tenant_id not in self.get_projects_for_user(user_id):
|
||||
# get_roles_for_user_and_project() returns a set
|
||||
roles = []
|
||||
try:
|
||||
roles = self.get_roles_for_user_and_project(user_id,
|
||||
tenant_id)
|
||||
except:
|
||||
# FIXME(gyee): we should never get into this situation
|
||||
# after user project role migration is completed
|
||||
pass
|
||||
if not roles:
|
||||
raise AssertionError('Invalid tenant')
|
||||
|
||||
raise AssertionError('Invalid project')
|
||||
try:
|
||||
tenant_ref = self.get_project(tenant_id)
|
||||
metadata_ref = self.get_metadata(user_id, tenant_id)
|
||||
@ -215,7 +202,6 @@ class Identity(sql.Base, identity.Driver):
|
||||
metadata_ref = {}
|
||||
except exception.MetadataNotFound:
|
||||
metadata_ref = {}
|
||||
|
||||
return (identity.filter_user(user_ref), tenant_ref, metadata_ref)
|
||||
|
||||
def get_project(self, tenant_id):
|
||||
@ -622,6 +608,7 @@ class Identity(sql.Base, identity.Driver):
|
||||
raise exception.DomainNotFound(domain_id=domain_id)
|
||||
return ref.to_dict()
|
||||
|
||||
@sql.handle_conflicts(type='domain')
|
||||
def get_domain_by_name(self, domain_name):
|
||||
session = self.get_session()
|
||||
try:
|
||||
|
@ -161,6 +161,24 @@ class Tenant(controller.V2Controller):
|
||||
return o
|
||||
|
||||
|
||||
def delete_tokens_for_user(token_api, trust_api, context, user_id, user):
|
||||
try:
|
||||
#First delete tokens that could get other tokens.
|
||||
for token_id in token_api.list_tokens(context, user_id):
|
||||
token_api.delete_token(context, token_id)
|
||||
#now delete trust tokens
|
||||
for trust_id in (trust_api.list_trusts_for_trustee(context, user_id)):
|
||||
token_list = token_api.list_tokens(context, userid,
|
||||
trust_id=trust_id)
|
||||
for token in token_list:
|
||||
token_api.delete_token(context, token)
|
||||
except exception.NotImplemented:
|
||||
# The users status has been changed but tokens remain valid for
|
||||
# backends that can't list tokens for users
|
||||
LOG.warning('User %s status has changed, but existing tokens '
|
||||
'remain valid' % user_id)
|
||||
|
||||
|
||||
class User(controller.V2Controller):
|
||||
def get_user(self, context, user_id):
|
||||
self.assert_admin(context)
|
||||
@ -215,16 +233,12 @@ class User(controller.V2Controller):
|
||||
self.assert_admin(context)
|
||||
user_ref = self.identity_api.update_user(context, user_id, user)
|
||||
|
||||
# If the password was changed or the user was disabled we clear tokens
|
||||
if user.get('password') or not user.get('enabled', True):
|
||||
try:
|
||||
for token_id in self.token_api.list_tokens(context, user_id):
|
||||
self.token_api.delete_token(context, token_id)
|
||||
except exception.NotImplemented:
|
||||
# The users status has been changed but tokens remain valid for
|
||||
# backends that can't list tokens for users
|
||||
LOG.warning('User %s status has changed, but existing tokens '
|
||||
'remain valid' % user_id)
|
||||
# If the password was changed or the user was disabled we clear tokens
|
||||
delete_tokens_for_user(self.token_api, self.trust_api,
|
||||
context,
|
||||
user_id,
|
||||
user)
|
||||
return {'user': self._filter_domain_id(user_ref)}
|
||||
|
||||
def delete_user(self, context, user_id):
|
||||
@ -329,7 +343,8 @@ class Role(controller.V2Controller):
|
||||
context, user_id, tenant_id, role_id)
|
||||
roles = self.identity_api.get_roles_for_user_and_project(
|
||||
context, user_id, tenant_id)
|
||||
self.token_api.revoke_tokens(context, user_id, tenant_id)
|
||||
delete_tokens_for_user(self.token_api, self.trust_api, context,
|
||||
user_id, tenant_id)
|
||||
|
||||
# COMPAT(diablo): CRUD extension
|
||||
def get_role_refs(self, context, user_id):
|
||||
|
@ -25,6 +25,7 @@ from keystone import identity
|
||||
from keystone import policy
|
||||
from keystone import routers
|
||||
from keystone import token
|
||||
from keystone import trust
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -34,7 +35,8 @@ DRIVERS = dict(
|
||||
ec2_api=ec2.Manager(),
|
||||
identity_api=identity.Manager(),
|
||||
policy_api=policy.Manager(),
|
||||
token_api=token.Manager())
|
||||
token_api=token.Manager(),
|
||||
trust_api=trust.Manager())
|
||||
|
||||
|
||||
@logging.fail_gracefully
|
||||
@ -81,7 +83,7 @@ def v3_app_factory(global_conf, **local_conf):
|
||||
conf.update(local_conf)
|
||||
mapper = routes.Mapper()
|
||||
v3routers = []
|
||||
for module in [auth, catalog, identity, policy]:
|
||||
for module in [auth, catalog, identity, policy, trust]:
|
||||
module.routers.append_v3_routers(mapper, v3routers)
|
||||
# TODO(ayoung): put token routes here
|
||||
return wsgi.ComposingRouter(mapper, v3routers)
|
||||
|
@ -39,6 +39,7 @@ from keystone import exception
|
||||
from keystone import identity
|
||||
from keystone import policy
|
||||
from keystone import token
|
||||
from keystone import trust
|
||||
|
||||
|
||||
do_monkeypatch = not os.getenv('STANDARD_THREADS')
|
||||
@ -74,6 +75,7 @@ def initialize_drivers():
|
||||
DRIVERS['identity_api'] = identity.Manager()
|
||||
DRIVERS['policy_api'] = policy.Manager()
|
||||
DRIVERS['token_api'] = token.Manager()
|
||||
DRIVERS['trust_api'] = trust.Manager()
|
||||
return DRIVERS
|
||||
|
||||
|
||||
|
@ -31,7 +31,11 @@ class Token(kvs.Base, token.Driver):
|
||||
ref = self.db.get('token-%s' % token_id)
|
||||
except exception.NotFound:
|
||||
raise exception.TokenNotFound(token_id=token_id)
|
||||
if ref['expires'] is None or ref['expires'] > timeutils.utcnow():
|
||||
now = timeutils.utcnow()
|
||||
expiry = ref['expires']
|
||||
if expiry is None:
|
||||
raise exception.TokenNotFound(token_id=token_id)
|
||||
if expiry > now:
|
||||
return copy.deepcopy(ref)
|
||||
else:
|
||||
raise exception.TokenNotFound(token_id=token_id)
|
||||
@ -39,8 +43,10 @@ class Token(kvs.Base, token.Driver):
|
||||
def create_token(self, token_id, data):
|
||||
token_id = token.unique_id(token_id)
|
||||
data_copy = copy.deepcopy(data)
|
||||
if 'expires' not in data:
|
||||
if not data_copy.get('expires'):
|
||||
data_copy['expires'] = token.default_expire_time()
|
||||
if 'trust_id' in data and data['trust_id'] is None:
|
||||
data_copy.pop('trust_id')
|
||||
self.db.set('token-%s' % token_id, data_copy)
|
||||
return copy.deepcopy(data_copy)
|
||||
|
||||
@ -53,7 +59,7 @@ class Token(kvs.Base, token.Driver):
|
||||
except exception.NotFound:
|
||||
raise exception.TokenNotFound(token_id=token_id)
|
||||
|
||||
def list_tokens(self, user_id, tenant_id=None):
|
||||
def list_tokens(self, user_id, tenant_id=None, trust_id=None):
|
||||
tokens = []
|
||||
now = timeutils.utcnow()
|
||||
for token, ref in self.db.items():
|
||||
@ -72,6 +78,10 @@ class Token(kvs.Base, token.Driver):
|
||||
continue
|
||||
if tenant.get('id') != tenant_id:
|
||||
continue
|
||||
if trust_id is not None:
|
||||
trust = ref.get('trust_id')
|
||||
if not trust:
|
||||
continue
|
||||
tokens.append(token.split('-', 1)[1])
|
||||
return tokens
|
||||
|
||||
|
@ -64,7 +64,7 @@ class Token(token.Driver):
|
||||
def create_token(self, token_id, data):
|
||||
data_copy = copy.deepcopy(data)
|
||||
ptk = self._prefix_token_id(token.unique_id(token_id))
|
||||
if 'expires' not in data_copy:
|
||||
if not data_copy.get('expires'):
|
||||
data_copy['expires'] = token.default_expire_time()
|
||||
kwargs = {}
|
||||
if data_copy['expires'] is not None:
|
||||
@ -99,7 +99,7 @@ class Token(token.Driver):
|
||||
self._add_to_revocation_list(data)
|
||||
return result
|
||||
|
||||
def list_tokens(self, user_id, tenant_id=None):
|
||||
def list_tokens(self, user_id, tenant_id=None, trust_id=None):
|
||||
tokens = []
|
||||
user_key = self._prefix_user_id(user_id)
|
||||
user_record = self.client.get(user_key) or ""
|
||||
@ -114,6 +114,13 @@ class Token(token.Driver):
|
||||
continue
|
||||
if tenant.get('id') != tenant_id:
|
||||
continue
|
||||
if trust_id is not None:
|
||||
trust = token_ref.get('trust_id')
|
||||
if not trust:
|
||||
continue
|
||||
if trust != trust_id:
|
||||
continue
|
||||
|
||||
tokens.append(token_id)
|
||||
return tokens
|
||||
|
||||
|
@ -43,16 +43,18 @@ class Token(sql.Base, token.Driver):
|
||||
query = query.filter_by(id=token.unique_id(token_id), valid=True)
|
||||
token_ref = query.first()
|
||||
now = datetime.datetime.utcnow()
|
||||
if token_ref and (not token_ref.expires or now < token_ref.expires):
|
||||
return token_ref.to_dict()
|
||||
else:
|
||||
if not token_ref:
|
||||
raise exception.TokenNotFound(token_id=token_id)
|
||||
if not token_ref.expires:
|
||||
raise exception.TokenNotFound(token_id=token_id)
|
||||
if now >= token_ref.expires:
|
||||
raise exception.TokenNotFound(token_id=token_id)
|
||||
return token_ref.to_dict()
|
||||
|
||||
def create_token(self, token_id, data):
|
||||
data_copy = copy.deepcopy(data)
|
||||
if 'expires' not in data_copy:
|
||||
if not data_copy.get('expires'):
|
||||
data_copy['expires'] = token.default_expire_time()
|
||||
|
||||
token_ref = TokenModel.from_dict(data_copy)
|
||||
token_ref.id = token.unique_id(token_id)
|
||||
token_ref.valid = True
|
||||
@ -73,7 +75,7 @@ class Token(sql.Base, token.Driver):
|
||||
token_ref.valid = False
|
||||
session.flush()
|
||||
|
||||
def list_tokens(self, user_id, tenant_id=None):
|
||||
def list_tokens(self, user_id, tenant_id=None, trust_id=None):
|
||||
session = self.get_session()
|
||||
tokens = []
|
||||
now = timeutils.utcnow()
|
||||
@ -93,6 +95,12 @@ class Token(sql.Base, token.Driver):
|
||||
continue
|
||||
if tenant.get('id') != tenant_id:
|
||||
continue
|
||||
if trust_id is not None:
|
||||
token_trust_id = token_ref_dict.get('trust_id')
|
||||
if not token_trust_id:
|
||||
continue
|
||||
if token_trust_id != trust_id:
|
||||
continue
|
||||
tokens.append(token_ref['id'])
|
||||
return tokens
|
||||
|
||||
|
@ -21,7 +21,7 @@ class ExternalAuthNotApplicable(Exception):
|
||||
pass
|
||||
|
||||
|
||||
@dependency.requires('catalog_api')
|
||||
@dependency.requires('catalog_api', 'trust_api', 'token_api')
|
||||
class Auth(controller.V2Controller):
|
||||
def ca_cert(self, context, auth=None):
|
||||
ca_file = open(CONF.signing.ca_certs, 'r')
|
||||
@ -78,6 +78,7 @@ class Auth(controller.V2Controller):
|
||||
context, auth)
|
||||
|
||||
user_ref, tenant_ref, metadata_ref, expiry = auth_info
|
||||
trust_id = metadata_ref.get('trust_id')
|
||||
user_ref = self._filter_domain_id(user_ref)
|
||||
if tenant_ref:
|
||||
tenant_ref = self._filter_domain_id(tenant_ref)
|
||||
@ -128,7 +129,8 @@ class Auth(controller.V2Controller):
|
||||
expires=auth_token_data['expires'],
|
||||
user=user_ref,
|
||||
tenant=tenant_ref,
|
||||
metadata=metadata_ref))
|
||||
metadata=metadata_ref,
|
||||
trust_id=trust_id))
|
||||
except Exception as e:
|
||||
# an identical token may have been created already.
|
||||
# if so, return the token_data as it is also identical
|
||||
@ -166,11 +168,43 @@ class Auth(controller.V2Controller):
|
||||
except exception.NotFound as e:
|
||||
raise exception.Unauthorized(e)
|
||||
|
||||
#A trust token cannot be used to get another token
|
||||
if 'trust' in old_token_ref:
|
||||
raise exception.Unauthorized()
|
||||
if 'trust_id' in old_token_ref["metadata"]:
|
||||
raise exception.Forbidden()
|
||||
|
||||
user_ref = old_token_ref['user']
|
||||
user_id = user_ref['id']
|
||||
if 'trust_id' in auth:
|
||||
trust_ref = self.trust_api.get_trust(context, auth['trust_id'])
|
||||
if trust_ref is None:
|
||||
raise exception.Forbidden()
|
||||
if user_id != trust_ref['trustee_user_id']:
|
||||
raise exception.Forbidden()
|
||||
if ('expires' in trust_ref) and (trust_ref['expires']):
|
||||
expiry = trust_ref['expires']
|
||||
if expiry < timeutils.parse_isotime(timeutils.isotime()):
|
||||
raise exception.Forbidden()()
|
||||
user_id = trust_ref['trustor_user_id']
|
||||
trustor_user_ref = (self.identity_api.get_user(
|
||||
context=context,
|
||||
user_id=trust_ref['trustor_user_id']))
|
||||
if not trustor_user_ref['enabled']:
|
||||
raise exception.Forbidden()()
|
||||
trustee_user_ref = self.identity_api.get_user(
|
||||
context, trust_ref['trustee_user_id'])
|
||||
if not trustee_user_ref['enabled']:
|
||||
raise exception.Forbidden()()
|
||||
if trust_ref['impersonation'] == 'True':
|
||||
current_user_ref = trustor_user_ref
|
||||
else:
|
||||
current_user_ref = trustee_user_ref
|
||||
|
||||
current_user_ref = self.identity_api.get_user(context=context,
|
||||
user_id=user_id)
|
||||
else:
|
||||
tenant_id = self._get_project_id_from_auth(context, auth)
|
||||
current_user_ref = self.identity_api.get_user(context=context,
|
||||
user_id=user_id)
|
||||
|
||||
tenant_id = self._get_project_id_from_auth(context, auth)
|
||||
|
||||
@ -185,6 +219,28 @@ class Auth(controller.V2Controller):
|
||||
context, user_id, tenant_id))
|
||||
|
||||
expiry = old_token_ref['expires']
|
||||
if 'trust_id' in auth:
|
||||
trust_id = auth['trust_id']
|
||||
trust_roles = []
|
||||
for role in trust_ref['roles']:
|
||||
if not 'roles' in metadata_ref:
|
||||
raise exception.Forbidden()()
|
||||
if role['id'] in metadata_ref['roles']:
|
||||
trust_roles.append(role['id'])
|
||||
else:
|
||||
raise exception.Forbidden()
|
||||
if 'expiry' in trust_ref and trust_ref['expiry']:
|
||||
trust_expiry = timeutils.parse_isotime(trust_ref['expiry'])
|
||||
if trust_expiry < expiry:
|
||||
expiry = trust_expiry
|
||||
metadata_ref['roles'] = trust_roles
|
||||
metadata_ref['trustee_user_id'] = trust_ref['trustee_user_id']
|
||||
metadata_ref['trust_id'] = trust_id
|
||||
|
||||
auth_token_data = self._get_auth_token_data(current_user_ref,
|
||||
tenant_ref,
|
||||
metadata_ref,
|
||||
expiry)
|
||||
return (current_user_ref, tenant_ref, metadata_ref, expiry)
|
||||
|
||||
def _authenticate_local(self, context, auth):
|
||||
@ -526,7 +582,12 @@ class Auth(controller.V2Controller):
|
||||
else:
|
||||
o['access']['metadata'] = {'is_admin': 0}
|
||||
if 'roles' in metadata_ref:
|
||||
o['access']['metadata']['roles'] = metadata_ref['roles']
|
||||
o['access']['metadata']['roles'] = metadata_ref['roles']
|
||||
if 'trust_id' in metadata_ref:
|
||||
o['access']['trust'] = {'trustee_user_id':
|
||||
metadata_ref['trustee_user_id'],
|
||||
'id': metadata_ref['trust_id']
|
||||
}
|
||||
return o
|
||||
|
||||
@classmethod
|
||||
|
@ -179,11 +179,15 @@ class Driver(object):
|
||||
"""
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_tokens(self, user_id):
|
||||
def list_tokens(self, user_id, tenant_id=None, trust_id=None):
|
||||
"""Returns a list of current token_id's for a user
|
||||
|
||||
:param user_id: identity of the user
|
||||
:type user_id: string
|
||||
:param tenant_id: identity of the tenant
|
||||
:type tenant_id: string
|
||||
:param trust_id: identified of the trust
|
||||
:type trust_id: string
|
||||
:returns: list of token_id's
|
||||
|
||||
"""
|
||||
|
19
keystone/trust/__init__.py
Normal file
19
keystone/trust/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone.trust.core import Manager, Driver
|
||||
from keystone.trust import controllers
|
||||
from keystone.trust import routers
|
0
keystone/trust/backends/__init__.py
Normal file
0
keystone/trust/backends/__init__.py
Normal file
92
keystone/trust/backends/kvs.py
Normal file
92
keystone/trust/backends/kvs.py
Normal file
@ -0,0 +1,92 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""
|
||||
An in memory implementation of the trusts API.
|
||||
only to be used for testing purposes
|
||||
"""
|
||||
import copy
|
||||
import datetime
|
||||
|
||||
|
||||
from keystone.common import kvs
|
||||
from keystone.openstack.common import timeutils
|
||||
from keystone import exception
|
||||
from keystone import trust
|
||||
|
||||
|
||||
class Trust(kvs.Base, trust.Driver):
|
||||
def create_trust(self, trust_id, trust, roles):
|
||||
trust_ref = trust
|
||||
trust_ref['id'] = trust_id
|
||||
trust_ref['deleted'] = False
|
||||
trust_ref['roles'] = roles
|
||||
if (trust_ref.get('expires_at') and
|
||||
trust_ref['expires_at'].tzinfo is not None):
|
||||
trust_ref['expires_at'] = (timeutils.normalize_time
|
||||
(trust_ref['expires_at']))
|
||||
|
||||
self.db.set('trust-%s' % trust_id, trust_ref)
|
||||
trustee_user_id = trust_ref['trustee_user_id']
|
||||
trustee_list = self.db.get('trustee-%s' % trustee_user_id, [])
|
||||
trustee_list.append(trust_id)
|
||||
self.db.set('trustee-%s' % trustee_user_id, trustee_list)
|
||||
trustor_user_id = trust_ref['trustor_user_id']
|
||||
trustor_list = self.db.get('trustor-%s' % trustor_user_id, [])
|
||||
trustor_list.append(trust_id)
|
||||
self.db.set('trustor-%s' % trustor_user_id, trustor_list)
|
||||
return copy.deepcopy(trust_ref)
|
||||
|
||||
def _filter_trust(selfself, ref):
|
||||
if ref['deleted']:
|
||||
return None
|
||||
if ref.get('expires_at') and timeutils.utcnow() > ref['expires_at']:
|
||||
return None
|
||||
ref = copy.deepcopy(ref)
|
||||
return ref
|
||||
|
||||
def get_trust(self, trust_id):
|
||||
try:
|
||||
ref = self.db.get('trust-%s' % trust_id)
|
||||
return self._filter_trust(ref)
|
||||
except exception.NotFound:
|
||||
return None
|
||||
|
||||
def delete_trust(self, trust_id):
|
||||
try:
|
||||
ref = self.db.get('trust-%s' % trust_id)
|
||||
except exception.NotFound:
|
||||
raise exception.TrustNotFound(token_id=token_id)
|
||||
ref['deleted'] = True
|
||||
self.db.set('trust-%s' % trust_id, ref)
|
||||
|
||||
def list_trusts(self):
|
||||
trusts = []
|
||||
for key, value in self.db.items():
|
||||
if key.startswith("trust-") and not value['deleted']:
|
||||
trusts.append(value)
|
||||
return trusts
|
||||
|
||||
def list_trusts_for_trustee(self, trustee_user_id):
|
||||
trusts = []
|
||||
for trust in self.db.get('trustee-%s' % trustee_user_id, []):
|
||||
trusts.append(self.get_trust(trust))
|
||||
return trusts
|
||||
|
||||
def list_trusts_for_trustor(self, trustor_user_id):
|
||||
trusts = []
|
||||
for trust in self.db.get('trustor-%s' % trustor_user_id, []):
|
||||
trusts.append(self.get_trust(trust))
|
||||
return trusts
|
123
keystone/trust/backends/sql.py
Normal file
123
keystone/trust/backends/sql.py
Normal file
@ -0,0 +1,123 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystone.common import sql
|
||||
from keystone import exception
|
||||
from keystone.openstack.common import timeutils
|
||||
from keystone import trust
|
||||
|
||||
|
||||
class TrustModel(sql.ModelBase, sql.DictBase):
|
||||
__tablename__ = 'trust'
|
||||
attributes = ['id', 'trustor_user_id', 'trustee_user_id',
|
||||
'project_id', 'impersonation', 'expires_at']
|
||||
id = sql.Column(sql.String(64), primary_key=True)
|
||||
#user id Of owner
|
||||
trustor_user_id = sql.Column(sql.String(64), unique=False, nullable=False,)
|
||||
#user_id of user allowed to consume this preauth
|
||||
trustee_user_id = sql.Column(sql.String(64), unique=False, nullable=False)
|
||||
project_id = sql.Column(sql.String(64), unique=False, nullable=True)
|
||||
impersonation = sql.Column(sql.Boolean)
|
||||
deleted_at = sql.Column(sql.DateTime)
|
||||
expires_at = sql.Column(sql.DateTime)
|
||||
extra = sql.Column(sql.JsonBlob())
|
||||
|
||||
|
||||
class TrustRole(sql.ModelBase):
|
||||
__tablename__ = 'trust_role'
|
||||
attributes = ['trust_id', 'role_id']
|
||||
trust_id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||
role_id = sql.Column(sql.String(64), primary_key=True, nullable=False)
|
||||
|
||||
|
||||
class Trust(sql.Base, trust.Driver):
|
||||
@sql.handle_conflicts(type='trust')
|
||||
def create_trust(self, trust_id, trust, roles):
|
||||
session = self.get_session()
|
||||
with session.begin():
|
||||
ref = TrustModel.from_dict(trust)
|
||||
ref['id'] = trust_id
|
||||
if ref.get('expires_at') and ref['expires_at'].tzinfo is not None:
|
||||
ref['expires_at'] = timeutils.normalize_time(ref['expires_at'])
|
||||
session.add(ref)
|
||||
added_roles = []
|
||||
for role in roles:
|
||||
trust_role = TrustRole()
|
||||
trust_role.trust_id = trust_id
|
||||
trust_role.role_id = role['id']
|
||||
added_roles.append({'id': role['id']})
|
||||
session.add(trust_role)
|
||||
session.flush()
|
||||
trust_dict = ref.to_dict()
|
||||
trust_dict['roles'] = added_roles
|
||||
return trust_dict
|
||||
|
||||
def _add_roles(self, trust_id, session, trust_dict):
|
||||
roles = []
|
||||
for role in session.query(TrustRole).filter_by(trust_id=trust_id):
|
||||
roles.append({'id': role.role_id})
|
||||
trust_dict['roles'] = roles
|
||||
|
||||
@sql.handle_conflicts(type='trust')
|
||||
def get_trust(self, trust_id):
|
||||
session = self.get_session()
|
||||
ref = (session.query(TrustModel).
|
||||
filter_by(deleted_at=None).
|
||||
filter_by(id=trust_id).first())
|
||||
if ref is None:
|
||||
return None
|
||||
if ref.expires_at is not None:
|
||||
now = timeutils.utcnow()
|
||||
if now > ref.expires_at:
|
||||
return None
|
||||
trust_dict = ref.to_dict()
|
||||
|
||||
self._add_roles(trust_id, session, trust_dict)
|
||||
return trust_dict
|
||||
|
||||
@sql.handle_conflicts(type='trust')
|
||||
def list_trusts(self):
|
||||
session = self.get_session()
|
||||
trusts = session.query(TrustModel).filter_by(deleted_at=None)
|
||||
return [trust_ref.to_dict() for trust_ref in trusts]
|
||||
|
||||
@sql.handle_conflicts(type='trust')
|
||||
def list_trusts_for_trustee(self, trustee_user_id):
|
||||
session = self.get_session()
|
||||
trusts = (session.query(TrustModel).
|
||||
filter_by(deleted_at=None).
|
||||
filter_by(trustee_user_id=trustee_user_id))
|
||||
return [trust_ref.to_dict() for trust_ref in trusts]
|
||||
|
||||
@sql.handle_conflicts(type='trust')
|
||||
def list_trusts_for_trustor(self, trustor_user_id):
|
||||
session = self.get_session()
|
||||
trusts = (session.query(TrustModel).
|
||||
filter_by(deleted_at=None).
|
||||
filter_by(trustor_user_id=trustor_user_id))
|
||||
return [trust_ref.to_dict() for trust_ref in trusts]
|
||||
|
||||
@sql.handle_conflicts(type='trust')
|
||||
def delete_trust(self, trust_id):
|
||||
session = self.get_session()
|
||||
with session.begin():
|
||||
try:
|
||||
trust_ref = (session.query(TrustModel).
|
||||
filter_by(id=trust_id).one())
|
||||
except sql.NotFound:
|
||||
raise exception.TrustNotFound(trust_id=trust_id)
|
||||
trust_ref.deleted_at = timeutils.utcnow()
|
||||
session.flush()
|
244
keystone/trust/controllers.py
Normal file
244
keystone/trust/controllers.py
Normal file
@ -0,0 +1,244 @@
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from keystone import config
|
||||
from keystone import exception
|
||||
from keystone import identity
|
||||
from keystone.common import controller
|
||||
from keystone.common import dependency
|
||||
from keystone.common import logging
|
||||
from keystone import exception
|
||||
from keystone.openstack.common import timeutils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
def _trustor_only(context, trust, user_id):
|
||||
if user_id != trust.get('trustor_user_id'):
|
||||
raise exception.Forbidden()
|
||||
|
||||
|
||||
def _admin_trustor_trustee_only(context, trust, user_id):
|
||||
if (user_id != trust.get('trustor_user_id') and
|
||||
user_id != trust.get('trustor_user_id') and
|
||||
context['is_admin']):
|
||||
raise exception.Forbidden()
|
||||
|
||||
|
||||
def _admin_trustor_only(context, trust, user_id):
|
||||
if user_id != trust.get('trustor_user_id') and not context['is_admin']:
|
||||
raise exception.Forbidden()
|
||||
|
||||
|
||||
@dependency.requires('identity_api', 'trust_api', 'token_api')
|
||||
class TrustV3(controller.V3Controller):
|
||||
collection_name = "trusts"
|
||||
member_name = "trust"
|
||||
|
||||
def _get_user_id(self, context):
|
||||
if 'token_id' in context:
|
||||
token_id = context['token_id']
|
||||
token = self.token_api.get_token(context, token_id)
|
||||
user_id = token['user']['id']
|
||||
return user_id
|
||||
return None
|
||||
|
||||
def get_trust(self, context, trust_id):
|
||||
user_id = self._get_user_id(context)
|
||||
trust = self.trust_api.get_trust(context, trust_id)
|
||||
if not trust:
|
||||
raise exception.TrustNotFound(trust_id)
|
||||
_admin_trustor_trustee_only(context, trust, user_id)
|
||||
if not trust:
|
||||
raise exception.TrustNotFound(trust_id=trust_id)
|
||||
if (user_id != trust['trustor_user_id'] and
|
||||
user_id != trust['trustee_user_id']):
|
||||
raise exception.Forbidden()
|
||||
self._fill_in_roles(context, trust,
|
||||
self.identity_api.list_roles(context))
|
||||
return TrustV3.wrap_member(context, trust)
|
||||
|
||||
def _fill_in_roles(self, context, trust, global_roles):
|
||||
if trust.get('expires_at') is not None:
|
||||
trust['expires_at'] = (timeutils.isotime
|
||||
(trust['expires_at'],
|
||||
subsecond=True))
|
||||
|
||||
if not 'roles' in trust:
|
||||
trust['roles'] = []
|
||||
trust_full_roles = []
|
||||
for trust_role in trust['roles']:
|
||||
if isinstance(trust_role, basestring):
|
||||
trust_role = {'id': trust_role}
|
||||
matching_roles = [x for x in global_roles
|
||||
if x['id'] == trust_role['id']]
|
||||
if matching_roles:
|
||||
full_role = identity.controllers.RoleV3.wrap_member(
|
||||
context, matching_roles[0])['role']
|
||||
trust_full_roles.append(full_role)
|
||||
trust['roles'] = trust_full_roles
|
||||
trust['roles_links'] = {
|
||||
'self': (CONF.public_endpoint % CONF +
|
||||
"trusts/%s/roles" % trust['id']),
|
||||
'next': None,
|
||||
'previous': None}
|
||||
|
||||
def _clean_role_list(self, context, trust, global_roles):
|
||||
trust_roles = []
|
||||
global_role_names = dict((r['name'], r)
|
||||
for r in
|
||||
global_roles)
|
||||
for role in trust.get('roles', []):
|
||||
if 'id' in role:
|
||||
trust_roles.append({'id': role['id']})
|
||||
elif 'name' in role:
|
||||
rolename = role['name']
|
||||
if rolename in global_role_names:
|
||||
trust_roles.append({'id':
|
||||
global_role_names[rolename]['id']})
|
||||
else:
|
||||
raise exception.RoleNotFound("role %s is not defined" %
|
||||
rolename)
|
||||
else:
|
||||
raise exception.ValidationError(attribute='id or name',
|
||||
target='roles')
|
||||
return trust_roles
|
||||
|
||||
@controller.protected
|
||||
def create_trust(self, context, trust=None):
|
||||
"""
|
||||
The user creating the trust must be trustor
|
||||
"""
|
||||
|
||||
#TODO instead of raising ValidationError on the first problem,
|
||||
#return a collection of all the problems.
|
||||
if not trust:
|
||||
raise exception.ValidationError(attribute='trust',
|
||||
target='request')
|
||||
try:
|
||||
user_id = self._get_user_id(context)
|
||||
_trustor_only(context, trust, user_id)
|
||||
#confirm that the trustee exists
|
||||
trustee_ref = self.identity_api.get_user(context,
|
||||
trust['trustee_user_id'])
|
||||
if not trustee_ref:
|
||||
raise exception.UserNotFound(user_id=trust['trustee_user_id'])
|
||||
global_roles = self.identity_api.list_roles(context)
|
||||
clean_roles = self._clean_role_list(context, trust, global_roles)
|
||||
if trust.get('project_id'):
|
||||
user_roles = self.identity_api.get_roles_for_user_and_project(
|
||||
context, user_id, trust['project_id'])
|
||||
else:
|
||||
user_roles = []
|
||||
for trust_role in clean_roles:
|
||||
matching_roles = [x for x in user_roles
|
||||
if x == trust_role['id']]
|
||||
if not matching_roles:
|
||||
raise exception.RoleNotFound(role_id=trust_role['id'])
|
||||
if trust.get('expires_at') is not None:
|
||||
if not trust['expires_at'].endswith('Z'):
|
||||
trust['expires_at'] += 'Z'
|
||||
trust['expires_at'] = (timeutils.parse_isotime
|
||||
(trust['expires_at']))
|
||||
new_trust = self.trust_api.create_trust(
|
||||
context=context,
|
||||
trust_id=uuid.uuid4().hex,
|
||||
trust=trust,
|
||||
roles=clean_roles)
|
||||
self._fill_in_roles(context,
|
||||
new_trust,
|
||||
global_roles)
|
||||
return TrustV3.wrap_member(context, new_trust)
|
||||
except KeyError as e:
|
||||
raise exception.ValidationError(attribute=e.args[0],
|
||||
target='trust')
|
||||
|
||||
@controller.protected
|
||||
def list_trusts(self, context):
|
||||
query = context['query_string']
|
||||
trusts = []
|
||||
if not query:
|
||||
self.assert_admin(context)
|
||||
trusts += self.trust_api.list_trusts(context)
|
||||
if 'trustor_user_id' in query:
|
||||
user_id = query['trustor_user_id']
|
||||
calling_user_id = self._get_user_id(context)
|
||||
if user_id != calling_user_id:
|
||||
raise exception.Forbidden()
|
||||
trusts += (self.trust_api.
|
||||
list_trusts_for_trustor(context, user_id))
|
||||
if 'trustee_user_id' in query:
|
||||
user_id = query['trustee_user_id']
|
||||
calling_user_id = self._get_user_id(context)
|
||||
if user_id != calling_user_id:
|
||||
raise exception.Forbidden()
|
||||
trusts += (self.trust_api.
|
||||
list_trusts_for_trustee(context, user_id))
|
||||
global_roles = self.identity_api.list_roles(context)
|
||||
for trust in trusts:
|
||||
self._fill_in_roles(context, trust, global_roles)
|
||||
return TrustV3.wrap_collection(context, trusts)
|
||||
|
||||
@controller.protected
|
||||
def delete_trust(self, context, trust_id):
|
||||
trust = self.trust_api.get_trust(context, trust_id)
|
||||
if not trust:
|
||||
raise exception.TrustNotFound(trust_id)
|
||||
|
||||
user_id = self._get_user_id(context)
|
||||
_admin_trustor_only(context, trust, user_id)
|
||||
self.trust_api.delete_trust(context, trust_id)
|
||||
userid = trust['trustor_user_id']
|
||||
token_list = self.token_api.list_tokens(context,
|
||||
userid,
|
||||
trust_id=trust_id)
|
||||
for token in token_list:
|
||||
self.token_api.delete_token(context, token)
|
||||
|
||||
@controller.protected
|
||||
def list_roles_for_trust(self, context, trust_id):
|
||||
trust = self.get_trust(context, trust_id)['trust']
|
||||
if not trust:
|
||||
raise exception.TrustNotFound(trust_id)
|
||||
user_id = self._get_user_id(context)
|
||||
_admin_trustor_trustee_only(context, trust, user_id)
|
||||
return {'roles': trust['roles'],
|
||||
'links': trust['roles_links']}
|
||||
|
||||
@controller.protected
|
||||
def check_role_for_trust(self, context, trust_id, role_id):
|
||||
"""Checks if a role has been assigned to a trust."""
|
||||
trust = self.trust_api.get_trust(context, trust_id)
|
||||
if not trust:
|
||||
raise exception.TrustNotFound(trust_id)
|
||||
user_id = self._get_user_id(context)
|
||||
_admin_trustor_trustee_only(context, trust, user_id)
|
||||
matching_roles = [x for x in trust['roles']
|
||||
if x['id'] == role_id]
|
||||
if not matching_roles:
|
||||
raise exception.RoleNotFound(role_id=role_id)
|
||||
|
||||
@controller.protected
|
||||
def get_role_for_trust(self, context, trust_id, role_id):
|
||||
"""Checks if a role has been assigned to a trust."""
|
||||
trust = self.trust_api.get_trust(context, trust_id)
|
||||
if not trust:
|
||||
raise exception.TrustNotFound(trust_id)
|
||||
|
||||
user_id = self._get_user_id(context)
|
||||
_admin_trustor_trustee_only(context, trust, user_id)
|
||||
matching_roles = [x for x in trust['roles']
|
||||
if x['id'] == role_id]
|
||||
if not matching_roles:
|
||||
raise exception.RoleNotFound(role_id=role_id)
|
||||
global_roles = self.identity_api.list_roles(context)
|
||||
matching_roles = [x for x in global_roles
|
||||
if x['id'] == role_id]
|
||||
if matching_roles:
|
||||
full_role = (identity.controllers.
|
||||
RoleV3.wrap_member(context, matching_roles[0]))
|
||||
return full_role
|
||||
else:
|
||||
raise exception.RoleNotFound(role_id=role_id)
|
63
keystone/trust/core.py
Normal file
63
keystone/trust/core.py
Normal file
@ -0,0 +1,63 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Main entry point into the Identity service."""
|
||||
|
||||
from keystone.common import dependency
|
||||
from keystone.common import logging
|
||||
from keystone.common import manager
|
||||
from keystone.common import wsgi
|
||||
from keystone import config
|
||||
from keystone import exception
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dependency.provider('trust_api')
|
||||
class Manager(manager.Manager):
|
||||
"""Default pivot point for the Trust backend.
|
||||
|
||||
See :mod:`keystone.common.manager.Manager` for more details on how this
|
||||
dynamically calls the backend.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(Manager, self).__init__(CONF.trust.driver)
|
||||
|
||||
|
||||
class Driver(object):
|
||||
def create_trust(self, trust_id, trust, roles):
|
||||
"""Create a new trust.
|
||||
|
||||
:returns: a new trust
|
||||
"""
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def get_trust(self, trust_id):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_trusts(self):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_trusts_for_trustee(self, trustee):
|
||||
raise exception.NotImplemented()
|
||||
|
||||
def list_trusts_for_trustor(self, trustor):
|
||||
raise exception.NotImplemented()
|
58
keystone/trust/routers.py
Normal file
58
keystone/trust/routers.py
Normal file
@ -0,0 +1,58 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""WSGI Routers for the Identity service."""
|
||||
from keystone.common import wsgi
|
||||
from keystone.trust import controllers
|
||||
from keystone.common import router
|
||||
|
||||
|
||||
def append_v3_routers(mapper, routers):
|
||||
trust_controller = controllers.TrustV3()
|
||||
|
||||
mapper.connect('/trusts',
|
||||
controller=trust_controller,
|
||||
action='create_trust',
|
||||
conditions=dict(method=['POST']))
|
||||
|
||||
mapper.connect('/trusts',
|
||||
controller=trust_controller,
|
||||
action='list_trusts',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
mapper.connect('/trusts/{trust_id}',
|
||||
controller=trust_controller,
|
||||
action='delete_trust',
|
||||
conditions=dict(method=['DELETE']))
|
||||
|
||||
mapper.connect('/trusts/{trust_id}',
|
||||
controller=trust_controller,
|
||||
action='get_trust',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
mapper.connect('/trusts/{trust_id}/roles',
|
||||
controller=trust_controller,
|
||||
action='list_roles_for_trust',
|
||||
conditions=dict(method=['GET']))
|
||||
|
||||
mapper.connect('/trusts/{trust_id}/roles/{role_id}',
|
||||
controller=trust_controller,
|
||||
action='check_role_for_trust',
|
||||
conditions=dict(method=['HEAD']))
|
||||
|
||||
mapper.connect('/trusts/{trust_id}/roles/{role_id}',
|
||||
controller=trust_controller,
|
||||
action='get_role_for_trust',
|
||||
conditions=dict(method=['GET']))
|
@ -22,3 +22,6 @@ driver = keystone.catalog.backends.sql.Catalog
|
||||
|
||||
[policy]
|
||||
driver = keystone.policy.backends.sql.Policy
|
||||
|
||||
[trust]
|
||||
driver = keystone.trust.backends.sql.Trust
|
||||
|
@ -109,5 +109,12 @@ ROLES = [
|
||||
}, {
|
||||
'id': 'other',
|
||||
'name': 'Other',
|
||||
}, {
|
||||
'id': 'browser',
|
||||
'name': 'Browser',
|
||||
}, {
|
||||
'id': 'writer',
|
||||
'name': 'Writer',
|
||||
}
|
||||
|
||||
]
|
||||
|
@ -12,23 +12,29 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from keystone import auth
|
||||
from keystone import config
|
||||
from keystone import exception
|
||||
from keystone.openstack.common import timeutils
|
||||
from keystone import test
|
||||
from keystone import token
|
||||
from keystone import trust
|
||||
|
||||
import default_fixtures
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
|
||||
|
||||
|
||||
def _build_user_auth(token=None, user_id=None, username=None,
|
||||
password=None, tenant_id=None, tenant_name=None):
|
||||
password=None, tenant_id=None, tenant_name=None,
|
||||
trust_id=None):
|
||||
"""Build auth dictionary.
|
||||
|
||||
It will create an auth dictionary based on all the arguments
|
||||
@ -49,6 +55,8 @@ def _build_user_auth(token=None, user_id=None, username=None,
|
||||
auth_json['tenantName'] = tenant_name
|
||||
if tenant_id is not None:
|
||||
auth_json['tenantId'] = tenant_id
|
||||
if trust_id is not None:
|
||||
auth_json['trust_id'] = trust_id
|
||||
return auth_json
|
||||
|
||||
|
||||
@ -452,6 +460,240 @@ class AuthWithRemoteUser(AuthTest):
|
||||
body_dict)
|
||||
|
||||
|
||||
class AuthWithTrust(AuthTest):
|
||||
def setUp(self):
|
||||
super(AuthWithTrust, self).setUp()
|
||||
|
||||
trust.Manager()
|
||||
self.trust_controller = trust.controllers.TrustV3()
|
||||
self.auth_v3_controller = auth.controllers.Auth()
|
||||
self.trustor = self.user_foo
|
||||
self.trustee = self.user_two
|
||||
self.assigned_roles = [self.role_member['id'],
|
||||
self.role_browser['id']]
|
||||
for assigned_role in self.assigned_roles:
|
||||
self.identity_api.add_role_to_user_and_project(
|
||||
self.trustor['id'], self.tenant_bar['id'], assigned_role)
|
||||
|
||||
self.sample_data = {'trustor_user_id': self.trustor['id'],
|
||||
'trustee_user_id': self.trustee['id'],
|
||||
'project_id': self.tenant_bar['id'],
|
||||
'impersonation': 'True',
|
||||
'roles': [{'id': self.role_browser['id']},
|
||||
{'name': self.role_member['name']}]}
|
||||
expires_at = timeutils.strtime(timeutils.utcnow() +
|
||||
datetime.timedelta(minutes=10),
|
||||
fmt=TIME_FORMAT)
|
||||
self.create_trust(expires_at=expires_at)
|
||||
|
||||
def create_trust(self, expires_at=None, impersonation='True'):
|
||||
username = self.trustor['name'],
|
||||
password = 'foo2'
|
||||
body_dict = _build_user_auth(username=username, password=password)
|
||||
self.unscoped_token = self.controller.authenticate({}, body_dict)
|
||||
context = {'token_id': self.unscoped_token['access']['token']['id']}
|
||||
trust_data = copy.deepcopy(self.sample_data)
|
||||
trust_data['expires_at'] = expires_at
|
||||
trust_data['impersonation'] = impersonation
|
||||
|
||||
self.new_trust = (self.trust_controller.create_trust
|
||||
(context, trust=trust_data)['trust'])
|
||||
|
||||
def build_v2_token_request(self, username, password):
|
||||
body_dict = _build_user_auth(username=username, password=password)
|
||||
self.unscoped_token = self.controller.authenticate({}, body_dict)
|
||||
unscoped_token_id = self.unscoped_token['access']['token']['id']
|
||||
request_body = _build_user_auth(token={'id': unscoped_token_id},
|
||||
trust_id=self.new_trust['id'],
|
||||
tenant_id=self.tenant_bar['id'])
|
||||
return request_body
|
||||
|
||||
def test_create_trust_bad_data_fails(self):
|
||||
context = {'token_id': self.unscoped_token['access']['token']['id']}
|
||||
bad_sample_data = {'trustor_user_id': self.trustor['id']}
|
||||
|
||||
self.assertRaises(exception.ValidationError,
|
||||
self.trust_controller.create_trust,
|
||||
context, trust=bad_sample_data)
|
||||
|
||||
def test_create_trust_no_roles(self):
|
||||
self.new_trust = None
|
||||
self.sample_data['roles'] = []
|
||||
self.create_trust()
|
||||
self.assertEquals(self.new_trust['roles'], [])
|
||||
|
||||
def test_create_trust(self):
|
||||
self.assertEquals(self.new_trust['trustor_user_id'],
|
||||
self.trustor['id'])
|
||||
self.assertEquals(self.new_trust['trustee_user_id'],
|
||||
self.trustee['id'])
|
||||
role_ids = [self.role_browser['id'], self.role_member['id']]
|
||||
self.assertTrue(timeutils.parse_strtime(self.new_trust['expires_at'],
|
||||
fmt=TIME_FORMAT))
|
||||
|
||||
for role in self.new_trust['roles']:
|
||||
self.assertIn(role['id'], role_ids)
|
||||
|
||||
def test_get_trust(self):
|
||||
context = {'token_id': self.unscoped_token['access']['token']['id']}
|
||||
trust = self.trust_controller.get_trust(context,
|
||||
self.new_trust['id'])['trust']
|
||||
self.assertEquals(trust['trustor_user_id'],
|
||||
self.trustor['id'])
|
||||
self.assertEquals(trust['trustee_user_id'],
|
||||
self.trustee['id'])
|
||||
role_ids = [self.role_browser['id'], self.role_member['id']]
|
||||
for role in self.new_trust['roles']:
|
||||
self.assertIn(role['id'], role_ids)
|
||||
|
||||
def test_create_trust_no_impersonation(self):
|
||||
self.create_trust(expires_at=None, impersonation='False')
|
||||
self.assertEquals(self.new_trust['trustor_user_id'],
|
||||
self.trustor['id'])
|
||||
self.assertEquals(self.new_trust['trustee_user_id'],
|
||||
self.trustee['id'])
|
||||
self.assertEquals(self.new_trust['impersonation'],
|
||||
'False')
|
||||
auth_response = self.fetch_v2_token_from_trust()
|
||||
token_user = auth_response['access']['user']
|
||||
self.assertEquals(token_user['id'],
|
||||
self.new_trust['trustee_user_id'])
|
||||
|
||||
#TODO Endpoints
|
||||
|
||||
def test_token_from_trust_wrong_user_fails(self):
|
||||
new_trust = self.create_trust()
|
||||
request_body = self.build_v2_token_request('FOO', 'foo2')
|
||||
self.assertRaises(
|
||||
exception.Forbidden,
|
||||
self.controller.authenticate, {}, request_body)
|
||||
|
||||
def fetch_v2_token_from_trust(self):
|
||||
request_body = self.build_v2_token_request('TWO', 'two2')
|
||||
auth_response = self.controller.authenticate({}, request_body)
|
||||
return auth_response
|
||||
|
||||
def fetch_v3_token_from_trust(self):
|
||||
self.identity_api.create_domain("default",
|
||||
{"name": "default",
|
||||
"id": "default"})
|
||||
v3_password_data = {
|
||||
'identity': {
|
||||
"methods": ["password"],
|
||||
"password": {
|
||||
"user": {
|
||||
"id": self.trustee["id"],
|
||||
"password": self.trustee["password"]}}
|
||||
},
|
||||
'scope': {
|
||||
'project': {
|
||||
'id': self.tenant_baz['id']}}}
|
||||
auth_response = (self.auth_v3_controller.authenticate_for_token
|
||||
({}, v3_password_data))
|
||||
token = auth_response.headers['X-Subject-Token']
|
||||
|
||||
v3_req_with_trust = {
|
||||
"identity": {
|
||||
"methods": ["token"],
|
||||
"token": {"id": token}},
|
||||
"scope": {
|
||||
"trust": {"id": self.new_trust['id']}}}
|
||||
token_auth_response = (self.auth_v3_controller.authenticate_for_token
|
||||
({}, v3_req_with_trust))
|
||||
return token_auth_response
|
||||
|
||||
def test_create_v3_token_from_trust(self):
|
||||
auth_response = self.fetch_v3_token_from_trust()
|
||||
|
||||
trust_token_user = auth_response.json['token']['user']
|
||||
self.assertEquals(trust_token_user['id'], self.trustor['id'])
|
||||
|
||||
trust_token_trust = auth_response.json['token']['trust']
|
||||
self.assertEquals(trust_token_trust['id'], self.new_trust['id'])
|
||||
self.assertEquals(trust_token_trust['trustor_user']['id'],
|
||||
self.trustor['id'])
|
||||
self.assertEquals(trust_token_trust['trustee_user']['id'],
|
||||
self.trustee['id'])
|
||||
|
||||
trust_token_roles = auth_response.json['token']['roles']
|
||||
self.assertEquals(len(trust_token_roles), 2)
|
||||
|
||||
def test_v3_trust_token_get_token_fails(self):
|
||||
auth_response = self.fetch_v3_token_from_trust()
|
||||
trust_token = auth_response.headers['X-Subject-Token']
|
||||
v3_token_data = {
|
||||
"methods": ["token"],
|
||||
"token": {"id": trust_token}
|
||||
}
|
||||
self.assertRaises(
|
||||
exception.Unauthorized,
|
||||
self.auth_v3_controller.authenticate_for_token,
|
||||
{}, v3_token_data)
|
||||
|
||||
def test_token_from_trust(self):
|
||||
auth_response = self.fetch_v2_token_from_trust()
|
||||
|
||||
self.assertIsNotNone(auth_response)
|
||||
self.assertEquals(len(auth_response['access']['metadata']['roles']),
|
||||
2,
|
||||
"user_foo has three roles, but the token should"
|
||||
" only get the two roles specified in the trust.")
|
||||
|
||||
def test_token_from_trust_cant_get_another_token(self):
|
||||
auth_response = self.fetch_v2_token_from_trust()
|
||||
trust_token_id = auth_response['access']['token']['id']
|
||||
request_body = _build_user_auth(token={'id': trust_token_id},
|
||||
tenant_id=self.tenant_bar['id'])
|
||||
self.assertRaises(
|
||||
exception.Forbidden,
|
||||
self.controller.authenticate, {}, request_body)
|
||||
|
||||
def test_delete_trust_revokes_token(self):
|
||||
context = {'token_id': self.unscoped_token['access']['token']['id']}
|
||||
auth_response = self.fetch_v2_token_from_trust()
|
||||
trust_id = self.new_trust['id']
|
||||
trust_token_id = auth_response['access']['token']['id']
|
||||
tokens = self.token_api.list_tokens(self.trustor['id'],
|
||||
trust_id=trust_id)
|
||||
self.assertEquals(len(tokens), 1)
|
||||
self.trust_controller.delete_trust(context, trust_id=trust_id)
|
||||
tokens = self.token_api.list_tokens(self.trustor['id'],
|
||||
trust_id=trust_id)
|
||||
self.assertEquals(len(tokens), 0)
|
||||
|
||||
def test_token_from_trust_with_no_role_fails(self):
|
||||
for assigned_role in self.assigned_roles:
|
||||
self.identity_api.remove_role_from_user_and_project(
|
||||
self.trustor['id'], self.tenant_bar['id'], assigned_role)
|
||||
request_body = self.build_v2_token_request('TWO', 'two2')
|
||||
self.assertRaises(
|
||||
exception.Forbidden,
|
||||
self.controller.authenticate, {}, request_body)
|
||||
|
||||
def test_expired_trust_get_token_fails(self):
|
||||
expiry = "1999-02-18T10:10:00Z"
|
||||
self.create_trust(expiry)
|
||||
request_body = self.build_v2_token_request('TWO', 'two2')
|
||||
self.assertRaises(
|
||||
exception.Forbidden,
|
||||
self.controller.authenticate, {}, request_body)
|
||||
|
||||
def test_token_from_trust_with_wrong_role_fails(self):
|
||||
self.identity_api.add_role_to_user_and_project(
|
||||
self.trustor['id'],
|
||||
self.tenant_bar['id'],
|
||||
self.role_other['id'])
|
||||
for assigned_role in self.assigned_roles:
|
||||
self.identity_api.remove_role_from_user_and_project(
|
||||
self.trustor['id'], self.tenant_bar['id'], assigned_role)
|
||||
|
||||
request_body = self.build_v2_token_request('TWO', 'two2')
|
||||
|
||||
self.assertRaises(
|
||||
exception.Forbidden,
|
||||
self.controller.authenticate, {}, request_body)
|
||||
|
||||
|
||||
class TokenExpirationTest(AuthTest):
|
||||
def _maintain_token_expiration(self):
|
||||
"""Token expiration should be maintained after re-auth & validation."""
|
||||
|
@ -29,6 +29,7 @@ from keystone import test
|
||||
|
||||
CONF = config.CONF
|
||||
DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
|
||||
TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ'
|
||||
|
||||
|
||||
class IdentityTests(object):
|
||||
@ -1888,12 +1889,14 @@ class TokenTests(object):
|
||||
self.assertRaises(exception.TokenNotFound,
|
||||
self.token_api.delete_token, token_id)
|
||||
|
||||
def create_token_sample_data(self, tenant_id=None):
|
||||
def create_token_sample_data(self, tenant_id=None, trust_id=None):
|
||||
token_id = uuid.uuid4().hex
|
||||
data = {'id': token_id, 'a': 'b',
|
||||
'user': {'id': 'testuserid'}}
|
||||
if tenant_id is not None:
|
||||
data['tenant'] = {'id': tenant_id, 'name': tenant_id}
|
||||
if trust_id is not None:
|
||||
data['trust_id'] = trust_id
|
||||
self.token_api.create_token(token_id, data)
|
||||
return token_id
|
||||
|
||||
@ -1936,6 +1939,13 @@ class TokenTests(object):
|
||||
self.assertNotIn(token_id3, tokens)
|
||||
self.assertIn(token_id4, tokens)
|
||||
|
||||
def test_token_list_trust(self):
|
||||
trust_id = uuid.uuid4().hex
|
||||
token_id5 = self.create_token_sample_data(trust_id=trust_id)
|
||||
tokens = self.token_api.list_tokens('testuserid', trust_id=trust_id)
|
||||
self.assertEquals(len(tokens), 1)
|
||||
self.assertIn(token_id5, tokens)
|
||||
|
||||
def test_get_token_404(self):
|
||||
self.assertRaises(exception.TokenNotFound,
|
||||
self.token_api.get_token,
|
||||
@ -1965,7 +1975,7 @@ class TokenTests(object):
|
||||
data = {'id': token_id, 'id_hash': token_id, 'a': 'b', 'expires': None,
|
||||
'user': {'id': 'testuserid'}}
|
||||
data_ref = self.token_api.create_token(token_id, data)
|
||||
self.assertDictEqual(data_ref, data)
|
||||
self.assertIsNotNone(data_ref['expires'])
|
||||
new_data_ref = self.token_api.get_token(token_id)
|
||||
self.assertEqual(data_ref, new_data_ref)
|
||||
|
||||
@ -2002,6 +2012,80 @@ class TokenTests(object):
|
||||
for x in xrange(2)])
|
||||
|
||||
|
||||
class TrustTests(object):
|
||||
def create_sample_trust(self, new_id):
|
||||
self.trustor = self.user_foo
|
||||
self.trustee = self.user_two
|
||||
trust_data = (self.trust_api.create_trust
|
||||
(new_id,
|
||||
{'trustor_user_id': self.trustor['id'],
|
||||
'trustee_user_id': self.user_two['id'],
|
||||
'project_id': self.tenant_bar['id'],
|
||||
'expires_at': timeutils.
|
||||
parse_isotime('2031-02-18T18:10:00Z'),
|
||||
'impersonation': True},
|
||||
roles=[{"id": "member"},
|
||||
{"id": "other"},
|
||||
{"id": "browser"}]))
|
||||
return trust_data
|
||||
|
||||
def test_delete_trust(self):
|
||||
new_id = uuid.uuid4().hex
|
||||
trust_data = self.create_sample_trust(new_id)
|
||||
trust_id = trust_data['id']
|
||||
self.assertIsNotNone(trust_data)
|
||||
trust_data = self.trust_api.get_trust(trust_id)
|
||||
self.assertEquals(new_id, trust_data['id'])
|
||||
self.trust_api.delete_trust(trust_id)
|
||||
self.assertIsNone(self.trust_api.get_trust(trust_id))
|
||||
|
||||
def test_get_trust(self):
|
||||
new_id = uuid.uuid4().hex
|
||||
trust_data = self.create_sample_trust(new_id)
|
||||
trust_id = trust_data['id']
|
||||
self.assertIsNotNone(trust_data)
|
||||
trust_data = self.trust_api.get_trust(trust_id)
|
||||
self.assertEquals(new_id, trust_data['id'])
|
||||
|
||||
def test_create_trust(self):
|
||||
new_id = uuid.uuid4().hex
|
||||
trust_data = self.create_sample_trust(new_id)
|
||||
|
||||
self.assertEquals(new_id, trust_data['id'])
|
||||
self.assertEquals(self.trustee['id'], trust_data['trustee_user_id'])
|
||||
self.assertEquals(self.trustor['id'], trust_data['trustor_user_id'])
|
||||
self.assertTrue(timeutils.normalize_time(trust_data['expires_at']) >
|
||||
timeutils.utcnow())
|
||||
|
||||
self.assertEquals([{'id':'member'},
|
||||
{'id': 'other'},
|
||||
{'id': 'browser'}], trust_data['roles'])
|
||||
|
||||
def test_list_trust_by_trustee(self):
|
||||
for i in range(0, 3):
|
||||
trust_data = self.create_sample_trust(uuid.uuid4().hex)
|
||||
trusts = self.trust_api.list_trusts_for_trustee(self.trustee)
|
||||
self.assertEqual(len(trusts), 3)
|
||||
self.assertEqual(trusts[0]["trustee_user_id"], self.trustee['id'])
|
||||
trusts = self.trust_api.list_trusts_for_trustee(self.trustor)
|
||||
self.assertEqual(len(trusts), 0)
|
||||
|
||||
def test_list_trust_by_trustee(self):
|
||||
for i in range(0, 3):
|
||||
trust_data = self.create_sample_trust(uuid.uuid4().hex)
|
||||
trusts = self.trust_api.list_trusts_for_trustor(self.trustor['id'])
|
||||
self.assertEqual(len(trusts), 3)
|
||||
self.assertEqual(trusts[0]["trustor_user_id"], self.trustor['id'])
|
||||
trusts = self.trust_api.list_trusts_for_trustor(self.trustee['id'])
|
||||
self.assertEqual(len(trusts), 0)
|
||||
|
||||
def test_list_trusts(self):
|
||||
for i in range(0, 3):
|
||||
trust_data = self.create_sample_trust(uuid.uuid4().hex)
|
||||
trusts = self.trust_api.list_trusts()
|
||||
self.assertEqual(len(trusts), 3)
|
||||
|
||||
|
||||
class CommonHelperTests(test.TestCase):
|
||||
def test_format_helper_raises_malformed_on_missing_key(self):
|
||||
with self.assertRaises(exception.MalformedEndpoint):
|
||||
|
@ -22,6 +22,7 @@ from keystone import exception
|
||||
from keystone.identity.backends import kvs as identity_kvs
|
||||
from keystone import test
|
||||
from keystone.token.backends import kvs as token_kvs
|
||||
from keystone.trust.backends import kvs as trust_kvs
|
||||
|
||||
import default_fixtures
|
||||
import test_backend
|
||||
@ -71,6 +72,15 @@ class KvsToken(test.TestCase, test_backend.TokenTests):
|
||||
self.token_api = token_kvs.Token(db={})
|
||||
|
||||
|
||||
class KvsTrust(test.TestCase, test_backend.TrustTests):
|
||||
def setUp(self):
|
||||
super(KvsTrust, self).setUp()
|
||||
self.trust_api = trust_kvs.Trust(db={})
|
||||
self.identity_api = identity_kvs.Identity(db={})
|
||||
self.catalog_api = catalog_kvs.Catalog(db={})
|
||||
self.load_fixtures(default_fixtures)
|
||||
|
||||
|
||||
class KvsCatalog(test.TestCase, test_backend.CatalogTests):
|
||||
def setUp(self):
|
||||
super(KvsCatalog, self).setUp()
|
||||
|
@ -24,6 +24,8 @@ from keystone import identity
|
||||
from keystone import policy
|
||||
from keystone import test
|
||||
from keystone import token
|
||||
from keystone import trust
|
||||
|
||||
|
||||
import default_fixtures
|
||||
import test_backend
|
||||
@ -43,6 +45,7 @@ class SqlTests(test.TestCase):
|
||||
self.catalog_man = catalog.Manager()
|
||||
self.identity_man = identity.Manager()
|
||||
self.token_man = token.Manager()
|
||||
self.trust_man = trust.Manager()
|
||||
self.policy_man = policy.Manager()
|
||||
|
||||
# create shortcut references to each driver
|
||||
@ -50,6 +53,7 @@ class SqlTests(test.TestCase):
|
||||
self.identity_api = self.identity_man.driver
|
||||
self.token_api = self.token_man.driver
|
||||
self.policy_api = self.policy_man.driver
|
||||
self.trust_api = self.trust_man.driver
|
||||
|
||||
# populate the engine with tables & fixtures
|
||||
self.load_fixtures(default_fixtures)
|
||||
@ -221,6 +225,10 @@ class SqlIdentity(SqlTests, test_backend.IdentityTests):
|
||||
self.assertEqual(arbitrary_value, ref['extra'][arbitrary_key])
|
||||
|
||||
|
||||
class SqlTrust(SqlTests, test_backend.TrustTests):
|
||||
pass
|
||||
|
||||
|
||||
class SqlToken(SqlTests, test_backend.TokenTests):
|
||||
pass
|
||||
|
||||
|
@ -90,7 +90,7 @@ class RestfulTestCase(test.TestCase):
|
||||
# Initialize headers dictionary
|
||||
headers = {} if not headers else headers
|
||||
|
||||
connection = httplib.HTTPConnection(host, port, timeout=10)
|
||||
connection = httplib.HTTPConnection(host, port, timeout=100000)
|
||||
|
||||
# Perform the request
|
||||
connection.request(method, path, body, headers)
|
||||
|
@ -8,6 +8,9 @@ driver = keystone.identity.backends.kvs.Identity
|
||||
driver = keystone.catalog.backends.templated.TemplatedCatalog
|
||||
template_file = default_catalog.templates
|
||||
|
||||
[trust]
|
||||
driver = keystone.trust.backends.kvs.Trust
|
||||
|
||||
[signing]
|
||||
certfile = ../examples/pki/certs/signing_cert.pem
|
||||
keyfile = ../examples/pki/private/signing_key.pem
|
||||
|
@ -526,6 +526,18 @@ class SqlUpgradeTests(test.TestCase):
|
||||
cmd = this_table.delete(id=project['id'])
|
||||
self.engine.execute(cmd)
|
||||
|
||||
def test_upgrade_trusts(self):
|
||||
self.assertEqual(self.schema.version, 0, "DB is at version 0")
|
||||
self.upgrade(18)
|
||||
self.assertTableColumns("trust",
|
||||
["id", "trustor_user_id",
|
||||
"trustee_user_id",
|
||||
"project_id", "impersonation",
|
||||
"deleted_at",
|
||||
"expires_at", "extra"])
|
||||
self.assertTableColumns("trust_role",
|
||||
["trust_id", "role_id"])
|
||||
|
||||
def populate_user_table(self, with_pass_enab=False,
|
||||
with_pass_enab_domain=False):
|
||||
# Populate the appropriate fields in the user
|
||||
|
@ -7,6 +7,8 @@ from keystone.common.sql import util as sql_util
|
||||
from keystone import auth
|
||||
from keystone import test
|
||||
from keystone import config
|
||||
from keystone.policy.backends import rules
|
||||
|
||||
|
||||
import test_content_types
|
||||
|
||||
@ -16,11 +18,14 @@ CONF = config.CONF
|
||||
|
||||
class RestfulTestCase(test_content_types.RestfulTestCase):
|
||||
def setUp(self):
|
||||
rules.reset()
|
||||
|
||||
self.config([
|
||||
test.etcdir('keystone.conf.sample'),
|
||||
test.testsdir('test_overrides.conf'),
|
||||
test.testsdir('backend_sql.conf'),
|
||||
test.testsdir('backend_sql_disk.conf')])
|
||||
|
||||
sql_util.setup_test_database()
|
||||
self.load_backends()
|
||||
|
||||
@ -62,6 +67,9 @@ class RestfulTestCase(test_content_types.RestfulTestCase):
|
||||
sql_util.teardown_test_database()
|
||||
# need to reset the plug-ins
|
||||
auth.controllers.AUTH_METHODS = {}
|
||||
#drop the policy rules
|
||||
CONF.reset()
|
||||
rules.reset()
|
||||
|
||||
def new_ref(self):
|
||||
"""Populates a ref with attributes common to all API entities."""
|
||||
|
286
tests/test_v3_trust.py
Normal file
286
tests/test_v3_trust.py
Normal file
@ -0,0 +1,286 @@
|
||||
import copy
|
||||
import uuid
|
||||
import test_v3
|
||||
import json
|
||||
|
||||
from keystone import config
|
||||
from keystone.common.sql import util as sql_util
|
||||
from keystone import test
|
||||
|
||||
import test_content_types
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id
|
||||
|
||||
|
||||
class TrustTestCase(test_v3.RestfulTestCase):
|
||||
def setUp(self):
|
||||
super(TrustTestCase, self).setUp()
|
||||
self.domain = None
|
||||
self.password = 'freeipa4all'
|
||||
self.auth_url = '/v2.0'
|
||||
self.admin_url = '/v2.0'
|
||||
self.admin_url_v3 = '/v3'
|
||||
self.url_template = "%(auth_url)s/%(resource)s"
|
||||
self.headers = {'Content-type': 'application/json'}
|
||||
self.trustor = self.create_user()
|
||||
self.trustee = self.create_user()
|
||||
self.role_1 = self.create_role()
|
||||
self.role_2 = self.create_role()
|
||||
self.grant_role_to_user(self.trustor['id'],
|
||||
self.role_1['id'],
|
||||
self.get_project()['id'])
|
||||
self.grant_role_to_user(self.trustor['id'],
|
||||
self.role_2['id'],
|
||||
self.get_project()['id'])
|
||||
|
||||
def v3_request(self, path, data):
|
||||
r = self.request(method='POST',
|
||||
path=path,
|
||||
body=data,
|
||||
headers=self.headers)
|
||||
return r
|
||||
|
||||
def get_unscoped_token_response(self, username, password):
|
||||
url = self.url_template % {'auth_url': self.admin_url,
|
||||
'resource': "tokens"}
|
||||
data = self.get_unscoped_auth(username=username, password=password)
|
||||
r = self.restful_request(method='POST',
|
||||
port=self._public_port(),
|
||||
path=url,
|
||||
body=data,
|
||||
headers=self.headers)
|
||||
if 'access' in r.body:
|
||||
return r.body['access']
|
||||
raise Exception(r)
|
||||
|
||||
def get_scoped_token_response(self, username, password, project_name):
|
||||
url = self.url_template % {'auth_url': self.admin_url,
|
||||
'resource': "tokens"}
|
||||
data = self.get_scoped_auth(username, password, project_name)
|
||||
r = self.restful_request(method='POST',
|
||||
port=self._public_port(),
|
||||
path=url,
|
||||
body=data,
|
||||
headers=self.headers)
|
||||
if 'access' in r.body:
|
||||
return r.body['access']
|
||||
raise Exception(r)
|
||||
|
||||
def get_admin_token_data(self):
|
||||
if not hasattr(self, 'admin_token_response'):
|
||||
self.admin_token_response = self.get_scoped_token_response(
|
||||
'admin', 'freeipa4all', 'demo')
|
||||
return self.admin_token_response
|
||||
|
||||
def get_admin_token_id(self):
|
||||
return 'ADMIN'
|
||||
|
||||
def make_admin_post_request(self, resource, data):
|
||||
return self.make_post_request(resource,
|
||||
data,
|
||||
self.get_admin_token_id())
|
||||
|
||||
def make_post_request(self, resource, data, token_id):
|
||||
headers = copy.copy(self.headers)
|
||||
headers["x-auth-token"] = token_id
|
||||
url = self.url_template % {'auth_url': self.admin_url_v3,
|
||||
'resource': resource}
|
||||
r = self.restful_request(method='POST',
|
||||
path=url,
|
||||
port=self._admin_port(),
|
||||
body=data,
|
||||
headers=headers)
|
||||
return r
|
||||
|
||||
def make_v2_post_request(self, resource, data, token_id):
|
||||
headers = copy.copy(self.headers)
|
||||
headers["x-auth-token"] = token_id
|
||||
url = self.url_template % {'auth_url': self.admin_url,
|
||||
'resource': resource}
|
||||
r = self.restful_request(method='POST',
|
||||
path=url,
|
||||
port=self._admin_port(),
|
||||
body=data,
|
||||
headers=headers)
|
||||
return r
|
||||
|
||||
def make_put_request(self, resource, data, token_id):
|
||||
headers = copy.copy(self.headers)
|
||||
headers["x-auth-token"] = self.get_admin_token_id()
|
||||
url = self.url_template % {'auth_url': self.admin_url_v3,
|
||||
'resource': resource}
|
||||
r = self.request(method='PUT',
|
||||
path=url,
|
||||
port=self._admin_port(),
|
||||
body=json.dumps(data),
|
||||
headers=headers)
|
||||
return r
|
||||
|
||||
def create_domain(self):
|
||||
domain = self.new_domain_ref()
|
||||
resource = 'domains'
|
||||
data = {'domain': domain}
|
||||
r = self.make_admin_post_request(resource, data)
|
||||
dom = r.body['domain']
|
||||
self.domain = dom
|
||||
|
||||
def create_project(self):
|
||||
project = self.new_project_ref(
|
||||
domain_id=self.get_domain()['id'])
|
||||
data = {'project': project}
|
||||
r = self.make_admin_post_request('projects', data)
|
||||
self.project = r.body['project']
|
||||
|
||||
def get_domain(self):
|
||||
if not self.domain:
|
||||
#once authenticate supports domains, use the following function
|
||||
# self.create_domain()
|
||||
self.domain = {'id': DEFAULT_DOMAIN_ID}
|
||||
return self.domain
|
||||
|
||||
def get_project(self):
|
||||
if not hasattr(self, 'project'):
|
||||
self.create_project()
|
||||
return self.project
|
||||
|
||||
def create_user(self):
|
||||
user_id = uuid.uuid4().hex
|
||||
user = {'user': {'name': uuid.uuid4().hex,
|
||||
'password': self.password,
|
||||
'enabled': True,
|
||||
'domain_id': self.get_domain()['id'],
|
||||
'project_id': self.get_project()['id']}}
|
||||
r = self.make_admin_post_request('users', user)
|
||||
return r.body['user']
|
||||
|
||||
def create_role(self):
|
||||
ref = self.new_role_ref()
|
||||
body = {'role': ref}
|
||||
r = self.make_admin_post_request('roles', body)
|
||||
return r.body['role']
|
||||
|
||||
def grant_role_to_user(self, user_id, role_id, project_id):
|
||||
"""PUT /projects/{project_id}/users/{user_id}/roles/{role_id}"""
|
||||
url_template = 'projects/%(project_id)s/users'\
|
||||
'/%(user_id)s/roles/%(role_id)s'
|
||||
url = url_template % {'project_id': project_id,
|
||||
'user_id': user_id,
|
||||
'role_id': role_id}
|
||||
r = self.make_put_request(url, '', self.get_admin_token_id())
|
||||
return r
|
||||
|
||||
def get_scoped_auth(self, username, password, project_name):
|
||||
return {"auth":
|
||||
{"passwordCredentials": {"username": username,
|
||||
"password": password},
|
||||
"projectName": project_name}}
|
||||
|
||||
def get_unscoped_auth(self, username, password):
|
||||
return {"auth":
|
||||
{"passwordCredentials": {"username": username,
|
||||
"password": password}}}
|
||||
|
||||
def create_trust(self, impersonation=True):
|
||||
trustor_token = self.get_scoped_token_response(
|
||||
self.trustor['name'],
|
||||
self.password,
|
||||
self.get_project()['name'])
|
||||
trustee_token = self.get_unscoped_token_response(self.trustee['name'],
|
||||
self.password)
|
||||
trust_request = {'trust':
|
||||
{'trustor_user_id': self.trustor['id'],
|
||||
'trustee_user_id': self.trustee['id'],
|
||||
'project_id': self.get_project()['id'],
|
||||
'impersonation': impersonation,
|
||||
'description': 'described',
|
||||
'roles': []}}
|
||||
trust_response = self.make_post_request('trusts', trust_request,
|
||||
trustor_token['token']['id'])
|
||||
return trust_response, trustee_token
|
||||
|
||||
def test_create_trust(self):
|
||||
trust_response, trustee_token = self.create_trust()
|
||||
trust_id = trust_response.body['trust']['id']
|
||||
self.assertEquals(trust_response.body['trust']['description'],
|
||||
'described')
|
||||
auth_data = {"auth": {"token": {'id': trustee_token['token']['id']},
|
||||
"trust_id": trust_id}}
|
||||
r = self.make_v2_post_request("tokens",
|
||||
auth_data,
|
||||
trustee_token['token']['id'])
|
||||
trust_token = r.body
|
||||
self.assertIsNotNone(trust_token['access']['token']['id'])
|
||||
self.assertEquals(trust_token['access']['trust']['trustee_user_id'],
|
||||
self.trustee['id'])
|
||||
self.assertEquals(trust_token['access']['trust']['id'], trust_id)
|
||||
|
||||
def test_delete_trust(self):
|
||||
trust_response, trustee_token = self.create_trust()
|
||||
url = self.url_template % {'auth_url': self.admin_url_v3,
|
||||
'resource': "trusts/"}
|
||||
url += trust_response.body['trust']['id']
|
||||
trustor_token = self.get_scoped_token_response(
|
||||
self.trustor['name'],
|
||||
self.password,
|
||||
self.get_project()['name'])
|
||||
|
||||
headers = copy.copy(self.headers)
|
||||
headers["x-auth-token"] = trustor_token['token']['id']
|
||||
response = self.request(method='DELETE',
|
||||
path=url,
|
||||
port=self._public_port(),
|
||||
body="",
|
||||
headers=headers)
|
||||
self.assertIsNotNone(response)
|
||||
|
||||
def test_list_trusts(self):
|
||||
trustor_token = self.get_scoped_token_response(
|
||||
self.trustor['name'],
|
||||
self.password,
|
||||
self.get_project()['name'])
|
||||
|
||||
for i in range(0, 3):
|
||||
trust_response, trustee_token = self.create_trust()
|
||||
url = self.url_template % {'auth_url': self.admin_url_v3,
|
||||
'resource': "trusts"}
|
||||
headers = copy.copy(self.headers)
|
||||
headers["x-auth-token"] = self.get_admin_token_id()
|
||||
trust_lists_response = self.restful_request(method='GET',
|
||||
path=url,
|
||||
port=self._public_port(),
|
||||
body="",
|
||||
headers=headers)
|
||||
trusts = trust_lists_response.body['trusts']
|
||||
self.assertEqual(len(trusts), 3)
|
||||
|
||||
trustee_url = url + "?trustee_user_id=" + self.trustee['id']
|
||||
headers["x-auth-token"] = trustee_token['token']['id']
|
||||
trust_lists_response = self.restful_request(
|
||||
method='GET', path=trustee_url, port=self._public_port(),
|
||||
body="", headers=headers)
|
||||
trusts = trust_lists_response.body['trusts']
|
||||
self.assertEqual(len(trusts), 3)
|
||||
|
||||
headers["x-auth-token"] = trustor_token['token']['id']
|
||||
|
||||
trust_lists_response = self.restful_request(
|
||||
method='GET', path=trustee_url, port=self._public_port(),
|
||||
body="", headers=headers, expected_status=403)
|
||||
|
||||
trustor_url = url + "?trustor_user_id=" + self.trustor['id']
|
||||
headers["x-auth-token"] = trustor_token['token']['id']
|
||||
trust_lists_response = self.restful_request(
|
||||
method='GET',
|
||||
path=trustor_url,
|
||||
port=self._public_port(),
|
||||
body="",
|
||||
headers=headers)
|
||||
trusts = trust_lists_response.body['trusts']
|
||||
self.assertEqual(len(trusts), 3)
|
||||
|
||||
headers["x-auth-token"] = trustee_token['token']['id']
|
||||
trust_lists_response = self.restful_request(
|
||||
method='GET', path=trustor_url, port=self._public_port(),
|
||||
body="", headers=headers, expected_status=403)
|
Loading…
Reference in New Issue
Block a user