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:
Adam Young 2013-02-26 14:54:32 -05:00 committed by Gerrit Code Review
parent ab6e552951
commit 601eeb50b6
36 changed files with 1645 additions and 99 deletions

View File

@ -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": [["@"]]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

View 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

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

View 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
View 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
View 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']))

View File

@ -22,3 +22,6 @@ driver = keystone.catalog.backends.sql.Catalog
[policy]
driver = keystone.policy.backends.sql.Policy
[trust]
driver = keystone.trust.backends.sql.Trust

View File

@ -109,5 +109,12 @@ ROLES = [
}, {
'id': 'other',
'name': 'Other',
}, {
'id': 'browser',
'name': 'Browser',
}, {
'id': 'writer',
'name': 'Writer',
}
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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